From 93f3f6906412d3b508c7911f62f94b4134724dc3 Mon Sep 17 00:00:00 2001 From: Brent Champion Date: Tue, 4 Nov 2025 17:47:26 -0500 Subject: [PATCH 1/2] fix: serialization for callback routes should accept empty bodies --- .../web/handlers.py | 22 +++++++++------- tests/web/handlers_test.py | 26 ++++++++----------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/aws_durable_execution_sdk_python_testing/web/handlers.py b/src/aws_durable_execution_sdk_python_testing/web/handlers.py index 6db0b8a..dda069c 100644 --- a/src/aws_durable_execution_sdk_python_testing/web/handlers.py +++ b/src/aws_durable_execution_sdk_python_testing/web/handlers.py @@ -81,21 +81,26 @@ def handle(self, parsed_route: Route, request: HTTPRequest) -> HTTPResponse: HTTPResponse: The HTTP response to send to the client """ - def _parse_json_body(self, request: HTTPRequest) -> dict[str, Any]: + def _parse_json_body( + self, request: HTTPRequest, required: bool = True + ) -> dict[str, Any]: """Parse JSON body from HTTP request with validation. Args: request: The HTTP request containing the JSON body + required: Whether the request body is required Returns: - dict: The parsed JSON data + dict: The parsed JSON data, or empty dict if not required and missing Raises: - InvalidParameterValueException: If the request body is empty or invalid JSON + InvalidParameterValueException: If the request body is required but empty or invalid JSON """ if not request.body: - msg = "Request body is required" - raise InvalidParameterValueException(msg) + if required: + msg = "Request body is required" + raise InvalidParameterValueException(msg) + return {} # Handle both dict and bytes body types if isinstance(request.body, dict): @@ -690,7 +695,7 @@ def handle(self, parsed_route: Route, request: HTTPRequest) -> HTTPResponse: callback_route = cast(CallbackFailureRoute, parsed_route) callback_id: str = callback_route.callback_id - body_data: dict[str, Any] = self._parse_json_body(request) + body_data: dict[str, Any] = self._parse_json_body(request, required=False) callback_request: SendDurableExecutionCallbackFailureRequest = ( SendDurableExecutionCallbackFailureRequest.from_dict( body_data, callback_id @@ -734,10 +739,7 @@ def handle(self, parsed_route: Route, request: HTTPRequest) -> HTTPResponse: HTTPResponse: The HTTP response to send to the client """ try: - # Parse request body for validation but heartbeat doesn't use the data - body_data: dict[str, Any] = self._parse_json_body(request) - SendDurableExecutionCallbackHeartbeatRequest.from_dict(body_data) - + # Heartbeat requests don't have a body, only callback_id from URL callback_route = cast(CallbackHeartbeatRoute, parsed_route) callback_id: str = callback_route.callback_id diff --git a/tests/web/handlers_test.py b/tests/web/handlers_test.py index d3217e5..bd8b5d4 100644 --- a/tests/web/handlers_test.py +++ b/tests/web/handlers_test.py @@ -2173,10 +2173,8 @@ def test_send_durable_execution_callback_failure_handler_empty_body(): ) response = handler.handle(callback_route, request) - # Handler returns 400 for empty body with AWS-compliant format - assert response.status_code == 400 - assert response.body["Type"] == "InvalidParameterValueException" - assert "Request body is required" in response.body["message"] + # Handler should accept empty body for failure requests + assert response.status_code == 200 def test_send_durable_execution_callback_heartbeat_handler(): @@ -2221,24 +2219,22 @@ def test_send_durable_execution_callback_heartbeat_handler_empty_body(): executor = Mock() handler = SendDurableExecutionCallbackHeartbeatHandler(executor) + base_route = Route.from_string( + "/2025-12-01/durable-execution-callbacks/test-id/heartbeat" + ) + callback_route = CallbackHeartbeatRoute.from_route(base_route) + request = HTTPRequest( method="POST", - path=Route.from_string( - "/2025-12-01/durable-execution-callbacks/test-id/heartbeat" - ), + path=callback_route, headers={}, query_params={}, body={}, ) - response = handler.handle( - Route.from_string("/2025-12-01/durable-execution-callbacks/test-id/heartbeat"), - request, - ) - # Handler returns 400 for empty body with AWS-compliant format - assert response.status_code == 400 - assert response.body["Type"] == "InvalidParameterValueException" - assert "Request body is required" in response.body["message"] + response = handler.handle(callback_route, request) + # Handler should accept empty body for heartbeat requests + assert response.status_code == 200 def test_health_handler(): From 063a8f8b8c04fed90c842428b36db8e71c4ed344 Mon Sep 17 00:00:00 2001 From: Brent Champion Date: Wed, 5 Nov 2025 09:44:29 -0500 Subject: [PATCH 2/2] fix: add _parse_json_body_optional for callback failure API --- .../web/handlers.py | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/aws_durable_execution_sdk_python_testing/web/handlers.py b/src/aws_durable_execution_sdk_python_testing/web/handlers.py index dda069c..2fee38e 100644 --- a/src/aws_durable_execution_sdk_python_testing/web/handlers.py +++ b/src/aws_durable_execution_sdk_python_testing/web/handlers.py @@ -81,25 +81,36 @@ def handle(self, parsed_route: Route, request: HTTPRequest) -> HTTPResponse: HTTPResponse: The HTTP response to send to the client """ - def _parse_json_body( - self, request: HTTPRequest, required: bool = True - ) -> dict[str, Any]: + def _parse_json_body(self, request: HTTPRequest) -> dict[str, Any]: """Parse JSON body from HTTP request with validation. Args: request: The HTTP request containing the JSON body - required: Whether the request body is required Returns: - dict: The parsed JSON data, or empty dict if not required and missing + dict: The parsed JSON data Raises: - InvalidParameterValueException: If the request body is required but empty or invalid JSON + InvalidParameterValueException: If the request body is empty + """ + if not request.body: + msg = "Request body is required" + raise InvalidParameterValueException(msg) + return self._parse_json_body_optional(request) + + def _parse_json_body_optional(self, request: HTTPRequest) -> dict[str, Any]: + """Parse JSON body from HTTP request with validation. + + Args: + request: The HTTP request containing the JSON body + + Returns: + dict: The parsed JSON data + + Raises: + InvalidParameterValueException: If the request body is invalid JSON """ if not request.body: - if required: - msg = "Request body is required" - raise InvalidParameterValueException(msg) return {} # Handle both dict and bytes body types @@ -695,7 +706,7 @@ def handle(self, parsed_route: Route, request: HTTPRequest) -> HTTPResponse: callback_route = cast(CallbackFailureRoute, parsed_route) callback_id: str = callback_route.callback_id - body_data: dict[str, Any] = self._parse_json_body(request, required=False) + body_data: dict[str, Any] = self._parse_json_body_optional(request) callback_request: SendDurableExecutionCallbackFailureRequest = ( SendDurableExecutionCallbackFailureRequest.from_dict( body_data, callback_id