Skip to content

Commit 3a31d90

Browse files
rarepolzRares Polenciuc
andauthored
feat: implement Lambda invocation with error handling (#98)
- Add parameter validation and error handling for all exception types - Implement status code validation and function error detection - Add ResourceNotFoundException and InvalidParameterValueException handling - Include test coverage for all error paths and edge cases - Support synchronous Lambda invocation with proper payload serialization and response parsing Co-authored-by: Rares Polenciuc <rarepolz@amazon.com>
1 parent c7a4dd0 commit 3a31d90

File tree

2 files changed

+469
-14
lines changed

2 files changed

+469
-14
lines changed

src/aws_durable_execution_sdk_python_testing/invoker.py

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
DurableExecutionInvocationInputWithClient,
1010
DurableExecutionInvocationOutput,
1111
InitialExecutionState,
12+
InvocationStatus,
1213
)
1314

1415
from aws_durable_execution_sdk_python_testing.exceptions import (
1516
DurableFunctionsTestError,
1617
)
1718
from aws_durable_execution_sdk_python_testing.model import LambdaContext
1819

19-
2020
if TYPE_CHECKING:
2121
from collections.abc import Callable
2222

@@ -143,17 +143,107 @@ def invoke(
143143
function_name: str,
144144
input: DurableExecutionInvocationInput,
145145
) -> DurableExecutionInvocationOutput:
146-
# TODO: wrap ResourceNotFoundException from lambda in ResourceNotFoundException from this lib
147-
response = self.lambda_client.invoke(
148-
FunctionName=function_name,
149-
InvocationType="RequestResponse", # Synchronous invocation
150-
Payload=json.dumps(input.to_dict(), default=str),
146+
"""Invoke AWS Lambda function and return durable execution result.
147+
148+
Args:
149+
function_name: Name of the Lambda function to invoke
150+
input: Durable execution invocation input
151+
152+
Returns:
153+
DurableExecutionInvocationOutput: Result of the function execution
154+
155+
Raises:
156+
ResourceNotFoundException: If function does not exist
157+
InvalidParameterValueException: If parameters are invalid
158+
DurableFunctionsTestError: For other invocation failures
159+
"""
160+
from aws_durable_execution_sdk_python_testing.exceptions import (
161+
ResourceNotFoundException,
162+
InvalidParameterValueException,
151163
)
152164

153-
# very simplified placeholder lol
154-
if response["StatusCode"] == 200: # noqa: PLR2004
155-
json_response = json.loads(response["Payload"].read().decode("utf-8"))
156-
return DurableExecutionInvocationOutput.from_dict(json_response)
165+
# Parameter validation
166+
if not function_name or not function_name.strip():
167+
msg = "Function name is required"
168+
raise InvalidParameterValueException(msg)
169+
170+
try:
171+
# Invoke AWS Lambda function using standard invoke method
172+
response = self.lambda_client.invoke(
173+
FunctionName=function_name,
174+
InvocationType="RequestResponse", # Synchronous invocation
175+
Payload=json.dumps(input.to_dict(), default=str),
176+
)
157177

158-
msg: str = f"Lambda invocation failed with status code: {response['StatusCode']}, {response['Payload']=}"
159-
raise DurableFunctionsTestError(msg)
178+
# Check HTTP status code
179+
status_code = response.get("StatusCode")
180+
if status_code not in (200, 202, 204):
181+
msg = f"Lambda invocation failed with status code: {status_code}"
182+
raise DurableFunctionsTestError(msg)
183+
184+
# Check for function errors
185+
if "FunctionError" in response:
186+
error_payload = response["Payload"].read().decode("utf-8")
187+
msg = f"Lambda invocation failed with status {status_code}: {error_payload}"
188+
raise DurableFunctionsTestError(msg)
189+
190+
# Parse response payload
191+
response_payload = response["Payload"].read().decode("utf-8")
192+
response_dict = json.loads(response_payload)
193+
194+
# Convert to DurableExecutionInvocationOutput
195+
return DurableExecutionInvocationOutput.from_dict(response_dict)
196+
197+
except self.lambda_client.exceptions.ResourceNotFoundException as e:
198+
msg = f"Function not found: {function_name}"
199+
raise ResourceNotFoundException(msg) from e
200+
except self.lambda_client.exceptions.InvalidParameterValueException as e:
201+
msg = f"Invalid parameter: {e}"
202+
raise InvalidParameterValueException(msg) from e
203+
except (
204+
self.lambda_client.exceptions.TooManyRequestsException,
205+
self.lambda_client.exceptions.ServiceException,
206+
self.lambda_client.exceptions.ResourceConflictException,
207+
self.lambda_client.exceptions.InvalidRequestContentException,
208+
self.lambda_client.exceptions.RequestTooLargeException,
209+
self.lambda_client.exceptions.UnsupportedMediaTypeException,
210+
self.lambda_client.exceptions.InvalidRuntimeException,
211+
self.lambda_client.exceptions.InvalidZipFileException,
212+
self.lambda_client.exceptions.ResourceNotReadyException,
213+
self.lambda_client.exceptions.SnapStartTimeoutException,
214+
self.lambda_client.exceptions.SnapStartNotReadyException,
215+
self.lambda_client.exceptions.SnapStartException,
216+
self.lambda_client.exceptions.RecursiveInvocationException,
217+
) as e:
218+
msg = f"Lambda invocation failed: {e}"
219+
raise DurableFunctionsTestError(msg) from e
220+
except (
221+
self.lambda_client.exceptions.InvalidSecurityGroupIDException,
222+
self.lambda_client.exceptions.EC2ThrottledException,
223+
self.lambda_client.exceptions.EFSMountConnectivityException,
224+
self.lambda_client.exceptions.SubnetIPAddressLimitReachedException,
225+
self.lambda_client.exceptions.EC2UnexpectedException,
226+
self.lambda_client.exceptions.InvalidSubnetIDException,
227+
self.lambda_client.exceptions.EC2AccessDeniedException,
228+
self.lambda_client.exceptions.EFSIOException,
229+
self.lambda_client.exceptions.ENILimitReachedException,
230+
self.lambda_client.exceptions.EFSMountTimeoutException,
231+
self.lambda_client.exceptions.EFSMountFailureException,
232+
) as e:
233+
msg = f"Lambda infrastructure error: {e}"
234+
raise DurableFunctionsTestError(msg) from e
235+
except (
236+
self.lambda_client.exceptions.KMSAccessDeniedException,
237+
self.lambda_client.exceptions.KMSDisabledException,
238+
self.lambda_client.exceptions.KMSNotFoundException,
239+
self.lambda_client.exceptions.KMSInvalidStateException,
240+
) as e:
241+
msg = f"Lambda KMS error: {e}"
242+
raise DurableFunctionsTestError(msg) from e
243+
except Exception as e:
244+
# Handle any remaining exceptions, including custom ones like DurableExecutionAlreadyStartedException
245+
if "DurableExecutionAlreadyStartedException" in str(type(e)):
246+
msg = f"Durable execution already started: {e}"
247+
raise DurableFunctionsTestError(msg) from e
248+
msg = f"Unexpected error during Lambda invocation: {e}"
249+
raise DurableFunctionsTestError(msg) from e

0 commit comments

Comments
 (0)