diff --git a/warehouse/resources/events.yaml b/warehouse/resources/events.yaml index 6969824..28bbdf1 100644 --- a/warehouse/resources/events.yaml +++ b/warehouse/resources/events.yaml @@ -31,4 +31,27 @@ components: products: type: array items: - $ref: "../../shared/resources/schemas.yaml#/Product" \ No newline at end of file + $ref: "../../shared/resources/schemas.yaml#/Product" + + PackagingFailed: + x-amazon-event-source: ecommerce.warehouse + x-amazon-events-detail-type: PackagingFailed + description: | + Event emitted when the warehouse service failed to create a package for an order. + + This could be due to multiple reasons, but often because all the products are not available + in stock. + allOf: + - $ref: "../../shared/resources/schemas.yaml#/EventBridgeHeader" + - type: object + properties: + detail: + type: object + required: + - orderId + properties: + orderId: + type: string + format: uuid + description: Identifier for the order relative to the packaging request + example: b2d0c356-f92b-4629-a87f-786331c2842f \ No newline at end of file diff --git a/warehouse/src/table_update/main.py b/warehouse/src/table_update/main.py index 33958f6..68b1166 100644 --- a/warehouse/src/table_update/main.py +++ b/warehouse/src/table_update/main.py @@ -59,17 +59,24 @@ def parse_record(ddb_record: dict) -> Optional[dict]: order_id = ddb_record["dynamodb"]["NewImage"]["orderId"]["S"] products = get_products(order_id) + # Create the detail + detail_type = "PackagingFailed" + detail = { + "orderId": order_id + } + # If there are products, we successfully created a package + if len(products) > 0: + detail_type = "PackageCreated" + detail["products"] = products + # Return event return { "Time": datetime.datetime.now(), "Source": "ecommerce.warehouse", "Resources": [order_id], "EventBusName": EVENT_BUS_NAME, - "DetailType": "PackageCreated", - "Detail": json.dumps({ - "orderId": order_id, - "products": products - }, cls=Encoder) + "DetailType": detail_type, + "Detail": json.dumps(detail, cls=Encoder) } @tracer.capture_method diff --git a/warehouse/tests/unit/test_table_update.py b/warehouse/tests/unit/test_table_update.py index 89ff026..420816d 100644 --- a/warehouse/tests/unit/test_table_update.py +++ b/warehouse/tests/unit/test_table_update.py @@ -86,6 +86,18 @@ def event_metadata_completed(order, order_products): }) } +@pytest.fixture +def event_metadata_failed(order): + return { + "Source": "ecommerce.warehouse", + "DetailType": "PackagingFailed", + "Resources": [order["orderId"]], + "EventBusName": "EVENT_BUS_NAME", + "Detail": json.dumps({ + "orderId": order["orderId"] + }) + } + @pytest.fixture def ddb_record_metadata_product(order): @@ -176,9 +188,28 @@ def test_parse_record_metadata_completed(lambda_module, ddb_record_metadata_comp compare_event(event_metadata_completed, response) +def test_parse_record_metadata_completed_empty(lambda_module, ddb_record_metadata_completed, event_metadata_failed, order, order_products): + """ + Test parse_record() with a metadata completed item without products + """ + + table = mock_table( + lambda_module.table, "query", + ["orderId", "productId"] + ) + + response = lambda_module.parse_record(ddb_record_metadata_completed) + + table.assert_no_pending_responses() + table.deactivate() + + assert response is not None + compare_event(event_metadata_failed, response) + + def test_parse_record_metadata_product(lambda_module, ddb_record_metadata_product): """ - Test parse_record() with a product item + Test parse_record() with a product item """ response = lambda_module.parse_record(ddb_record_metadata_product) @@ -226,4 +257,40 @@ def test_handler_metadata_completed(lambda_module, context, ddb_record_metadata_ lambda_module.handler(event, context) table.assert_no_pending_responses() - table.deactivate() \ No newline at end of file + table.deactivate() + + eventbridge.assert_no_pending_responses() + eventbridge.deactivate() + + +def test_handler_metadata_completed_empty(lambda_module, context, ddb_record_metadata_completed, event_metadata_failed, order, order_products): + """ + Test handler() with a metadata completed item but no products + """ + + event_metadata_failed = copy.deepcopy(event_metadata_failed) + + table = mock_table( + lambda_module.table, "query", + ["orderId", "productId"] + ) + # Stubbing Event Bridge + eventbridge = stub.Stubber(lambda_module.eventbridge) + # Ignore time and detail + event_metadata_failed["Time"] = stub.ANY + event_metadata_failed["Detail"] = stub.ANY + expected_params = {"Entries": [event_metadata_failed]} + eventbridge.add_response("put_events", {}, expected_params) + eventbridge.activate() + + event = {"Records": [ + ddb_record_metadata_completed + ]} + + lambda_module.handler(event, context) + + table.assert_no_pending_responses() + table.deactivate() + + eventbridge.assert_no_pending_responses() + eventbridge.deactivate() \ No newline at end of file