Skip to content
Draft
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
30 changes: 28 additions & 2 deletions back/boxtribute_server/business_logic/warehouse/qr_code/crud.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import hashlib

from ....db import db
from ....errors import DeletedLocation, ResourceDoesNotExist
from ....models.definitions.box import Box
from ....models.definitions.history import DbChangeHistory
from ....models.definitions.location import Location
from ....models.definitions.product import Product
from ....models.definitions.qr_code import QrCode
from ....models.utils import utcnow

Expand All @@ -16,6 +19,30 @@ def create_qr_code(*, user_id, box_label_identifier=None):
All operations are run inside an atomic transaction. If e.g. the box look-up fails,
the operations are rolled back (i.e. no new QR code is inserted).
"""
box = None
if box_label_identifier is not None:
box = (
Box.select(
Box,
Product.id, # otherwise box.save violates FK constraint
Product.deleted_on,
Product.name,
Location.id,
Location.deleted_on,
Location.name,
)
.join(Product)
.join(Location, src=Box)
.where(Box.label_identifier == box_label_identifier)
.get_or_none()
)

if box is None:
return ResourceDoesNotExist(name="Box")

if box.location.deleted_on is not None:
return DeletedLocation(name=box.location.name)

with db.database.atomic():
now = utcnow()
new_qr_code = QrCode.create(created_on=now)
Expand All @@ -33,8 +60,7 @@ def create_qr_code(*, user_id, box_label_identifier=None):
change_date=now,
)

if box_label_identifier is not None:
box = Box.get(Box.label_identifier == box_label_identifier)
if box is not None:
box.qr_code = new_qr_code.id
box.save()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from ariadne import MutationType
from flask import g

from ....authz import authorize
from ....authz import authorize, handle_unauthorized
from .crud import create_qr_code

mutation = MutationType()


@mutation.field("createQrCode")
@handle_unauthorized
def resolve_create_qr_code(*_, box_label_identifier=None):
authorize(permission="qr:create")
return create_qr_code(user_id=g.user.id, box_label_identifier=box_label_identifier)
1 change: 1 addition & 0 deletions back/boxtribute_server/graph_ql/bindables.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def resolve_data_cube_type(obj, *_):
UnionType("DeleteBoxesResult", resolve_type_by_class_name),
UnionType("MoveBoxesResult", resolve_type_by_class_name),
UnionType("QrCodeResult", resolve_type_by_class_name),
UnionType("CreateQrCodeResult", resolve_type_by_class_name),
UnionType("BoxResult", resolve_type_by_class_name),
UnionType("ShareableLinkCreationResult", resolve_type_by_class_name),
UnionType("TagError", resolve_type_by_class_name),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# - input argument: creationInput/updateInput
# - input type: <Resource>CreationInput/UpdateInput
type Mutation {
createQrCode(boxLabelIdentifier: String): QrCode
createQrCode(boxLabelIdentifier: String): CreateQrCodeResult
" Create a new box in a location, containing items of certain product and size. Optionally pass tags to assign to the box. "
createBox(creationInput: BoxCreationInput): Box
" Update one or more properties of a box with specified label identifier. "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -697,10 +697,21 @@ type DeletedTagError {
type DeletedBaseError {
name: String!
}
type DeletedProductError {
name: String!
}
type DeletedBoxError {
labelIdentifier: String!
}
type QrCodeAlreadyAssignedToBox {
code: String!
labelIdentifier: String!
}
type InvalidDateError {
date: Datetime!
}

union CreateQrCodeResult = QrCode | DeletedBoxError | DeletedLocationError | DeletedProductError | InsufficientPermissionError | ResourceDoesNotExistError | UnauthorizedForBaseError | QrCodeAlreadyAssignedToBox
union MoveBoxesResult = BoxesResult | InsufficientPermissionError | ResourceDoesNotExistError | UnauthorizedForBaseError | DeletedLocationError
union DeleteBoxesResult = BoxesResult | InsufficientPermissionError
union CreateCustomProductResult = Product | InsufficientPermissionError | ResourceDoesNotExistError | UnauthorizedForBaseError | InvalidPriceError | EmptyNameError
Expand Down
2 changes: 1 addition & 1 deletion back/test/auth0_integration_tests/test_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_mutations(auth0_client, mocker):
)
user_id = "100000001"

mutation = "mutation { createQrCode { id } }"
mutation = "mutation { createQrCode { ...on QrCode { id } } }"
response = assert_successful_request(auth0_client, mutation, field="createQrCode")
assert response is not None

Expand Down
2 changes: 1 addition & 1 deletion back/test/auth0_integration_tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def test_check_beta_feature_access(dropapp_dev_client, mocker):
"dev_coordinator@boxaid.org"
)

mutation = "mutation { createQrCode { id } }"
mutation = "mutation { createQrCode { ...on QrCode { id } } }"
assert_successful_request(dropapp_dev_client, mutation)

mutation = "mutation { deleteTag(id: 1) { id } }"
Expand Down
13 changes: 10 additions & 3 deletions back/test/endpoint_tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def test_base_specific_permissions(client, mocker):

data = {
"query": """mutation {
qr2: createQrCode { code }
qr3: createQrCode { code }
qr2: createQrCode { ...on QrCode { code } }
qr3: createQrCode { ...on QrCode { code } }
}"""
}
response = client.post("/graphql", json=data)
Expand Down Expand Up @@ -354,6 +354,13 @@ def test_update_non_existent_resource(
"...on UnauthorizedForBaseError { id name organisationName }",
{"id": "0", "name": "", "organisationName": ""},
],
# Test case 8.2.32
[
"createQrCode",
'boxLabelIdentifier: "xxx"',
"...on ResourceDoesNotExistError { id name }",
{"id": None, "name": "Box"},
],
],
)
def test_mutate_resource_does_not_exist(
Expand Down Expand Up @@ -395,7 +402,7 @@ def test_mutation_arbitrary_database_error(read_only_client, mocker):
mocker.patch(
"boxtribute_server.business_logic.warehouse.qr_code.mutations.create_qr_code"
).side_effect = peewee.PeeweeException
mutation = "mutation { createQrCode { id } }"
mutation = "mutation { createQrCode { ...on QrCode { id } } }"
assert_internal_server_error(read_only_client, mutation, field="createQrCode")


Expand Down
2 changes: 1 addition & 1 deletion back/test/endpoint_tests/test_cron.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_reseed_db(cron_client, monkeypatch, mocker, default_users):

# Success; perform actual sourcing of seed (takes about 2s)
# Create QR code and verify that it is removed after reseeding
mutation = "mutation { createQrCode { id code } }"
mutation = "mutation { createQrCode { ...on QrCode { id code } } }"
response = assert_successful_request(cron_client, mutation)
code = response["code"]
response = cron_client.get(reseed_db_path, headers=headers)
Expand Down
9 changes: 7 additions & 2 deletions back/test/endpoint_tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,6 @@ def test_invalid_permission_for_given_resource_id(read_only_client, query):
}) {
id
}""",
# Test case 8.2.33
"createQrCode { id }",
"""createTransferAgreement(
creationInput : {
initiatingOrganisationId: 1,
Expand Down Expand Up @@ -613,6 +611,13 @@ def test_invalid_permission_for_user_read(
"...on InsufficientPermissionError { name }",
{"name": "beneficiary:create"},
],
# Test case 8.2.33
[
"createQrCode",
'boxLabelIdentifier: "xyz"',
"...on InsufficientPermissionError { name }",
{"name": "qr:create"},
],
],
)
def test_mutate_insufficient_permission(
Expand Down
13 changes: 4 additions & 9 deletions back/test/endpoint_tests/test_qr.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import date

from boxtribute_server.models.definitions.history import DbChangeHistory
from utils import assert_bad_user_input, assert_successful_request
from utils import assert_successful_request


def test_qr_exists_query(read_only_client, default_qr_code):
Expand Down Expand Up @@ -52,21 +52,21 @@ def test_code_not_associated_with_box(read_only_client, qr_code_without_box):

def test_qr_code_mutation(client, box_without_qr_code):
# Test case 8.2.30
mutation = "mutation { createQrCode { id } }"
mutation = "mutation { createQrCode { ...on QrCode { id } } }"
qr_code = assert_successful_request(client, mutation)
qr_code_id = int(qr_code["id"])
assert qr_code_id > 2

# Test case 8.2.31
mutation = f"""mutation {{
createQrCode(boxLabelIdentifier: "{box_without_qr_code['label_identifier']}")
{{
{{ ...on QrCode {{
id
box {{ ...on Box {{
id
numberOfItems
}} }}
}}
}} }}
}}"""
created_qr_code = assert_successful_request(client, mutation)
assert int(created_qr_code["id"]) == qr_code_id + 1
Expand All @@ -76,11 +76,6 @@ def test_qr_code_mutation(client, box_without_qr_code):
)
assert int(created_qr_code["box"]["id"]) == box_without_qr_code["id"]

# Test case 8.2.32
assert_bad_user_input(
client, """mutation { createQrCode(boxLabelIdentifier: "xxx") { id } }"""
)

history_entries = list(
DbChangeHistory.select(
DbChangeHistory.changes,
Expand Down