Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions libs/labelbox/src/labelbox/schema/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,49 @@ def extend_reservations(self, queue_type) -> int:
res = self.client.execute(query_str, {id_param: self.uid})
return res["extendReservations"]

def clear_queue_reservations(self, task_queue_id: str) -> Optional[int]:
"""Clears every reservation in one task queue of this project.

Warning:
This is destructive and cannot be undone. Every labeler or
reviewer holding a data row in the queue loses it, and unsaved
work on those data rows can be lost. Only reservations are
cleared: no labels are deleted and no data rows are enqueued or
moved. The data rows go back to the queue for anyone to pick up.

Requires the project permission to clear all reservations (the Admin
and Team Manager roles, or a custom role that grants it). Not
supported on GitHub-integrated projects with the reservation
lifecycle enabled.

Args:
task_queue_id: ID of a task queue of this project, from
``project.task_queues()``.
Returns:
The number of reservations the queue held when they were
cleared, or ``None`` if that count could not be read (the
reservations were still cleared).
Raises:
ResourceNotFoundError: If the task queue is not in this project.
LabelboxError: If the reservations could not be cleared.
"""
query_str = """mutation ClearQueueReservationsPyApi($input: ClearQueueReservationsInput!) {
clearQueueReservations(input: $input) {
success
clearedReservationCount
}
}"""

result = self.client.execute(
query_str,
{"input": {"projectId": self.uid, "queueId": task_queue_id}},
)["clearQueueReservations"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing queue raises TypeError

Medium Severity

When the queue is not in the project, client.execute returns None for a RESOURCE_NOT_FOUND GraphQL error. Indexing that result then raises TypeError instead of the documented ResourceNotFoundError, so callers catching that exception miss the failure.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9c9c3ce. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid: execute returns None on RESOURCE_NOT_FOUND without raise_return_resource_not_found=True. This PR merged before the fix; fixed in #2090.

if not result["success"]:
raise LabelboxError(
f"Reservations of task queue {task_queue_id} were not cleared"
)
return result["clearedReservationCount"]

def bulk_assign_data_rows(
self,
user_id: str,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from unittest.mock import MagicMock

import pytest
from lbox.exceptions import LabelboxError

from labelbox.schema.project import Project


@pytest.fixture
def mock_client():
return MagicMock()


@pytest.fixture
def project(mock_client):
return Project(
mock_client,
{
"id": "test_project_id",
"name": "test",
"createdAt": "2021-06-01T00:00:00.000Z",
"updatedAt": "2021-06-01T00:00:00.000Z",
"autoAuditNumberOfLabels": 1,
"autoAuditPercentage": 100,
"dataRowCount": 1,
"description": "test",
"editorTaskType": "MODEL_CHAT_EVALUATION",
"lastActivityTime": "2021-06-01T00:00:00.000Z",
"allowedMediaType": "IMAGE",
"setupComplete": "2021-06-01T00:00:00.000Z",
"modelSetupComplete": None,
"uploadType": "Auto",
"isBenchmarkEnabled": False,
"isConsensusEnabled": False,
},
)


def test_clear_queue_reservations_sends_mutation_and_returns_count(
project, mock_client
):
mock_client.execute.return_value = {
"clearQueueReservations": {
"success": True,
"clearedReservationCount": 12,
}
}

result = project.clear_queue_reservations("queue_1")

assert result == 12
mock_client.execute.assert_called_once()
query_str, params = mock_client.execute.call_args[0]
assert "ClearQueueReservationsPyApi" in query_str
assert "clearedReservationCount" in query_str
assert params == {
"input": {"projectId": "test_project_id", "queueId": "queue_1"}
}


def test_clear_queue_reservations_returns_none_when_count_unknown(
project, mock_client
):
mock_client.execute.return_value = {
"clearQueueReservations": {
"success": True,
"clearedReservationCount": None,
}
}

assert project.clear_queue_reservations("queue_1") is None


def test_clear_queue_reservations_raises_on_server_failure(
project, mock_client
):
mock_client.execute.return_value = {
"clearQueueReservations": {
"success": False,
"clearedReservationCount": None,
}
}

with pytest.raises(LabelboxError, match="queue_1"):
project.clear_queue_reservations("queue_1")
Loading