From a22adebc3179929b40c815634ae9c3b65b2405f0 Mon Sep 17 00:00:00 2001 From: Philipp Metzner Date: Fri, 3 Jan 2025 11:44:47 +0100 Subject: [PATCH] Change assignTagToBoxes mutation to handle multiple tags --- back/boxtribute_server/authz.py | 4 +- .../business_logic/warehouse/box/crud.py | 7 +- .../business_logic/warehouse/box/mutations.py | 76 +++++++++++--- back/boxtribute_server/graph_ql/bindables.py | 1 + .../definitions/protected/inputs.graphql | 2 +- .../definitions/protected/mutations.graphql | 4 +- .../definitions/protected/types.graphql | 17 ++++ back/test/endpoint_tests/test_app.py | 12 +-- back/test/endpoint_tests/test_box.py | 98 ++++++++++++------- back/test/endpoint_tests/test_permissions.py | 24 ++--- graphql/generated/graphql-env.d.ts | 7 +- graphql/generated/schema.graphql | 23 ++++- 12 files changed, 190 insertions(+), 85 deletions(-) diff --git a/back/boxtribute_server/authz.py b/back/boxtribute_server/authz.py index 1ad8d09f1..54436b1af 100644 --- a/back/boxtribute_server/authz.py +++ b/back/boxtribute_server/authz.py @@ -335,8 +335,8 @@ def authorize_cross_organisation_access( MUTATIONS_FOR_BETA_LEVEL[3] = MUTATIONS_FOR_BETA_LEVEL[2] + ("deleteBoxes",) MUTATIONS_FOR_BETA_LEVEL[4] = MUTATIONS_FOR_BETA_LEVEL[3] + ( "moveBoxesToLocation", - "assignTagToBoxes", - "unassignTagFromBoxes", + "assignTagsToBoxes", + "unassignTagsFromBoxes", ) MUTATIONS_FOR_BETA_LEVEL[5] = MUTATIONS_FOR_BETA_LEVEL[4] + ( "createCustomProduct", diff --git a/back/boxtribute_server/business_logic/warehouse/box/crud.py b/back/boxtribute_server/business_logic/warehouse/box/crud.py index 43a09a86e..5d707f63c 100644 --- a/back/boxtribute_server/business_logic/warehouse/box/crud.py +++ b/back/boxtribute_server/business_logic/warehouse/box/crud.py @@ -440,8 +440,8 @@ def move_boxes_to_location(*, user_id, boxes, location): return list(Box.select().where(Box.id << box_ids)) -def assign_tag_to_boxes(*, user_id, boxes, tag): - """Add TagsRelation entries for given boxes and tag. Update last_modified_* fields +def assign_tags_to_boxes(*, user_id, boxes, tag_ids): + """Add TagsRelation entries for given boxes and tags. Update last_modified_* fields of the affected boxes. Return the list of updated boxes. """ @@ -453,11 +453,12 @@ def assign_tag_to_boxes(*, user_id, boxes, tag): TagsRelation( object_id=box.id, object_type=TaggableObjectType.Box, - tag=tag.id, + tag=tag_id, created_on=now, created_by=user_id, ) for box in boxes + for tag_id in tag_ids ] box_ids = [box.id for box in boxes] diff --git a/back/boxtribute_server/business_logic/warehouse/box/mutations.py b/back/boxtribute_server/business_logic/warehouse/box/mutations.py index aa87e8c8f..d63d072ad 100644 --- a/back/boxtribute_server/business_logic/warehouse/box/mutations.py +++ b/back/boxtribute_server/business_logic/warehouse/box/mutations.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import Any from ariadne import MutationType from flask import g @@ -19,7 +20,7 @@ from ....models.definitions.tag import Tag from ....models.definitions.tags_relation import TagsRelation from .crud import ( - assign_tag_to_boxes, + assign_tags_to_boxes, create_box, delete_boxes, move_boxes_to_location, @@ -36,6 +37,11 @@ class BoxesResult: invalid_box_label_identifiers: list[str] +@dataclass(kw_only=True) +class AssignTagsToBoxesResult(BoxesResult): + tag_error_info: list[dict[str, Any]] + + @mutation.field("createBox") def resolve_create_box(*_, creation_input): requested_location = Location.get_by_id(creation_input["location_id"]) @@ -156,18 +162,51 @@ def resolve_move_boxes_to_location(*_, update_input): ) -@mutation.field("assignTagToBoxes") @handle_unauthorized -def resolve_assign_tag_to_boxes(*_, update_input): - tag_id = update_input["tag_id"] - if (tag := Tag.get_or_none(tag_id)) is None: - return ResourceDoesNotExist(name="Tag", id=tag_id) +def authorize_tag(tag): authorize(permission="tag_relation:assign") authorize(permission="tag:read", base_id=tag.base_id) - if tag.deleted_on is not None: - return DeletedTag(name=tag.name) - if tag.type == TagType.Beneficiary: - return TagTypeMismatch(expected_type=TagType.Box) + + +def _validate_tags(tag_ids): + tag_errors = [] + valid_tag_ids = [] + for tag_id in tag_ids: + if (tag := Tag.get_or_none(tag_id)) is None: + tag_errors.append( + {"id": tag_id, "error": ResourceDoesNotExist(name="Tag", id=tag_id)} + ) + continue + + if (error := authorize_tag(tag)) is not None: + tag_errors.append({"id": tag_id, "error": error}) + continue + + if tag.deleted_on is not None: + tag_errors.append({"id": tag_id, "error": DeletedTag(name=tag.name)}) + continue + + if tag.type == TagType.Beneficiary: + tag_errors.append( + {"id": tag_id, "error": TagTypeMismatch(expected_type=TagType.Box)} + ) + continue + + valid_tag_ids.append(tag_id) + return valid_tag_ids, tag_errors + + +@mutation.field("assignTagsToBoxes") +def resolve_assign_tags_to_boxes(*_, update_input): + tag_ids = set(update_input["tag_ids"]) + valid_tag_ids, tag_errors = _validate_tags(tag_ids) + + if not valid_tag_ids: + return AssignTagsToBoxesResult( + updated_boxes=[], + invalid_box_label_identifiers=[], + tag_error_info=tag_errors, + ) label_identifiers = set(update_input["label_identifiers"]) boxes = ( @@ -179,7 +218,9 @@ def resolve_assign_tag_to_boxes(*_, update_input): on=( (TagsRelation.object_id == Box.id) & (TagsRelation.object_type == TaggableObjectType.Box) - & (TagsRelation.tag == tag.id) + # TODO: this is not correct because it filters out a box if it has only + # one of the requested tags already + & (TagsRelation.tag << valid_tag_ids) & TagsRelation.deleted_on.is_null() ), ) @@ -194,18 +235,21 @@ def resolve_assign_tag_to_boxes(*_, update_input): ) valid_box_label_identifiers = {box.label_identifier for box in boxes} - return BoxesResult( - updated_boxes=assign_tag_to_boxes(user_id=g.user.id, boxes=boxes, tag=tag), + return AssignTagsToBoxesResult( + updated_boxes=assign_tags_to_boxes( + user_id=g.user.id, boxes=boxes, tag_ids=valid_tag_ids + ), invalid_box_label_identifiers=sorted( label_identifiers.difference(valid_box_label_identifiers) ), + tag_error_info=tag_errors, ) -@mutation.field("unassignTagFromBoxes") +@mutation.field("unassignTagsFromBoxes") @handle_unauthorized -def resolve_unassign_tag_from_boxes(*_, update_input): - tag_id = update_input["tag_id"] +def resolve_unassign_tags_from_boxes(*_, update_input): + tag_id = update_input["tag_ids"][0] if (tag := Tag.get_or_none(tag_id)) is None: return ResourceDoesNotExist(name="Tag", id=tag_id) authorize(permission="tag_relation:assign") diff --git a/back/boxtribute_server/graph_ql/bindables.py b/back/boxtribute_server/graph_ql/bindables.py index 3484f265f..5752f4c35 100644 --- a/back/boxtribute_server/graph_ql/bindables.py +++ b/back/boxtribute_server/graph_ql/bindables.py @@ -182,6 +182,7 @@ def resolve_location_type(obj, *_): UnionType("UnassignTagFromBoxesResult", resolve_type_by_class_name), UnionType("QrCodeResult", resolve_type_by_class_name), UnionType("BoxResult", resolve_type_by_class_name), + UnionType("TagError", resolve_type_by_class_name), ) interface_types = ( InterfaceType("Location", resolve_location_type), diff --git a/back/boxtribute_server/graph_ql/definitions/protected/inputs.graphql b/back/boxtribute_server/graph_ql/definitions/protected/inputs.graphql index a8b4744ae..4cb52b9d5 100644 --- a/back/boxtribute_server/graph_ql/definitions/protected/inputs.graphql +++ b/back/boxtribute_server/graph_ql/definitions/protected/inputs.graphql @@ -34,7 +34,7 @@ input BoxMoveInput { input BoxAssignTagInput { labelIdentifiers: [String!]! - tagId: Int! + tagIds: [Int!]! } input CustomProductCreationInput { diff --git a/back/boxtribute_server/graph_ql/definitions/protected/mutations.graphql b/back/boxtribute_server/graph_ql/definitions/protected/mutations.graphql index 5a9534550..fbeb7473a 100644 --- a/back/boxtribute_server/graph_ql/definitions/protected/mutations.graphql +++ b/back/boxtribute_server/graph_ql/definitions/protected/mutations.graphql @@ -12,9 +12,9 @@ type Mutation { " Any boxes that are non-existing, already inside the requested location, inside a different base other than the one of the requested location, and/or in a base that the user must not access are returned in the `BoxesResult.invalidBoxLabelIdentifiers` list. " moveBoxesToLocation(updateInput: BoxMoveInput): MoveBoxesResult " Any boxes that are non-existing, already assigned to the requested tag, and/or in a base that the user must not access are returned in the `BoxesResult.invalidBoxLabelIdentifiers` list. " - assignTagToBoxes(updateInput: BoxAssignTagInput): AssignTagToBoxesResult + assignTagsToBoxes(updateInput: BoxAssignTagInput): AssignTagsToBoxesResult " Any boxes that are non-existing, don't have the requested tag assigned, and/or in a base that the user must not access are returned in the `BoxesResult.invalidBoxLabelIdentifiers` list. " - unassignTagFromBoxes(updateInput: BoxAssignTagInput): UnassignTagFromBoxesResult + unassignTagsFromBoxes(updateInput: BoxAssignTagInput): UnassignTagFromBoxesResult " Create a new beneficiary in a base, using first/last name, date of birth, and group identifier. Optionally pass tags to assign to the beneficiary. " createBeneficiary(creationInput: BeneficiaryCreationInput): Beneficiary diff --git a/back/boxtribute_server/graph_ql/definitions/protected/types.graphql b/back/boxtribute_server/graph_ql/definitions/protected/types.graphql index 48d8c0a4e..03776022f 100644 --- a/back/boxtribute_server/graph_ql/definitions/protected/types.graphql +++ b/back/boxtribute_server/graph_ql/definitions/protected/types.graphql @@ -588,6 +588,23 @@ type ShipmentDetail { receivedOn: Datetime } +""" +Utility response type for box bulk tag-mutations, containing both updated boxes and invalid boxes (ignored due to e.g. being deleted, in prohibited base, and/or non-existing) as well as optional info about erroneous tags. +""" +type AssignTagsToBoxesResult { + updatedBoxes: [Box!]! + invalidBoxLabelIdentifiers: [String!]! + tagErrorInfo: [TagErrorInfo!] +} + +""" Error info about tag with specified ID. """ +type TagErrorInfo { + id: ID! + error: TagError! +} + +union TagError = InsufficientPermissionError | ResourceDoesNotExistError | UnauthorizedForBaseError | TagTypeMismatchError | DeletedTagError + type InsufficientPermissionError { " e.g. 'product:write' missing " name: String! diff --git a/back/test/endpoint_tests/test_app.py b/back/test/endpoint_tests/test_app.py index bb419c3db..b0c5ca124 100644 --- a/back/test/endpoint_tests/test_app.py +++ b/back/test/endpoint_tests/test_app.py @@ -321,15 +321,15 @@ def test_update_non_existent_resource( ], # Test case 8.2.23g [ - "assignTagToBoxes", - 'updateInput: { labelIdentifiers: ["12345678"], tagId: 0 }', - "...on ResourceDoesNotExistError { id name }", - {"id": "0", "name": "Tag"}, + "assignTagsToBoxes", + 'updateInput: { labelIdentifiers: ["12345678"], tagIds: [0] }', + "tagErrorInfo { id error { ...on ResourceDoesNotExistError { id name } } }", + {"tagErrorInfo": [{"error": {"id": "0", "name": "Tag"}, "id": "0"}]}, ], # Test case 8.2.24g [ - "unassignTagFromBoxes", - 'updateInput: { labelIdentifiers: ["12345678"], tagId: 0 }', + "unassignTagsFromBoxes", + 'updateInput: { labelIdentifiers: ["12345678"], tagIds: [0] }', "...on ResourceDoesNotExistError { id name }", {"id": "0", "name": "Tag"}, ], diff --git a/back/test/endpoint_tests/test_box.py b/back/test/endpoint_tests/test_box.py index 5cad12bbf..b9e550523 100644 --- a/back/test/endpoint_tests/test_box.py +++ b/back/test/endpoint_tests/test_box.py @@ -567,53 +567,56 @@ def test_box_mutations( assert response == {"name": f"{deleted_location['name']}"} # Test case 8.2.23a, 8.2.23c - mutation = f"""mutation {{ assignTagToBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {tag_id} }} ) {{ - ...on BoxesResult {{ + mutation = f"""mutation {{ assignTagsToBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{tag_id}] }} ) {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers - }} }} }}""" + tagErrorInfo {{ id }} + }} }}""" response = assert_successful_request(client, mutation) assert response == { "updatedBoxes": [{"tags": [{"id": tag_id}]}], "invalidBoxLabelIdentifiers": [another_created_box_label_identifier], + "tagErrorInfo": [], } - mutation = f"""mutation {{ assignTagToBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {tag_id} }} ) {{ - ...on BoxesResult {{ + mutation = f"""mutation {{ assignTagsToBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{tag_id}] }} ) {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers - }} }} }}""" + tagErrorInfo {{ id }} + }} }}""" response = assert_successful_request(client, mutation) assert response == { "updatedBoxes": [], "invalidBoxLabelIdentifiers": raw_label_identifiers, + "tagErrorInfo": [], } generic_tag_id = str(tags[2]["id"]) - mutation = f"""mutation {{ assignTagToBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {generic_tag_id} }} ) {{ - ...on BoxesResult {{ + mutation = f"""mutation {{ assignTagsToBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{generic_tag_id}] }} ) {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers - }} }} }}""" + tagErrorInfo {{ id }} + }} }}""" response = assert_successful_request(client, mutation) assert response == { "updatedBoxes": [ {"tags": [{"id": tag_id}, {"id": generic_tag_id}]} for _ in range(2) ], "invalidBoxLabelIdentifiers": [], + "tagErrorInfo": [], } another_generic_tag_id = str(tags[5]["id"]) - mutation = f"""mutation {{ assignTagToBoxes( updateInput: {{ + mutation = f"""mutation {{ assignTagsToBoxes( updateInput: {{ labelIdentifiers: [{label_identifiers}], - tagId: {another_generic_tag_id} }} ) {{ - ...on BoxesResult {{ + tagIds: [{another_generic_tag_id}] }} ) {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers - }} }} }}""" + tagErrorInfo {{ id }} + }} }}""" response = assert_successful_request(client, mutation) assert response == { "updatedBoxes": [ @@ -627,12 +630,13 @@ def test_box_mutations( for _ in range(2) ], "invalidBoxLabelIdentifiers": [], + "tagErrorInfo": [], } # Test case 8.2.24a label_identifier = f'"{created_box["labelIdentifier"]}"' - mutation = f"""mutation {{ unassignTagFromBoxes( updateInput: {{ - labelIdentifiers: [{label_identifier}], tagId: {generic_tag_id} }} ) {{ + mutation = f"""mutation {{ unassignTagsFromBoxes( updateInput: {{ + labelIdentifiers: [{label_identifier}], tagIds: [{generic_tag_id}] }} ) {{ ...on BoxesResult {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers @@ -658,8 +662,8 @@ def test_box_mutations( } # Test case 8.2.24c - mutation = f"""mutation {{ unassignTagFromBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {generic_tag_id} }} ) {{ + mutation = f"""mutation {{ unassignTagsFromBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{generic_tag_id}] }} ) {{ ...on BoxesResult {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers @@ -672,15 +676,27 @@ def test_box_mutations( # Test case 8.2.23h beneficiary_tag_id = str(tags[0]["id"]) - mutation = f"""mutation {{ assignTagToBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {beneficiary_tag_id} }} ) {{ - ...on TagTypeMismatchError {{ expectedType }} }} }}""" + mutation = f"""mutation {{ assignTagsToBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{beneficiary_tag_id}] }} ) + {{ + tagErrorInfo {{ + id + error {{ ...on TagTypeMismatchError {{ expectedType }} }} + }} }} }}""" response = assert_successful_request(client, mutation) - assert response == {"expectedType": TagType.Box.name} + assert response == { + "tagErrorInfo": [ + { + "id": beneficiary_tag_id, + "error": {"expectedType": TagType.Box.name}, + } + ] + } # Test case 8.2.24h - mutation = f"""mutation {{ unassignTagFromBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {beneficiary_tag_id} }} ) {{ + mutation = f"""mutation {{ unassignTagsFromBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{beneficiary_tag_id}] }} ) + {{ ...on BoxesResult {{ updatedBoxes {{ id }} invalidBoxLabelIdentifiers @@ -694,15 +710,20 @@ def test_box_mutations( # Test case 8.2.23i deleted_tag_id = str(tags[4]["id"]) tag_name = tags[4]["name"] - mutation = f"""mutation {{ assignTagToBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {deleted_tag_id} }} ) {{ - ...on DeletedTagError {{ name }} }} }}""" + mutation = f"""mutation {{ assignTagsToBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{deleted_tag_id}] }} ) {{ + tagErrorInfo {{ + id + error {{ ...on DeletedTagError {{ name }} }} + }} }} }}""" response = assert_successful_request(client, mutation) - assert response == {"name": tag_name} + assert response == { + "tagErrorInfo": [{"id": deleted_tag_id, "error": {"name": tag_name}}] + } # Test case 8.2.24i - mutation = f"""mutation {{ unassignTagFromBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {deleted_tag_id} }} ) {{ + mutation = f"""mutation {{ unassignTagsFromBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{deleted_tag_id}] }} ) {{ ...on BoxesResult {{ updatedBoxes {{ id }} invalidBoxLabelIdentifiers @@ -750,21 +771,22 @@ def test_box_mutations( } # Test case 8.2.23b, 8.2.23d, 8.2.23j - mutation = f"""mutation {{ assignTagToBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {tag_id} }} ) {{ - ...on BoxesResult {{ + mutation = f"""mutation {{ assignTagsToBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{tag_id}] }} ) {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers - }} }} }}""" + tagErrorInfo {{ id }} + }} }}""" response = assert_successful_request(client, mutation) assert response == { "updatedBoxes": [], "invalidBoxLabelIdentifiers": raw_label_identifiers, + "tagErrorInfo": [], } # Test case 8.2.24b, 8.2.24d, 8.2.24j - mutation = f"""mutation {{ unassignTagFromBoxes( updateInput: {{ - labelIdentifiers: [{label_identifiers}], tagId: {tag_id} }} ) {{ + mutation = f"""mutation {{ unassignTagsFromBoxes( updateInput: {{ + labelIdentifiers: [{label_identifiers}], tagIds: [{tag_id}] }} ) {{ ...on BoxesResult {{ updatedBoxes {{ tags {{ id }} }} invalidBoxLabelIdentifiers diff --git a/back/test/endpoint_tests/test_permissions.py b/back/test/endpoint_tests/test_permissions.py index ad23dda88..8cdba3627 100644 --- a/back/test/endpoint_tests/test_permissions.py +++ b/back/test/endpoint_tests/test_permissions.py @@ -576,15 +576,15 @@ def test_invalid_permission_for_user_read( ], # Test case 8.2.23e [ - "assignTagToBoxes", - 'updateInput: { labelIdentifiers: ["12345678"], tagId: 2 }', - "...on InsufficientPermissionError { name }", - {"name": "tag_relation:assign"}, + "assignTagsToBoxes", + 'updateInput: { labelIdentifiers: ["12345678"], tagIds: [2] }', + "tagErrorInfo { id error { ...on InsufficientPermissionError { name } } }", + {"tagErrorInfo": [{"error": {"name": "tag_relation:assign"}, "id": "2"}]}, ], # Test case 8.2.24e [ - "unassignTagFromBoxes", - 'updateInput: { labelIdentifiers: ["12345678"], tagId: 2 }', + "unassignTagsFromBoxes", + 'updateInput: { labelIdentifiers: ["12345678"], tagIds: [2] }', "...on InsufficientPermissionError { name }", {"name": "tag_relation:assign"}, ], @@ -653,15 +653,15 @@ def test_mutate_insufficient_permission( ], # Test case 8.2.23f [ - "assignTagToBoxes", - 'updateInput: { labelIdentifiers: ["12345678"], tagId: 4 }', - "...on UnauthorizedForBaseError { id }", - {"id": "2"}, + "assignTagsToBoxes", + 'updateInput: { labelIdentifiers: ["12345678"], tagIds: [4] }', + "tagErrorInfo { id error { ...on UnauthorizedForBaseError { id } } }", + {"tagErrorInfo": [{"error": {"id": "2"}, "id": "4"}]}, ], # Test case 8.2.24f [ - "unassignTagFromBoxes", - 'updateInput: { labelIdentifiers: ["12345678"], tagId: 4 }', + "unassignTagsFromBoxes", + 'updateInput: { labelIdentifiers: ["12345678"], tagIds: [4] }', "...on UnauthorizedForBaseError { id }", {"id": "2"}, ], diff --git a/graphql/generated/graphql-env.d.ts b/graphql/generated/graphql-env.d.ts index 3b88e6466..2d798f628 100644 --- a/graphql/generated/graphql-env.d.ts +++ b/graphql/generated/graphql-env.d.ts @@ -3,6 +3,7 @@ export type introspection_types = { 'AssignTagToBoxesResult': { kind: 'UNION'; name: 'AssignTagToBoxesResult'; fields: {}; possibleTypes: 'BoxesResult' | 'DeletedTagError' | 'InsufficientPermissionError' | 'ResourceDoesNotExistError' | 'TagTypeMismatchError' | 'UnauthorizedForBaseError'; }; + 'AssignTagsToBoxesResult': { kind: 'OBJECT'; name: 'AssignTagsToBoxesResult'; fields: { 'invalidBoxLabelIdentifiers': { name: 'invalidBoxLabelIdentifiers'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; } }; 'tagErrorInfo': { name: 'tagErrorInfo'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TagErrorInfo'; ofType: null; }; }; } }; 'updatedBoxes': { name: 'updatedBoxes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Box'; ofType: null; }; }; }; } }; }; }; 'Base': { kind: 'OBJECT'; name: 'Base'; fields: { 'beneficiaries': { name: 'beneficiaries'; type: { kind: 'OBJECT'; name: 'BeneficiaryPage'; ofType: null; } }; 'currencyName': { name: 'currencyName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'deletedOn': { name: 'deletedOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'distributionEvents': { name: 'distributionEvents'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; }; }; }; } }; 'distributionEventsBeforeReturnedFromDistributionState': { name: 'distributionEventsBeforeReturnedFromDistributionState'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; }; }; }; } }; 'distributionEventsInReturnedFromDistributionState': { name: 'distributionEventsInReturnedFromDistributionState'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; }; }; }; } }; 'distributionEventsStatistics': { name: 'distributionEventsStatistics'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionEventsStatistics'; ofType: null; }; }; }; } }; 'distributionEventsTrackingGroups': { name: 'distributionEventsTrackingGroups'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; }; }; }; } }; 'distributionSpots': { name: 'distributionSpots'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; }; }; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'locations': { name: 'locations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ClassicLocation'; ofType: null; }; }; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'organisation': { name: 'organisation'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Organisation'; ofType: null; }; } }; 'products': { name: 'products'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Product'; ofType: null; }; }; }; } }; 'tags': { name: 'tags'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; } }; }; }; 'BasicDimensionInfo': { kind: 'INTERFACE'; name: 'BasicDimensionInfo'; fields: { 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; possibleTypes: 'DimensionInfo' | 'ProductDimensionInfo' | 'TagDimensionInfo'; }; 'Beneficiary': { kind: 'OBJECT'; name: 'Beneficiary'; fields: { 'active': { name: 'active'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'age': { name: 'age'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'base': { name: 'base'; type: { kind: 'OBJECT'; name: 'Base'; ofType: null; } }; 'comment': { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'createdBy': { name: 'createdBy'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'createdOn': { name: 'createdOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'dateOfBirth': { name: 'dateOfBirth'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; } }; 'dateOfSignature': { name: 'dateOfSignature'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; } }; 'familyHead': { name: 'familyHead'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'firstName': { name: 'firstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'gender': { name: 'gender'; type: { kind: 'ENUM'; name: 'HumanGender'; ofType: null; } }; 'groupIdentifier': { name: 'groupIdentifier'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'isVolunteer': { name: 'isVolunteer'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'languages': { name: 'languages'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'Language'; ofType: null; }; }; } }; 'lastModifiedBy': { name: 'lastModifiedBy'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'lastModifiedOn': { name: 'lastModifiedOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'lastName': { name: 'lastName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'registered': { name: 'registered'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'signature': { name: 'signature'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'signed': { name: 'signed'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'tags': { name: 'tags'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; } }; 'tokens': { name: 'tokens'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'transactions': { name: 'transactions'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Transaction'; ofType: null; }; }; } }; }; }; @@ -14,7 +15,7 @@ export type introspection_types = { 'BeneficiaryUpdateInput': { kind: 'INPUT_OBJECT'; name: 'BeneficiaryUpdateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'firstName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'lastName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'groupIdentifier'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'dateOfBirth'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; }; defaultValue: null }, { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'gender'; type: { kind: 'ENUM'; name: 'HumanGender'; ofType: null; }; defaultValue: null }, { name: 'languages'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'Language'; ofType: null; }; }; }; defaultValue: null }, { name: 'familyHeadId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'isVolunteer'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }, { name: 'registered'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }, { name: 'signature'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'dateOfSignature'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; }; defaultValue: null }]; }; 'Boolean': unknown; 'Box': { kind: 'OBJECT'; name: 'Box'; fields: { 'comment': { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'createdBy': { name: 'createdBy'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'createdOn': { name: 'createdOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'deletedOn': { name: 'deletedOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'displayUnit': { name: 'displayUnit'; type: { kind: 'OBJECT'; name: 'Unit'; ofType: null; } }; 'distributionEvent': { name: 'distributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'history': { name: 'history'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'HistoryEntry'; ofType: null; }; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'labelIdentifier': { name: 'labelIdentifier'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'lastModifiedBy': { name: 'lastModifiedBy'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'lastModifiedOn': { name: 'lastModifiedOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'location': { name: 'location'; type: { kind: 'INTERFACE'; name: 'Location'; ofType: null; } }; 'measureValue': { name: 'measureValue'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'numberOfItems': { name: 'numberOfItems'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'product': { name: 'product'; type: { kind: 'OBJECT'; name: 'Product'; ofType: null; } }; 'qrCode': { name: 'qrCode'; type: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; } }; 'shipmentDetail': { name: 'shipmentDetail'; type: { kind: 'OBJECT'; name: 'ShipmentDetail'; ofType: null; } }; 'size': { name: 'size'; type: { kind: 'OBJECT'; name: 'Size'; ofType: null; } }; 'state': { name: 'state'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'BoxState'; ofType: null; }; } }; 'tags': { name: 'tags'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; } }; }; }; - 'BoxAssignTagInput': { kind: 'INPUT_OBJECT'; name: 'BoxAssignTagInput'; isOneOf: false; inputFields: [{ name: 'labelIdentifiers'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; }; defaultValue: null }, { name: 'tagId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }]; }; + 'BoxAssignTagInput': { kind: 'INPUT_OBJECT'; name: 'BoxAssignTagInput'; isOneOf: false; inputFields: [{ name: 'labelIdentifiers'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; }; defaultValue: null }, { name: 'tagIds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; }; }; defaultValue: null }]; }; 'BoxCreationInput': { kind: 'INPUT_OBJECT'; name: 'BoxCreationInput'; isOneOf: false; inputFields: [{ name: 'productId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'sizeId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'displayUnitId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'measureValue'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; }; defaultValue: null }, { name: 'numberOfItems'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'locationId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'qrCode'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'tagIds'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; }; defaultValue: null }]; }; 'BoxMoveInput': { kind: 'INPUT_OBJECT'; name: 'BoxMoveInput'; isOneOf: false; inputFields: [{ name: 'labelIdentifiers'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; }; defaultValue: null }, { name: 'locationId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }]; }; 'BoxPage': { kind: 'OBJECT'; name: 'BoxPage'; fields: { 'elements': { name: 'elements'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Box'; ofType: null; }; }; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; 'totalCount': { name: 'totalCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; }; }; @@ -73,7 +74,7 @@ export type introspection_types = { 'MovedBoxDataDimensions': { kind: 'OBJECT'; name: 'MovedBoxDataDimensions'; fields: { 'category': { name: 'category'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'DimensionInfo'; ofType: null; }; } }; 'dimension': { name: 'dimension'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DimensionInfo'; ofType: null; }; }; }; } }; 'size': { name: 'size'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'DimensionInfo'; ofType: null; }; } }; 'tag': { name: 'tag'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TagDimensionInfo'; ofType: null; }; }; }; } }; 'target': { name: 'target'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'TargetDimensionInfo'; ofType: null; }; } }; }; }; 'MovedBoxesData': { kind: 'OBJECT'; name: 'MovedBoxesData'; fields: { 'dimensions': { name: 'dimensions'; type: { kind: 'OBJECT'; name: 'MovedBoxDataDimensions'; ofType: null; } }; 'facts': { name: 'facts'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'MovedBoxesResult'; ofType: null; }; } }; }; }; 'MovedBoxesResult': { kind: 'OBJECT'; name: 'MovedBoxesResult'; fields: { 'absoluteMeasureValue': { name: 'absoluteMeasureValue'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'boxesCount': { name: 'boxesCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'categoryId': { name: 'categoryId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'dimensionId': { name: 'dimensionId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'gender': { name: 'gender'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'ProductGender'; ofType: null; }; } }; 'itemsCount': { name: 'itemsCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'movedOn': { name: 'movedOn'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Date'; ofType: null; }; } }; 'organisationName': { name: 'organisationName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'productName': { name: 'productName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'sizeId': { name: 'sizeId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'tagIds': { name: 'tagIds'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; } }; 'targetId': { name: 'targetId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; - 'Mutation': { kind: 'OBJECT'; name: 'Mutation'; fields: { 'acceptTransferAgreement': { name: 'acceptTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'addPackingListEntryToDistributionEvent': { name: 'addPackingListEntryToDistributionEvent'; type: { kind: 'OBJECT'; name: 'PackingListEntry'; ofType: null; } }; 'assignBoxToDistributionEvent': { name: 'assignBoxToDistributionEvent'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'assignTag': { name: 'assignTag'; type: { kind: 'UNION'; name: 'TaggableResource'; ofType: null; } }; 'assignTagToBoxes': { name: 'assignTagToBoxes'; type: { kind: 'UNION'; name: 'AssignTagToBoxesResult'; ofType: null; } }; 'cancelShipment': { name: 'cancelShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'cancelTransferAgreement': { name: 'cancelTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'changeDistributionEventState': { name: 'changeDistributionEventState'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'completeDistributionEventsTrackingGroup': { name: 'completeDistributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; } }; 'createBeneficiary': { name: 'createBeneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'createBox': { name: 'createBox'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'createCustomProduct': { name: 'createCustomProduct'; type: { kind: 'UNION'; name: 'CreateCustomProductResult'; ofType: null; } }; 'createDistributionEvent': { name: 'createDistributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'createDistributionSpot': { name: 'createDistributionSpot'; type: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; } }; 'createQrCode': { name: 'createQrCode'; type: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; } }; 'createShipment': { name: 'createShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'createTag': { name: 'createTag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; 'createTransferAgreement': { name: 'createTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'deactivateBeneficiary': { name: 'deactivateBeneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'deleteBoxes': { name: 'deleteBoxes'; type: { kind: 'UNION'; name: 'DeleteBoxesResult'; ofType: null; } }; 'deleteProduct': { name: 'deleteProduct'; type: { kind: 'UNION'; name: 'DeleteProductResult'; ofType: null; } }; 'deleteTag': { name: 'deleteTag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; 'disableStandardProduct': { name: 'disableStandardProduct'; type: { kind: 'UNION'; name: 'DisableStandardProductResult'; ofType: null; } }; 'editCustomProduct': { name: 'editCustomProduct'; type: { kind: 'UNION'; name: 'EditCustomProductResult'; ofType: null; } }; 'editStandardProductInstantiation': { name: 'editStandardProductInstantiation'; type: { kind: 'UNION'; name: 'EditStandardProductInstantiationResult'; ofType: null; } }; 'enableStandardProduct': { name: 'enableStandardProduct'; type: { kind: 'UNION'; name: 'EnableStandardProductResult'; ofType: null; } }; 'markShipmentAsLost': { name: 'markShipmentAsLost'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'moveBoxesToLocation': { name: 'moveBoxesToLocation'; type: { kind: 'UNION'; name: 'MoveBoxesResult'; ofType: null; } }; 'moveItemsFromBoxToDistributionEvent': { name: 'moveItemsFromBoxToDistributionEvent'; type: { kind: 'OBJECT'; name: 'UnboxedItemsCollection'; ofType: null; } }; 'moveItemsFromReturnTrackingGroupToBox': { name: 'moveItemsFromReturnTrackingGroupToBox'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingEntry'; ofType: null; } }; 'moveNotDeliveredBoxesInStock': { name: 'moveNotDeliveredBoxesInStock'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'rejectTransferAgreement': { name: 'rejectTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'removeAllPackingListEntriesFromDistributionEventForProduct': { name: 'removeAllPackingListEntriesFromDistributionEventForProduct'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'removeItemsFromUnboxedItemsCollection': { name: 'removeItemsFromUnboxedItemsCollection'; type: { kind: 'OBJECT'; name: 'UnboxedItemsCollection'; ofType: null; } }; 'removePackingListEntryFromDistributionEvent': { name: 'removePackingListEntryFromDistributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'sendShipment': { name: 'sendShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'setReturnedNumberOfItemsForDistributionEventsTrackingGroup': { name: 'setReturnedNumberOfItemsForDistributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingEntry'; ofType: null; } }; 'startDistributionEventsTrackingGroup': { name: 'startDistributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; } }; 'startReceivingShipment': { name: 'startReceivingShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'unassignBoxFromDistributionEvent': { name: 'unassignBoxFromDistributionEvent'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'unassignTag': { name: 'unassignTag'; type: { kind: 'UNION'; name: 'TaggableResource'; ofType: null; } }; 'unassignTagFromBoxes': { name: 'unassignTagFromBoxes'; type: { kind: 'UNION'; name: 'UnassignTagFromBoxesResult'; ofType: null; } }; 'updateBeneficiary': { name: 'updateBeneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'updateBox': { name: 'updateBox'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'updatePackingListEntry': { name: 'updatePackingListEntry'; type: { kind: 'OBJECT'; name: 'PackingListEntry'; ofType: null; } }; 'updateSelectedProductsForDistributionEventPackingList': { name: 'updateSelectedProductsForDistributionEventPackingList'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'updateShipmentWhenPreparing': { name: 'updateShipmentWhenPreparing'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'updateShipmentWhenReceiving': { name: 'updateShipmentWhenReceiving'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'updateTag': { name: 'updateTag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; }; }; + 'Mutation': { kind: 'OBJECT'; name: 'Mutation'; fields: { 'acceptTransferAgreement': { name: 'acceptTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'addPackingListEntryToDistributionEvent': { name: 'addPackingListEntryToDistributionEvent'; type: { kind: 'OBJECT'; name: 'PackingListEntry'; ofType: null; } }; 'assignBoxToDistributionEvent': { name: 'assignBoxToDistributionEvent'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'assignTag': { name: 'assignTag'; type: { kind: 'UNION'; name: 'TaggableResource'; ofType: null; } }; 'assignTagsToBoxes': { name: 'assignTagsToBoxes'; type: { kind: 'OBJECT'; name: 'AssignTagsToBoxesResult'; ofType: null; } }; 'cancelShipment': { name: 'cancelShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'cancelTransferAgreement': { name: 'cancelTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'changeDistributionEventState': { name: 'changeDistributionEventState'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'completeDistributionEventsTrackingGroup': { name: 'completeDistributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; } }; 'createBeneficiary': { name: 'createBeneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'createBox': { name: 'createBox'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'createCustomProduct': { name: 'createCustomProduct'; type: { kind: 'UNION'; name: 'CreateCustomProductResult'; ofType: null; } }; 'createDistributionEvent': { name: 'createDistributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'createDistributionSpot': { name: 'createDistributionSpot'; type: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; } }; 'createQrCode': { name: 'createQrCode'; type: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; } }; 'createShipment': { name: 'createShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'createTag': { name: 'createTag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; 'createTransferAgreement': { name: 'createTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'deactivateBeneficiary': { name: 'deactivateBeneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'deleteBoxes': { name: 'deleteBoxes'; type: { kind: 'UNION'; name: 'DeleteBoxesResult'; ofType: null; } }; 'deleteProduct': { name: 'deleteProduct'; type: { kind: 'UNION'; name: 'DeleteProductResult'; ofType: null; } }; 'deleteTag': { name: 'deleteTag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; 'disableStandardProduct': { name: 'disableStandardProduct'; type: { kind: 'UNION'; name: 'DisableStandardProductResult'; ofType: null; } }; 'editCustomProduct': { name: 'editCustomProduct'; type: { kind: 'UNION'; name: 'EditCustomProductResult'; ofType: null; } }; 'editStandardProductInstantiation': { name: 'editStandardProductInstantiation'; type: { kind: 'UNION'; name: 'EditStandardProductInstantiationResult'; ofType: null; } }; 'enableStandardProduct': { name: 'enableStandardProduct'; type: { kind: 'UNION'; name: 'EnableStandardProductResult'; ofType: null; } }; 'markShipmentAsLost': { name: 'markShipmentAsLost'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'moveBoxesToLocation': { name: 'moveBoxesToLocation'; type: { kind: 'UNION'; name: 'MoveBoxesResult'; ofType: null; } }; 'moveItemsFromBoxToDistributionEvent': { name: 'moveItemsFromBoxToDistributionEvent'; type: { kind: 'OBJECT'; name: 'UnboxedItemsCollection'; ofType: null; } }; 'moveItemsFromReturnTrackingGroupToBox': { name: 'moveItemsFromReturnTrackingGroupToBox'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingEntry'; ofType: null; } }; 'moveNotDeliveredBoxesInStock': { name: 'moveNotDeliveredBoxesInStock'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'rejectTransferAgreement': { name: 'rejectTransferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'removeAllPackingListEntriesFromDistributionEventForProduct': { name: 'removeAllPackingListEntriesFromDistributionEventForProduct'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'removeItemsFromUnboxedItemsCollection': { name: 'removeItemsFromUnboxedItemsCollection'; type: { kind: 'OBJECT'; name: 'UnboxedItemsCollection'; ofType: null; } }; 'removePackingListEntryFromDistributionEvent': { name: 'removePackingListEntryFromDistributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'sendShipment': { name: 'sendShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'setReturnedNumberOfItemsForDistributionEventsTrackingGroup': { name: 'setReturnedNumberOfItemsForDistributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingEntry'; ofType: null; } }; 'startDistributionEventsTrackingGroup': { name: 'startDistributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; } }; 'startReceivingShipment': { name: 'startReceivingShipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'unassignBoxFromDistributionEvent': { name: 'unassignBoxFromDistributionEvent'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'unassignTag': { name: 'unassignTag'; type: { kind: 'UNION'; name: 'TaggableResource'; ofType: null; } }; 'unassignTagsFromBoxes': { name: 'unassignTagsFromBoxes'; type: { kind: 'UNION'; name: 'UnassignTagFromBoxesResult'; ofType: null; } }; 'updateBeneficiary': { name: 'updateBeneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'updateBox': { name: 'updateBox'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'updatePackingListEntry': { name: 'updatePackingListEntry'; type: { kind: 'OBJECT'; name: 'PackingListEntry'; ofType: null; } }; 'updateSelectedProductsForDistributionEventPackingList': { name: 'updateSelectedProductsForDistributionEventPackingList'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'updateShipmentWhenPreparing': { name: 'updateShipmentWhenPreparing'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'updateShipmentWhenReceiving': { name: 'updateShipmentWhenReceiving'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'updateTag': { name: 'updateTag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; }; }; 'Organisation': { kind: 'OBJECT'; name: 'Organisation'; fields: { 'bases': { name: 'bases'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Base'; ofType: null; }; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'PackingListEntry': { kind: 'OBJECT'; name: 'PackingListEntry'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'matchingPackedItemsCollections': { name: 'matchingPackedItemsCollections'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'ItemsCollection'; ofType: null; }; }; }; } }; 'numberOfItems': { name: 'numberOfItems'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'product': { name: 'product'; type: { kind: 'OBJECT'; name: 'Product'; ofType: null; } }; 'size': { name: 'size'; type: { kind: 'OBJECT'; name: 'Size'; ofType: null; } }; 'state': { name: 'state'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'PackingListEntryState'; ofType: null; }; } }; }; }; 'PackingListEntryInput': { kind: 'INPUT_OBJECT'; name: 'PackingListEntryInput'; isOneOf: false; inputFields: [{ name: 'distributionEventId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'productId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'sizeId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'numberOfItems'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }]; }; @@ -117,6 +118,8 @@ export type introspection_types = { 'Tag': { kind: 'OBJECT'; name: 'Tag'; fields: { 'base': { name: 'base'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Base'; ofType: null; }; } }; 'color': { name: 'color'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'description': { name: 'description'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'taggedResources': { name: 'taggedResources'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'TaggableResource'; ofType: null; }; }; } }; 'type': { name: 'type'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'TagType'; ofType: null; }; } }; }; }; 'TagCreationInput': { kind: 'INPUT_OBJECT'; name: 'TagCreationInput'; isOneOf: false; inputFields: [{ name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'description'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'color'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'type'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'TagType'; ofType: null; }; }; defaultValue: null }, { name: 'baseId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }]; }; 'TagDimensionInfo': { kind: 'OBJECT'; name: 'TagDimensionInfo'; fields: { 'color': { name: 'color'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; }; + 'TagError': { kind: 'UNION'; name: 'TagError'; fields: {}; possibleTypes: 'DeletedTagError' | 'InsufficientPermissionError' | 'ResourceDoesNotExistError' | 'TagTypeMismatchError' | 'UnauthorizedForBaseError'; }; + 'TagErrorInfo': { kind: 'OBJECT'; name: 'TagErrorInfo'; fields: { 'error': { name: 'error'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'TagError'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; 'TagOperationInput': { kind: 'INPUT_OBJECT'; name: 'TagOperationInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'resourceId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'resourceType'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'TaggableResourceType'; ofType: null; }; }; defaultValue: null }]; }; 'TagType': { name: 'TagType'; enumValues: 'Box' | 'Beneficiary' | 'All'; }; 'TagTypeMismatchError': { kind: 'OBJECT'; name: 'TagTypeMismatchError'; fields: { 'expectedType': { name: 'expectedType'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'TagType'; ofType: null; }; } }; }; }; diff --git a/graphql/generated/schema.graphql b/graphql/generated/schema.graphql index cd8fc8426..b938e6a3d 100644 --- a/graphql/generated/schema.graphql +++ b/graphql/generated/schema.graphql @@ -173,7 +173,7 @@ input BoxMoveInput { input BoxAssignTagInput { labelIdentifiers: [String!]! - tagId: Int! + tagIds: [Int!]! } input CustomProductCreationInput { @@ -349,9 +349,9 @@ type Mutation { " Any boxes that are non-existing, already inside the requested location, inside a different base other than the one of the requested location, and/or in a base that the user must not access are returned in the `BoxesResult.invalidBoxLabelIdentifiers` list. " moveBoxesToLocation(updateInput: BoxMoveInput): MoveBoxesResult " Any boxes that are non-existing, already assigned to the requested tag, and/or in a base that the user must not access are returned in the `BoxesResult.invalidBoxLabelIdentifiers` list. " - assignTagToBoxes(updateInput: BoxAssignTagInput): AssignTagToBoxesResult + assignTagsToBoxes(updateInput: BoxAssignTagInput): AssignTagsToBoxesResult " Any boxes that are non-existing, don't have the requested tag assigned, and/or in a base that the user must not access are returned in the `BoxesResult.invalidBoxLabelIdentifiers` list. " - unassignTagFromBoxes(updateInput: BoxAssignTagInput): UnassignTagFromBoxesResult + unassignTagsFromBoxes(updateInput: BoxAssignTagInput): UnassignTagFromBoxesResult " Create a new beneficiary in a base, using first/last name, date of birth, and group identifier. Optionally pass tags to assign to the beneficiary. " createBeneficiary(creationInput: BeneficiaryCreationInput): Beneficiary @@ -1136,6 +1136,23 @@ type ShipmentDetail { receivedOn: Datetime } +""" +Utility response type for box bulk tag-mutations, containing both updated boxes and invalid boxes (ignored due to e.g. being deleted, in prohibited base, and/or non-existing) as well as optional info about erroneous tags. +""" +type AssignTagsToBoxesResult { + updatedBoxes: [Box!]! + invalidBoxLabelIdentifiers: [String!]! + tagErrorInfo: [TagErrorInfo!] +} + +""" Error info about tag with specified ID. """ +type TagErrorInfo { + id: ID! + error: TagError! +} + +union TagError = InsufficientPermissionError | ResourceDoesNotExistError | UnauthorizedForBaseError | TagTypeMismatchError | DeletedTagError + type InsufficientPermissionError { " e.g. 'product:write' missing " name: String!