Skip to content

Commit 7f95a5c

Browse files
authored
fix: parse callback success request payload properly (#129)
1 parent 4594a3c commit 7f95a5c

File tree

2 files changed

+90
-4
lines changed

2 files changed

+90
-4
lines changed

src/aws_durable_execution_sdk_python_testing/web/handlers.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import base64
56
import json
67
import logging
78
from abc import ABC, abstractmethod
@@ -183,6 +184,37 @@ def _no_content_response(
183184

184185
# Removed deprecated _error_response method - use AWS exceptions directly
185186

187+
def _parse_callback_result_payload(self, request: HTTPRequest) -> bytes:
188+
"""Parse callback result payload from request body.
189+
190+
Expects JSON payload with base64-encoded Result field.
191+
192+
Args:
193+
request: The HTTP request containing the JSON payload
194+
195+
Returns:
196+
bytes: The decoded result payload
197+
198+
Raises:
199+
InvalidParameterValueException: If payload parsing fails
200+
"""
201+
if not request.body or not isinstance(request.body, bytes):
202+
return b""
203+
204+
try:
205+
payload = json.loads(request.body.decode("utf-8"))
206+
if isinstance(payload, dict) and "Result" in payload:
207+
result_value = payload["Result"]
208+
if isinstance(result_value, str):
209+
return base64.b64decode(result_value)
210+
return b""
211+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
212+
msg = f"Failed to parse JSON payload: {e}"
213+
raise InvalidParameterValueException(msg) from e
214+
except ValueError as e:
215+
msg = f"Failed to decode base64 result: {e}"
216+
raise InvalidParameterValueException(msg) from e
217+
186218
def _parse_query_param(self, request: HTTPRequest, param_name: str) -> str | None:
187219
"""Parse a single query parameter from the request.
188220
@@ -611,8 +643,7 @@ def handle(self, parsed_route: Route, request: HTTPRequest) -> HTTPResponse:
611643
callback_route = cast(CallbackSuccessRoute, parsed_route)
612644
callback_id: str = callback_route.callback_id
613645

614-
# For binary payload operations, body is raw bytes
615-
result_bytes = request.body if isinstance(request.body, bytes) else b""
646+
result_bytes: bytes = self._parse_callback_result_payload(request)
616647

617648
callback_response: SendDurableExecutionCallbackSuccessResponse = ( # noqa: F841
618649
self.executor.send_callback_success(

tests/web/handlers_test.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
import base64
6+
import json
57
from typing import TYPE_CHECKING, Any
68
from unittest.mock import Mock
79

@@ -2037,13 +2039,15 @@ def test_send_durable_execution_callback_success_handler():
20372039
assert isinstance(route, CallbackSuccessRoute)
20382040
assert route.callback_id == "test-callback-id"
20392041

2040-
# Test with valid request body (bytes for callback operations)
2042+
result_data = base64.b64encode(b"success-result").decode("utf-8")
2043+
request_body = json.dumps({"Result": result_data}).encode("utf-8")
2044+
20412045
request = HTTPRequest(
20422046
method="POST",
20432047
path=route,
20442048
headers={"Content-Type": "application/json"},
20452049
query_params={},
2046-
body=b"success-result",
2050+
body=request_body,
20472051
)
20482052

20492053
response = handler.handle(route, request)
@@ -2058,6 +2062,57 @@ def test_send_durable_execution_callback_success_handler():
20582062
)
20592063

20602064

2065+
def test_send_durable_execution_callback_success_handler_invalid_json():
2066+
"""Test SendDurableExecutionCallbackSuccessHandler with invalid JSON."""
2067+
executor = Mock()
2068+
handler = SendDurableExecutionCallbackSuccessHandler(executor)
2069+
2070+
router = Router()
2071+
route = router.find_route(
2072+
"/2025-12-01/durable-execution-callbacks/test-callback-id/succeed", "POST"
2073+
)
2074+
2075+
request = HTTPRequest(
2076+
method="POST",
2077+
path=route,
2078+
headers={"Content-Type": "application/json"},
2079+
query_params={},
2080+
body=b"invalid-json",
2081+
)
2082+
2083+
response = handler.handle(route, request)
2084+
2085+
assert response.status_code == 400
2086+
assert response.body["Type"] == "InvalidParameterValueException"
2087+
assert "Failed to parse JSON payload" in response.body["message"]
2088+
2089+
2090+
def test_send_durable_execution_callback_success_handler_invalid_base64():
2091+
"""Test SendDurableExecutionCallbackSuccessHandler with invalid base64."""
2092+
executor = Mock()
2093+
handler = SendDurableExecutionCallbackSuccessHandler(executor)
2094+
2095+
router = Router()
2096+
route = router.find_route(
2097+
"/2025-12-01/durable-execution-callbacks/test-callback-id/succeed", "POST"
2098+
)
2099+
2100+
request_body = json.dumps({"Result": "invalid-base64!"}).encode("utf-8")
2101+
request = HTTPRequest(
2102+
method="POST",
2103+
path=route,
2104+
headers={"Content-Type": "application/json"},
2105+
query_params={},
2106+
body=request_body,
2107+
)
2108+
2109+
response = handler.handle(route, request)
2110+
2111+
assert response.status_code == 400
2112+
assert response.body["Type"] == "InvalidParameterValueException"
2113+
assert "Failed to decode base64 result" in response.body["message"]
2114+
2115+
20612116
def test_send_durable_execution_callback_success_handler_empty_body():
20622117
"""Test SendDurableExecutionCallbackSuccessHandler with empty body."""
20632118
executor = Mock()

0 commit comments

Comments
 (0)