From 9c3410d1c1c2538a8551ac1681d98c8a101bc5f5 Mon Sep 17 00:00:00 2001 From: Quinn Sinclair <> Date: Sun, 19 Oct 2025 21:45:12 +0000 Subject: [PATCH 1/4] [Chore] Update OperationType to use ChainedInvoke instead of invoke Ctx: - Model has been updated and the invoke member of the ChainedInvoke enum has now become ChainedInvoke over invoke. This commit updates the model to use chainedinvoke instead. --- .../2015-03-31/service-2.json | 2 +- .../lambdainternal/2015-03-31/service-2.json | 2 +- .../lambda_service.py | 21 ++++++++++++++-- src/aws_durable_execution_sdk_python/state.py | 2 +- tests/lambda_service_test.py | 12 +++++----- tests/operation/invoke_test.py | 24 +++++++++---------- tests/state_test.py | 14 +++++------ 7 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json index 11438cb..1148f86 100644 --- a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json +++ b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json @@ -9387,7 +9387,7 @@ "STEP", "WAIT", "CALLBACK", - "INVOKE" + "CHAINED_INVOKE" ] }, "OperationUpdate":{ diff --git a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json index 10a4cbb..a68b04c 100644 --- a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json +++ b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json @@ -9386,7 +9386,7 @@ "STEP", "WAIT", "CALLBACK", - "INVOKE" + "CHAINED_INVOKE" ] }, "OperationUpdate":{ diff --git a/src/aws_durable_execution_sdk_python/lambda_service.py b/src/aws_durable_execution_sdk_python/lambda_service.py index db80854..798c2a4 100644 --- a/src/aws_durable_execution_sdk_python/lambda_service.py +++ b/src/aws_durable_execution_sdk_python/lambda_service.py @@ -49,7 +49,24 @@ class OperationType(Enum): STEP = "STEP" WAIT = "WAIT" CALLBACK = "CALLBACK" - INVOKE = "INVOKE" + CHAINED_INVOKE = "CHAINED_INVOKE" + + +class CallbackTimeoutType(Enum): + TIMEOUT = "Callback.Timeout" + HEARTBEAT = "Callback.Heartbeat" + + +class ChainedInvokeFailedToStartType(Enum): + FAILED_TO_START = "ChainedInvoke.FailedToStart" + + +class ChainedInvokeTimeoutType(Enum): + TIMEOUT = "ChainedInvoke.Timeout" + + +class ChainedInvokeStopType(Enum): + STOPPED = "ChainedInvoke.Stopped" class OperationSubType(Enum): @@ -543,7 +560,7 @@ def create_invoke_start( return cls( operation_id=identifier.operation_id, parent_id=identifier.parent_id, - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, sub_type=OperationSubType.INVOKE, action=OperationAction.START, name=identifier.name, diff --git a/src/aws_durable_execution_sdk_python/state.py b/src/aws_durable_execution_sdk_python/state.py index eb704b9..3b2f79a 100644 --- a/src/aws_durable_execution_sdk_python/state.py +++ b/src/aws_durable_execution_sdk_python/state.py @@ -59,7 +59,7 @@ def create_from_operation(cls, operation: Operation) -> CheckpointedResult: result = callback_details.result if callback_details else None error = callback_details.error if callback_details else None - case OperationType.INVOKE: + case OperationType.CHAINED_INVOKE: invoke_details = operation.invoke_details result = invoke_details.result if invoke_details else None error = invoke_details.error if invoke_details else None diff --git a/tests/lambda_service_test.py b/tests/lambda_service_test.py index 63c67bf..14c8b95 100644 --- a/tests/lambda_service_test.py +++ b/tests/lambda_service_test.py @@ -815,13 +815,13 @@ def test_operation_update_wait_and_invoke_types(): invoke_options = InvokeOptions(function_name="test_func") invoke_update = OperationUpdate( operation_id="invoke_op", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, action=OperationAction.START, invoke_options=invoke_options, ) result = invoke_update.to_dict() - assert result["Type"] == "INVOKE" + assert result["Type"] == "CHAINED_INVOKE" assert result["InvokeOptions"]["FunctionName"] == "test_func" @@ -844,12 +844,12 @@ def test_operation_update_create_invoke(): invoke_options = InvokeOptions(function_name="test-function") update = OperationUpdate( operation_id="invoke1", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, action=OperationAction.START, invoke_options=invoke_options, ) - assert update.operation_type == OperationType.INVOKE + assert update.operation_type == OperationType.CHAINED_INVOKE assert update.invoke_options == invoke_options @@ -1299,7 +1299,7 @@ def test_operation_to_dict_with_invoke_details_partial(): operation = Operation( operation_id="test", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.PENDING, invoke_details=invoke_details, ) @@ -1402,7 +1402,7 @@ def test_operation_to_dict_with_invoke_details_error(): operation = Operation( operation_id="test", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, invoke_details=invoke_details, ) diff --git a/tests/operation/invoke_test.py b/tests/operation/invoke_test.py index fe4eaa5..f47471c 100644 --- a/tests/operation/invoke_test.py +++ b/tests/operation/invoke_test.py @@ -38,7 +38,7 @@ def test_invoke_handler_already_succeeded(): operation = Operation( operation_id="invoke1", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, invoke_details=InvokeDetails( durable_execution_arn="invoked_arn", result=json.dumps("test_result") @@ -66,7 +66,7 @@ def test_invoke_handler_already_succeeded_none_result(): operation = Operation( operation_id="invoke2", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, invoke_details=InvokeDetails(durable_execution_arn="invoked_arn", result=None), ) @@ -91,7 +91,7 @@ def test_invoke_handler_already_succeeded_no_invoke_details(): operation = Operation( operation_id="invoke3", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, invoke_details=None, ) @@ -119,7 +119,7 @@ def test_invoke_handler_already_failed(): ) operation = Operation( operation_id="invoke4", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, invoke_details=InvokeDetails(durable_execution_arn="invoked_arn", error=error), ) @@ -146,7 +146,7 @@ def test_invoke_handler_already_timed_out(): ) operation = Operation( operation_id="invoke5", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.TIMED_OUT, invoke_details=InvokeDetails(durable_execution_arn="invoked_arn", error=error), ) @@ -170,7 +170,7 @@ def test_invoke_handler_already_started(): operation = Operation( operation_id="invoke6", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), ) @@ -194,7 +194,7 @@ def test_invoke_handler_already_started_with_timeout(): operation = Operation( operation_id="invoke7", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), ) @@ -239,7 +239,7 @@ def test_invoke_handler_new_operation(): operation_update = mock_state.create_checkpoint.call_args[1]["operation_update"] assert operation_update.operation_id == "invoke8" - assert operation_update.operation_type == OperationType.INVOKE + assert operation_update.operation_type == OperationType.CHAINED_INVOKE assert operation_update.action == OperationAction.START assert operation_update.name == "test_invoke" assert operation_update.payload == json.dumps("test_input") @@ -316,7 +316,7 @@ def test_invoke_handler_custom_serdes(): operation = Operation( operation_id="invoke12", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, invoke_details=InvokeDetails( durable_execution_arn="invoked_arn", @@ -409,7 +409,7 @@ def test_invoke_handler_with_operation_name(): operation = Operation( operation_id="invoke14", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), ) @@ -433,7 +433,7 @@ def test_invoke_handler_without_operation_name(): operation = Operation( operation_id="invoke15", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), ) @@ -480,7 +480,7 @@ def test_invoke_handler_already_succeeded_with_none_payload(): operation = Operation( operation_id="invoke17", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, invoke_details=InvokeDetails( durable_execution_arn="invoked_arn", result=json.dumps("test_result") diff --git a/tests/state_test.py b/tests/state_test.py index a5fdbc3..61096b1 100644 --- a/tests/state_test.py +++ b/tests/state_test.py @@ -62,7 +62,7 @@ def test_checkpointed_result_create_from_operation_invoke(): ) operation = Operation( operation_id="op1", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, invoke_details=invoke_details, ) @@ -81,7 +81,7 @@ def test_checkpointed_result_create_from_operation_invoke_with_error(): invoke_details = InvokeDetails(durable_execution_arn="arn:test", error=error) operation = Operation( operation_id="op1", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, invoke_details=invoke_details, ) @@ -96,7 +96,7 @@ def test_checkpointed_result_create_from_operation_invoke_no_details(): """Test CheckpointedResult.create_from_operation with INVOKE operation but no invoke_details.""" operation = Operation( operation_id="op1", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, ) result = CheckpointedResult.create_from_operation(operation) @@ -116,7 +116,7 @@ def test_checkpointed_result_create_from_operation_invoke_with_both_result_and_e ) operation = Operation( operation_id="op1", - operation_type=OperationType.INVOKE, + operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, invoke_details=invoke_details, ) @@ -524,9 +524,9 @@ def test_checkpointed_result_is_timed_out_false_for_other_statuses(): status=status, ) result = CheckpointedResult.create_from_operation(operation) - assert ( - result.is_timed_out() is False - ), f"is_timed_out should be False for status {status}" + assert result.is_timed_out() is False, ( + f"is_timed_out should be False for status {status}" + ) def test_fetch_paginated_operations_with_marker(): From c5c3df1ed32c2a6d2044b0618a4108d493788528 Mon Sep 17 00:00:00 2001 From: Quinn Sinclair <> Date: Sun, 19 Oct 2025 21:50:59 +0000 Subject: [PATCH 2/4] [chore] Set ReplayChildren to type alias To ease the process of updating models, we are setting up replayChildren as a type alias. --- src/aws_durable_execution_sdk_python/lambda_service.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/aws_durable_execution_sdk_python/lambda_service.py b/src/aws_durable_execution_sdk_python/lambda_service.py index 798c2a4..dbad8cf 100644 --- a/src/aws_durable_execution_sdk_python/lambda_service.py +++ b/src/aws_durable_execution_sdk_python/lambda_service.py @@ -6,7 +6,7 @@ from dataclasses import dataclass, field from enum import Enum from pathlib import Path -from typing import TYPE_CHECKING, Any, Protocol +from typing import TYPE_CHECKING, Any, Protocol, TypeAlias import boto3 # type: ignore @@ -22,6 +22,8 @@ logger = logging.getLogger(__name__) +ReplayChildren: TypeAlias = bool + # region model class OperationAction(Enum): @@ -94,7 +96,7 @@ def from_dict(cls, data: MutableMapping[str, Any]) -> ExecutionDetails: @dataclass(frozen=True) class ContextDetails: - replay_children: bool = False + replay_children: ReplayChildren = False result: str | None = None error: ErrorObject | None = None @@ -287,7 +289,7 @@ def to_dict(self) -> MutableMapping[str, Any]: @dataclass(frozen=True) class ContextOptions: - replay_children: bool = False + replay_children: ReplayChildren = False @classmethod def from_dict(cls, data: MutableMapping[str, Any]) -> ContextOptions: From efcd0662313eb814681c2a8368656169c358fd91 Mon Sep 17 00:00:00 2001 From: Quinn Sinclair Date: Sun, 19 Oct 2025 23:54:23 +0000 Subject: [PATCH 3/4] [Chore] Update Model with rename of invoke to chained-invoke Ctx: Internal model has been updated, replacing invokeOptions and InvokeDetails with ChainedInvokeDetails and ChainedInvokeOptions --- .../2015-03-31/service-2.json | 12 +- .../lambdainternal/2015-03-31/service-2.json | 12 +- .../lambda_service.py | 72 +++++----- .../operation/invoke.py | 10 +- src/aws_durable_execution_sdk_python/state.py | 2 +- tests/lambda_service_test.py | 130 ++++++++---------- tests/operation/invoke_test.py | 39 +++--- tests/state_test.py | 20 ++- 8 files changed, 139 insertions(+), 158 deletions(-) diff --git a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json index 1148f86..bd0dd05 100644 --- a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json +++ b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal-local/2015-03-31/service-2.json @@ -7321,10 +7321,9 @@ "Error":{"shape":"EventError"} } }, - "InvokeDetails":{ + "ChainedInvokeDetails":{ "type":"structure", "members":{ - "DurableExecutionArn":{"shape":"DurableExecutionArn"}, "Result":{"shape":"OperationPayload"}, "Error":{"shape":"ErrorObject"} } @@ -7343,12 +7342,11 @@ "RESPONSE_STREAM" ] }, - "InvokeOptions":{ + "ChainedInvokeOptions":{ "type":"structure", "members":{ "FunctionName":{"shape":"FunctionName"}, - "FunctionQualifier":{"shape":"Version"}, - "DurableExecutionName":{"shape":"DurableExecutionName"} + "TimeoutSeconds":{"shape":"DurationSeconds"} } }, "InvokeResponseStreamUpdate":{ @@ -9332,7 +9330,7 @@ "StepDetails":{"shape":"StepDetails"}, "WaitDetails":{"shape":"WaitDetails"}, "CallbackDetails":{"shape":"CallbackDetails"}, - "InvokeDetails":{"shape":"InvokeDetails"} + "ChainedInvokeDetails":{"shape":"ChainedInvokeDetails"} } }, "OperationAction":{ @@ -9405,7 +9403,7 @@ "StepOptions":{"shape":"StepOptions"}, "WaitOptions":{"shape":"WaitOptions"}, "CallbackOptions":{"shape":"CallbackOptions"}, - "InvokeOptions":{"shape":"InvokeOptions"} + "ChainedInvokeOptions":{"shape":"ChainedInvokeOptions"} } }, "OperationUpdates":{ diff --git a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json index a68b04c..264d2c6 100644 --- a/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json +++ b/src/aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json @@ -7320,10 +7320,9 @@ "Error":{"shape":"EventError"} } }, - "InvokeDetails":{ + "ChainedInvokeDetails":{ "type":"structure", "members":{ - "DurableExecutionArn":{"shape":"DurableExecutionArn"}, "Result":{"shape":"OperationPayload"}, "Error":{"shape":"ErrorObject"} } @@ -7342,12 +7341,11 @@ "RESPONSE_STREAM" ] }, - "InvokeOptions":{ + "ChainedInvokeOptions":{ "type":"structure", "members":{ "FunctionName":{"shape":"FunctionName"}, - "FunctionQualifier":{"shape":"Version"}, - "DurableExecutionName":{"shape":"DurableExecutionName"} + "TimeoutSeconds":{"shape":"DurationSeconds"} } }, "InvokeResponseStreamUpdate":{ @@ -9331,7 +9329,7 @@ "StepDetails":{"shape":"StepDetails"}, "WaitDetails":{"shape":"WaitDetails"}, "CallbackDetails":{"shape":"CallbackDetails"}, - "InvokeDetails":{"shape":"InvokeDetails"} + "ChainedInvokeDetails":{"shape":"ChainedInvokeDetails"} } }, "OperationAction":{ @@ -9404,7 +9402,7 @@ "StepOptions":{"shape":"StepOptions"}, "WaitOptions":{"shape":"WaitOptions"}, "CallbackOptions":{"shape":"CallbackOptions"}, - "InvokeOptions":{"shape":"InvokeOptions"} + "ChainedInvokeOptions":{"shape":"ChainedInvokeOptions"} } }, "OperationUpdates":{ diff --git a/src/aws_durable_execution_sdk_python/lambda_service.py b/src/aws_durable_execution_sdk_python/lambda_service.py index dbad8cf..772b7f6 100644 --- a/src/aws_durable_execution_sdk_python/lambda_service.py +++ b/src/aws_durable_execution_sdk_python/lambda_service.py @@ -23,6 +23,8 @@ logger = logging.getLogger(__name__) ReplayChildren: TypeAlias = bool +OperationPayload: TypeAlias = str +TimeoutSeconds: TypeAlias = int # region model @@ -97,7 +99,7 @@ def from_dict(cls, data: MutableMapping[str, Any]) -> ExecutionDetails: @dataclass(frozen=True) class ContextDetails: replay_children: ReplayChildren = False - result: str | None = None + result: OperationPayload | None = None error: ErrorObject | None = None @classmethod @@ -169,7 +171,7 @@ def to_callable_runtime_error(self) -> CallableRuntimeError: class StepDetails: attempt: int = 0 next_attempt_timestamp: datetime.datetime | None = None - result: str | None = None + result: OperationPayload | None = None error: ErrorObject | None = None @classmethod @@ -209,16 +211,14 @@ def from_dict(cls, data: MutableMapping[str, Any]) -> CallbackDetails: @dataclass(frozen=True) -class InvokeDetails: - durable_execution_arn: str +class ChainedInvokeDetails: result: str | None = None error: ErrorObject | None = None @classmethod - def from_dict(cls, data: MutableMapping[str, Any]) -> InvokeDetails: + def from_dict(cls, data: MutableMapping[str, Any]) -> ChainedInvokeDetails: error_raw = data.get("Error") return cls( - durable_execution_arn=data["DurableExecutionArn"], result=data.get("Result"), error=ErrorObject.from_dict(error_raw) if error_raw else None, ) @@ -252,7 +252,7 @@ def to_dict(self) -> MutableMapping[str, Any]: @dataclass(frozen=True) class CallbackOptions: - timeout_seconds: int = 0 + timeout_seconds: TimeoutSeconds = 0 heartbeat_timeout_seconds: int = 0 @classmethod @@ -270,20 +270,22 @@ def to_dict(self) -> MutableMapping[str, Any]: @dataclass(frozen=True) -class InvokeOptions: +class ChainedInvokeOptions: function_name: str - timeout_seconds: int = 0 + timeout_seconds: TimeoutSeconds = 0 @classmethod - def from_dict(cls, data: MutableMapping[str, Any]) -> InvokeOptions: + def from_dict(cls, data: MutableMapping[str, Any]) -> ChainedInvokeOptions: return cls( function_name=data["FunctionName"], timeout_seconds=data.get("TimeoutSeconds", 0), ) def to_dict(self) -> MutableMapping[str, Any]: - result: MutableMapping[str, Any] = {"FunctionName": self.function_name} - result["TimeoutSeconds"] = self.timeout_seconds + result: MutableMapping[str, Any] = { + "FunctionName": self.function_name, + "TimeoutSeconds": self.timeout_seconds, + } return result @@ -318,7 +320,7 @@ class OperationUpdate: step_options: StepOptions | None = None wait_options: WaitOptions | None = None callback_options: CallbackOptions | None = None - invoke_options: InvokeOptions | None = None + chained_invoke_options: ChainedInvokeOptions | None = None def to_dict(self) -> MutableMapping[str, Any]: result: MutableMapping[str, Any] = { @@ -345,8 +347,8 @@ def to_dict(self) -> MutableMapping[str, Any]: result["WaitOptions"] = self.wait_options.to_dict() if self.callback_options: result["CallbackOptions"] = self.callback_options.to_dict() - if self.invoke_options: - result["InvokeOptions"] = self.invoke_options.to_dict() + if self.chained_invoke_options: + result["ChainedInvokeOptions"] = self.chained_invoke_options.to_dict() return result @@ -371,9 +373,9 @@ def from_dict(cls, data: MutableMapping[str, Any]) -> OperationUpdate: if callback_data := data.get("CallbackOptions"): callback_options = CallbackOptions.from_dict(callback_data) - invoke_options = None - if invoke_data := data.get("InvokeOptions"): - invoke_options = InvokeOptions.from_dict(invoke_data) + chained_invoke_options = None + if invoke_data := data.get("ChainedInvokeOptions"): + chained_invoke_options = ChainedInvokeOptions.from_dict(invoke_data) return cls( operation_id=data["Id"], @@ -388,7 +390,7 @@ def from_dict(cls, data: MutableMapping[str, Any]) -> OperationUpdate: step_options=step_options, wait_options=wait_options, callback_options=callback_options, - invoke_options=invoke_options, + chained_invoke_options=chained_invoke_options, ) @classmethod @@ -556,7 +558,7 @@ def create_invoke_start( cls, identifier: OperationIdentifier, payload: str, - invoke_options: InvokeOptions, + chained_invoke_options: ChainedInvokeOptions, ) -> OperationUpdate: """Create an instance of OperationUpdate for type: INVOKE, action: START.""" return cls( @@ -567,7 +569,7 @@ def create_invoke_start( action=OperationAction.START, name=identifier.name, payload=payload, - invoke_options=invoke_options, + chained_invoke_options=chained_invoke_options, ) # endregion invoke @@ -676,7 +678,7 @@ class Operation: step_details: StepDetails | None = None wait_details: WaitDetails | None = None callback_details: CallbackDetails | None = None - invoke_details: InvokeDetails | None = None + chained_invoke_details: ChainedInvokeDetails | None = None @classmethod def from_dict(cls, data: MutableMapping[str, Any]) -> Operation: @@ -715,9 +717,11 @@ def from_dict(cls, data: MutableMapping[str, Any]) -> Operation: if callback_details_input := data.get("CallbackDetails"): callback_details = CallbackDetails.from_dict(callback_details_input) - invoke_details = None - if invoke_details_input := data.get("InvokeDetails"): - invoke_details = InvokeDetails.from_dict(invoke_details_input) + chained_invoke_details = None + if chained_invoke_details := data.get("chained_invoke_details"): + chained_invoke_details = ChainedInvokeDetails.from_dict( + chained_invoke_details + ) return cls( operation_id=data["Id"], @@ -733,7 +737,7 @@ def from_dict(cls, data: MutableMapping[str, Any]) -> Operation: step_details=step_details, wait_details=wait_details, callback_details=callback_details, - invoke_details=invoke_details, + chained_invoke_details=chained_invoke_details, ) def to_dict(self) -> MutableMapping[str, Any]: @@ -782,15 +786,13 @@ def to_dict(self) -> MutableMapping[str, Any]: if self.callback_details.error: callback_dict["Error"] = self.callback_details.error.to_dict() result["CallbackDetails"] = callback_dict - if self.invoke_details: - invoke_dict: MutableMapping[str, Any] = { - "DurableExecutionArn": self.invoke_details.durable_execution_arn - } - if self.invoke_details.result: - invoke_dict["Result"] = self.invoke_details.result - if self.invoke_details.error: - invoke_dict["Error"] = self.invoke_details.error.to_dict() - result["InvokeDetails"] = invoke_dict + if self.chained_invoke_details: + invoke_dict: MutableMapping[str, Any] = {} + if self.chained_invoke_details.result: + invoke_dict["Result"] = self.chained_invoke_details.result + if self.chained_invoke_details.error: + invoke_dict["Error"] = self.chained_invoke_details.error.to_dict() + result["ChainedInvokeDetails"] = invoke_dict return result diff --git a/src/aws_durable_execution_sdk_python/operation/invoke.py b/src/aws_durable_execution_sdk_python/operation/invoke.py index 3002d07..fa1ff78 100644 --- a/src/aws_durable_execution_sdk_python/operation/invoke.py +++ b/src/aws_durable_execution_sdk_python/operation/invoke.py @@ -10,7 +10,7 @@ FatalError, ) from aws_durable_execution_sdk_python.lambda_service import ( - InvokeOptions, + ChainedInvokeOptions, OperationUpdate, ) from aws_durable_execution_sdk_python.serdes import deserialize, serialize @@ -50,12 +50,12 @@ def invoke_handler( # Return persisted result - no need to check for errors in successful operations if ( checkpointed_result.operation - and checkpointed_result.operation.invoke_details - and checkpointed_result.operation.invoke_details.result + and checkpointed_result.operation.chained_invoke_details + and checkpointed_result.operation.chained_invoke_details.result ): return deserialize( serdes=config.serdes_result, - data=checkpointed_result.operation.invoke_details.result, + data=checkpointed_result.operation.chained_invoke_details.result, operation_id=operation_identifier.operation_id, durable_execution_arn=state.durable_execution_arn, ) @@ -85,7 +85,7 @@ def invoke_handler( start_operation: OperationUpdate = OperationUpdate.create_invoke_start( identifier=operation_identifier, payload=serialized_payload, - invoke_options=InvokeOptions( + chained_invoke_options=ChainedInvokeOptions( function_name=function_name, timeout_seconds=config.timeout_seconds ), ) diff --git a/src/aws_durable_execution_sdk_python/state.py b/src/aws_durable_execution_sdk_python/state.py index 3b2f79a..d529b1d 100644 --- a/src/aws_durable_execution_sdk_python/state.py +++ b/src/aws_durable_execution_sdk_python/state.py @@ -60,7 +60,7 @@ def create_from_operation(cls, operation: Operation) -> CheckpointedResult: error = callback_details.error if callback_details else None case OperationType.CHAINED_INVOKE: - invoke_details = operation.invoke_details + invoke_details = operation.chained_invoke_details result = invoke_details.result if invoke_details else None error = invoke_details.error if invoke_details else None diff --git a/tests/lambda_service_test.py b/tests/lambda_service_test.py index 14c8b95..39489bb 100644 --- a/tests/lambda_service_test.py +++ b/tests/lambda_service_test.py @@ -20,8 +20,8 @@ DurableServiceClient, ErrorObject, ExecutionDetails, - InvokeDetails, - InvokeOptions, + ChainedInvokeDetails, + ChainedInvokeOptions, LambdaClient, Operation, OperationAction, @@ -330,42 +330,35 @@ def test_callback_details_minimal(): def test_invoke_details_from_dict(): - """Test InvokeDetails.from_dict method.""" + """Test ChainedInvokeDetails.from_dict method.""" error_data = {"ErrorMessage": "Invoke error"} data = { - "DurableExecutionArn": "arn:test", "Result": "invoke_result", "Error": error_data, } - details = InvokeDetails.from_dict(data) - assert details.durable_execution_arn == "arn:test" + details = ChainedInvokeDetails.from_dict(data) assert details.result == "invoke_result" assert details.error.message == "Invoke error" def test_invoke_details_all_fields(): - """Test InvokeDetails.from_dict with all fields.""" + """Test ChainedInvokeDetails.from_dict with all fields.""" error_data = {"ErrorMessage": "Invoke failed", "ErrorType": "InvokeError"} data = { - "DurableExecutionArn": "arn:aws:lambda:us-west-2:123456789012:function:test", "Result": "invoke_success", "Error": error_data, } - details = InvokeDetails.from_dict(data) - assert ( - details.durable_execution_arn - == "arn:aws:lambda:us-west-2:123456789012:function:test" - ) + details = ChainedInvokeDetails.from_dict(data) assert details.result == "invoke_success" assert details.error.message == "Invoke failed" assert details.error.type == "InvokeError" def test_invoke_details_minimal(): - """Test InvokeDetails.from_dict with minimal required data.""" + """Test ChainedInvokeDetails.from_dict with minimal required data.""" data = {"DurableExecutionArn": "arn:minimal"} - details = InvokeDetails.from_dict(data) - assert details.durable_execution_arn == "arn:minimal" + details = ChainedInvokeDetails.from_dict(data) + assert hasattr(details, "durable_execution_arn") == False assert details.result is None assert details.error is None @@ -405,17 +398,17 @@ def test_callback_options_from_dict_partial(): def test_invoke_options_from_dict(): - """Test InvokeOptions.from_dict method.""" + """Test ChainedInvokeOptions.from_dict method.""" data = {"FunctionName": "test-function", "TimeoutSeconds": 120} - options = InvokeOptions.from_dict(data) + options = ChainedInvokeOptions.from_dict(data) assert options.function_name == "test-function" assert options.timeout_seconds == 120 def test_invoke_options_from_dict_required_only(): - """Test InvokeOptions.from_dict with only required field.""" + """Test ChainedInvokeOptions.from_dict with only required field.""" data = {"FunctionName": "test-function"} - options = InvokeOptions.from_dict(data) + options = ChainedInvokeOptions.from_dict(data) assert options.function_name == "test-function" assert options.timeout_seconds == 0 @@ -450,10 +443,10 @@ def test_callback_options_roundtrip(): def test_invoke_options_roundtrip(): - """Test InvokeOptions to_dict -> from_dict roundtrip.""" - original = InvokeOptions(function_name="test-func", timeout_seconds=120) + """Test ChainedInvokeOptions to_dict -> from_dict roundtrip.""" + original = ChainedInvokeOptions(function_name="test-func", timeout_seconds=120) data = original.to_dict() - restored = InvokeOptions.from_dict(data) + restored = ChainedInvokeOptions.from_dict(data) assert restored == original @@ -502,8 +495,8 @@ def test_callback_options_all_fields(): def test_invoke_options_to_dict(): - """Test InvokeOptions.to_dict method.""" - options = InvokeOptions( + """Test ChainedInvokeOptions.to_dict method.""" + options = ChainedInvokeOptions( function_name="test_function", timeout_seconds=30, ) @@ -516,8 +509,8 @@ def test_invoke_options_to_dict(): def test_invoke_options_to_dict_minimal(): - """Test InvokeOptions.to_dict with minimal fields.""" - options = InvokeOptions(function_name="test_function") + """Test ChainedInvokeOptions.to_dict with minimal fields.""" + options = ChainedInvokeOptions(function_name="test_function") result = options.to_dict() assert result == {"FunctionName": "test_function", "TimeoutSeconds": 0} @@ -544,16 +537,16 @@ def test_context_options_to_dict_false(): def test_invoke_options_from_dict_missing_function_name(): - """Test InvokeOptions.from_dict with missing required FunctionName.""" + """Test ChainedInvokeOptions.from_dict with missing required FunctionName.""" data = {"TimeoutSeconds": 60} with pytest.raises(KeyError): - InvokeOptions.from_dict(data) + ChainedInvokeOptions.from_dict(data) def test_invoke_options_to_dict_complete(): - """Test InvokeOptions.to_dict with all fields.""" - options = InvokeOptions(function_name="test_func", timeout_seconds=120) + """Test ChainedInvokeOptions.to_dict with all fields.""" + options = ChainedInvokeOptions(function_name="test_func", timeout_seconds=120) result = options.to_dict() @@ -569,7 +562,7 @@ def test_invoke_options_to_dict_complete(): def test_operation_update_create_invoke_start(): """Test OperationUpdate.create_invoke_start method to cover line 545.""" identifier = OperationIdentifier("test-id", "parent-id") - invoke_options = InvokeOptions("test-func", 120) + invoke_options = ChainedInvokeOptions("test-func", 120) update = OperationUpdate.create_invoke_start(identifier, "payload", invoke_options) assert update.operation_id == "test-id" @@ -616,7 +609,9 @@ def test_operation_update_to_dict_complete(): callback_options = CallbackOptions( timeout_seconds=300, heartbeat_timeout_seconds=60 ) - invoke_options = InvokeOptions(function_name="test_func", timeout_seconds=60) + chained_invoke_options = ChainedInvokeOptions( + function_name="test_func", timeout_seconds=60 + ) update = OperationUpdate( operation_id="op1", @@ -629,7 +624,7 @@ def test_operation_update_to_dict_complete(): step_options=step_options, wait_options=wait_options, callback_options=callback_options, - invoke_options=invoke_options, + chained_invoke_options=chained_invoke_options, ) result = update.to_dict() @@ -644,7 +639,7 @@ def test_operation_update_to_dict_complete(): "StepOptions": {"NextAttemptDelaySeconds": 30}, "WaitOptions": {"WaitSeconds": 60}, "CallbackOptions": {"TimeoutSeconds": 300, "HeartbeatTimeoutSeconds": 60}, - "InvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 60}, + "ChainedInvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 60}, } assert result == expected @@ -812,17 +807,17 @@ def test_operation_update_wait_and_invoke_types(): assert result["WaitOptions"]["WaitSeconds"] == 30 # Test INVOKE operation - invoke_options = InvokeOptions(function_name="test_func") + chained_invoke_options = ChainedInvokeOptions(function_name="test_func") invoke_update = OperationUpdate( operation_id="invoke_op", operation_type=OperationType.CHAINED_INVOKE, action=OperationAction.START, - invoke_options=invoke_options, + chained_invoke_options=chained_invoke_options, ) result = invoke_update.to_dict() assert result["Type"] == "CHAINED_INVOKE" - assert result["InvokeOptions"]["FunctionName"] == "test_func" + assert result["ChainedInvokeOptions"]["FunctionName"] == "test_func" def test_operation_update_create_wait(): @@ -841,16 +836,16 @@ def test_operation_update_create_wait(): def test_operation_update_create_invoke(): """Test OperationUpdate factory method for INVOKE operations.""" - invoke_options = InvokeOptions(function_name="test-function") + chained_invoke_options = ChainedInvokeOptions(function_name="test-function") update = OperationUpdate( operation_id="invoke1", operation_type=OperationType.CHAINED_INVOKE, action=OperationAction.START, - invoke_options=invoke_options, + chained_invoke_options=chained_invoke_options, ) assert update.operation_type == OperationType.CHAINED_INVOKE - assert update.invoke_options == invoke_options + assert update.chained_invoke_options == chained_invoke_options def test_operation_update_with_sub_type(): @@ -889,7 +884,9 @@ def test_operation_update_complete_with_new_fields(): callback_options = CallbackOptions( timeout_seconds=300, heartbeat_timeout_seconds=60 ) - invoke_options = InvokeOptions(function_name="test_func", timeout_seconds=60) + chained_invoke_options = ChainedInvokeOptions( + function_name="test_func", timeout_seconds=60 + ) update = OperationUpdate( operation_id="op1", @@ -904,7 +901,7 @@ def test_operation_update_complete_with_new_fields(): step_options=step_options, wait_options=wait_options, callback_options=callback_options, - invoke_options=invoke_options, + chained_invoke_options=chained_invoke_options, ) result = update.to_dict() @@ -921,7 +918,7 @@ def test_operation_update_complete_with_new_fields(): "StepOptions": {"NextAttemptDelaySeconds": 30}, "WaitOptions": {"WaitSeconds": 60}, "CallbackOptions": {"TimeoutSeconds": 300, "HeartbeatTimeoutSeconds": 60}, - "InvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 60}, + "ChainedInvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 60}, } assert result == expected @@ -1078,7 +1075,7 @@ def test_operation_update_from_dict_with_all_options(): "StepOptions": {"NextAttemptDelaySeconds": 30}, "WaitOptions": {"WaitSeconds": 60}, "CallbackOptions": {"TimeoutSeconds": 300, "HeartbeatTimeoutSeconds": 60}, - "InvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 120}, + "ChainedInvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 120}, } update = OperationUpdate.from_dict(data) @@ -1089,7 +1086,7 @@ def test_operation_update_from_dict_with_all_options(): assert update.step_options is not None assert update.wait_options is not None assert update.callback_options is not None - assert update.invoke_options is not None + assert update.chained_invoke_options is not None # ============================================================================= @@ -1109,7 +1106,7 @@ def test_operation_from_dict_with_all_options(): "StepOptions": {"NextAttemptDelaySeconds": 30}, "WaitOptions": {"WaitSeconds": 60}, "CallbackOptions": {"TimeoutSeconds": 300, "HeartbeatTimeoutSeconds": 60}, - "InvokeOptions": {"FunctionName": "test-func", "TimeoutSeconds": 120}, + "ChainedInvokeOptions": {"FunctionName": "test-func", "TimeoutSeconds": 120}, } operation = Operation.from_dict(data) assert operation.operation_id == "test-id" @@ -1173,13 +1170,13 @@ def test_operation_from_dict_individual_options(): op4 = Operation.from_dict(data4) assert op4.operation_id == "test4" - # Test with just InvokeOptions + # Test with just ChainedInvokeOptions data5 = { "Id": "test5", "Type": "STEP", "Action": "START", "Status": "PENDING", - "InvokeOptions": {"FunctionName": "test-func"}, + "ChainedInvokeOptions": {"FunctionName": "test-func"}, } op5 = Operation.from_dict(data5) assert op5.operation_id == "test5" @@ -1195,7 +1192,7 @@ def test_operation_from_dict_with_all_option_types(): "StepOptions": {"NextAttemptDelaySeconds": 30}, "WaitOptions": {"WaitSeconds": 60}, "CallbackOptions": {"TimeoutSeconds": 300, "HeartbeatTimeoutSeconds": 60}, - "InvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 120}, + "ChainedInvokeOptions": {"FunctionName": "test_func", "TimeoutSeconds": 120}, } operation = Operation.from_dict(data) @@ -1219,9 +1216,7 @@ def test_operation_to_dict_with_all_details(): callback_details = CallbackDetails( callback_id="cb123", result="callback_result", error=None ) - invoke_details = InvokeDetails( - durable_execution_arn="arn:test", result="invoke_result", error=None - ) + chained_invoke_details = ChainedInvokeDetails(result="invoke_result", error=None) operation = Operation( operation_id="test", @@ -1236,7 +1231,7 @@ def test_operation_to_dict_with_all_details(): step_details=step_details, wait_details=wait_details, callback_details=callback_details, - invoke_details=invoke_details, + chained_invoke_details=chained_invoke_details, sub_type=OperationSubType.STEP, ) @@ -1249,7 +1244,7 @@ def test_operation_to_dict_with_all_details(): 2023, 1, 1, tzinfo=datetime.UTC ) assert result["CallbackDetails"]["CallbackId"] == "cb123" - assert result["InvokeDetails"]["DurableExecutionArn"] == "arn:test" + assert result["ChainedInvokeDetails"]["Result"] == "invoke_result" def test_operation_to_dict_with_step_details_partial(): @@ -1293,20 +1288,17 @@ def test_operation_to_dict_with_callback_details_partial(): def test_operation_to_dict_with_invoke_details_partial(): """Test Operation.to_dict with invoke_details having some None fields.""" - invoke_details = InvokeDetails( - durable_execution_arn="arn:test", result=None, error=None - ) + chained_invoke_details = ChainedInvokeDetails(result=None, error=None) operation = Operation( operation_id="test", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.PENDING, - invoke_details=invoke_details, + chained_invoke_details=chained_invoke_details, ) result = operation.to_dict() - invoke_dict = result["InvokeDetails"] - assert invoke_dict["DurableExecutionArn"] == "arn:test" + invoke_dict = result["ChainedInvokeDetails"] assert "Result" not in invoke_dict assert "Error" not in invoke_dict @@ -1392,23 +1384,21 @@ def test_operation_to_dict_with_callback_details_error(): def test_operation_to_dict_with_invoke_details_error(): - """Test Operation.to_dict with invoke_details having error.""" + """Test Operation.to_dict with chained_invoke_details having error.""" error = ErrorObject( message="Invoke failed", type="InvokeError", data=None, stack_trace=None ) - invoke_details = InvokeDetails( - durable_execution_arn="arn:test", result=None, error=error - ) + chained_invoke_details = ChainedInvokeDetails(result=None, error=error) operation = Operation( operation_id="test", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, - invoke_details=invoke_details, + chained_invoke_details=chained_invoke_details, ) result = operation.to_dict() - invoke_dict = result["InvokeDetails"] + invoke_dict = result["ChainedInvokeDetails"] assert invoke_dict["Error"]["ErrorMessage"] == "Invoke failed" assert invoke_dict["Error"]["ErrorType"] == "InvokeError" @@ -1467,7 +1457,10 @@ def test_operation_from_dict_complete(): "StepDetails": {"Result": "step_result", "Attempt": 1}, "WaitDetails": {"ScheduledTimestamp": start_time}, "CallbackDetails": {"CallbackId": "cb1", "Result": "callback_result"}, - "InvokeDetails": {"DurableExecutionArn": "arn:test", "Result": "invoke_result"}, + "ChainedInvokeDetails": { + "DurableExecutionArn": "arn:test", + "Result": "invoke_result", + }, } operation = Operation.from_dict(data) assert operation.operation_id == "op1" @@ -1483,7 +1476,6 @@ def test_operation_from_dict_complete(): assert operation.step_details.result == "step_result" assert operation.wait_details.scheduled_timestamp == start_time assert operation.callback_details.callback_id == "cb1" - assert operation.invoke_details.durable_execution_arn == "arn:test" def test_operation_to_dict_with_subtype(): diff --git a/tests/operation/invoke_test.py b/tests/operation/invoke_test.py index f47471c..834958a 100644 --- a/tests/operation/invoke_test.py +++ b/tests/operation/invoke_test.py @@ -17,7 +17,7 @@ from aws_durable_execution_sdk_python.identifier import OperationIdentifier from aws_durable_execution_sdk_python.lambda_service import ( ErrorObject, - InvokeDetails, + ChainedInvokeDetails, Operation, OperationAction, OperationStatus, @@ -40,9 +40,7 @@ def test_invoke_handler_already_succeeded(): operation_id="invoke1", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, - invoke_details=InvokeDetails( - durable_execution_arn="invoked_arn", result=json.dumps("test_result") - ), + chained_invoke_details=ChainedInvokeDetails(result=json.dumps("test_result")), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -68,7 +66,7 @@ def test_invoke_handler_already_succeeded_none_result(): operation_id="invoke2", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, - invoke_details=InvokeDetails(durable_execution_arn="invoked_arn", result=None), + chained_invoke_details=ChainedInvokeDetails(result=None), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -84,8 +82,8 @@ def test_invoke_handler_already_succeeded_none_result(): assert result is None -def test_invoke_handler_already_succeeded_no_invoke_details(): - """Test invoke_handler when operation succeeded but has no invoke_details.""" +def test_invoke_handler_already_succeeded_no_chained_invoke_details(): + """Test invoke_handler when operation succeeded but has no chained_invoke_details.""" mock_state = Mock(spec=ExecutionState) mock_state.durable_execution_arn = "test_arn" @@ -93,7 +91,7 @@ def test_invoke_handler_already_succeeded_no_invoke_details(): operation_id="invoke3", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, - invoke_details=None, + chained_invoke_details=None, ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -121,7 +119,7 @@ def test_invoke_handler_already_failed(): operation_id="invoke4", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, - invoke_details=InvokeDetails(durable_execution_arn="invoked_arn", error=error), + chained_invoke_details=ChainedInvokeDetails(error=error), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -148,7 +146,7 @@ def test_invoke_handler_already_timed_out(): operation_id="invoke5", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.TIMED_OUT, - invoke_details=InvokeDetails(durable_execution_arn="invoked_arn", error=error), + chained_invoke_details=ChainedInvokeDetails(error=error), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -172,7 +170,7 @@ def test_invoke_handler_already_started(): operation_id="invoke6", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, - invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), + chained_invoke_details=ChainedInvokeDetails(), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -196,7 +194,7 @@ def test_invoke_handler_already_started_with_timeout(): operation_id="invoke7", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, - invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), + chained_invoke_details=ChainedInvokeDetails(), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -243,8 +241,8 @@ def test_invoke_handler_new_operation(): assert operation_update.action == OperationAction.START assert operation_update.name == "test_invoke" assert operation_update.payload == json.dumps("test_input") - assert operation_update.invoke_options.function_name == "test_function" - assert operation_update.invoke_options.timeout_seconds == 60 + assert operation_update.chained_invoke_options.function_name == "test_function" + assert operation_update.chained_invoke_options.timeout_seconds == 60 def test_invoke_handler_new_operation_with_timeout(): @@ -306,7 +304,7 @@ def test_invoke_handler_no_config(): # Verify default config was used operation_update = mock_state.create_checkpoint.call_args[1]["operation_update"] - assert operation_update.invoke_options.timeout_seconds == 0 + assert operation_update.chained_invoke_options.timeout_seconds == 0 def test_invoke_handler_custom_serdes(): @@ -318,8 +316,7 @@ def test_invoke_handler_custom_serdes(): operation_id="invoke12", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, - invoke_details=InvokeDetails( - durable_execution_arn="invoked_arn", + chained_invoke_details=ChainedInvokeDetails( result='{"key": "VALUE", "number": "84", "list": [1, 2, 3]}', ), ) @@ -411,7 +408,7 @@ def test_invoke_handler_with_operation_name(): operation_id="invoke14", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, - invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), + chained_invoke_details=ChainedInvokeDetails(), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -435,7 +432,7 @@ def test_invoke_handler_without_operation_name(): operation_id="invoke15", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.STARTED, - invoke_details=InvokeDetails(durable_execution_arn="invoked_arn"), + chained_invoke_details=ChainedInvokeDetails(), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result @@ -482,9 +479,7 @@ def test_invoke_handler_already_succeeded_with_none_payload(): operation_id="invoke17", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, - invoke_details=InvokeDetails( - durable_execution_arn="invoked_arn", result=json.dumps("test_result") - ), + chained_invoke_details=ChainedInvokeDetails(result=json.dumps("test_result")), ) mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result diff --git a/tests/state_test.py b/tests/state_test.py index 61096b1..5a6550a 100644 --- a/tests/state_test.py +++ b/tests/state_test.py @@ -10,7 +10,7 @@ CheckpointOutput, CheckpointUpdatedExecutionState, ErrorObject, - InvokeDetails, + ChainedInvokeDetails, LambdaClient, Operation, OperationAction, @@ -57,14 +57,12 @@ def test_checkpointed_result_create_from_operation_callback(): def test_checkpointed_result_create_from_operation_invoke(): """Test CheckpointedResult.create_from_operation with INVOKE operation.""" - invoke_details = InvokeDetails( - durable_execution_arn="arn:test", result="invoke_result" - ) + chained_invoke_details = ChainedInvokeDetails(result="invoke_result") operation = Operation( operation_id="op1", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.SUCCEEDED, - invoke_details=invoke_details, + chained_invoke_details=chained_invoke_details, ) result = CheckpointedResult.create_from_operation(operation) assert result.operation == operation @@ -78,12 +76,12 @@ def test_checkpointed_result_create_from_operation_invoke_with_error(): error = ErrorObject( message="Invoke error", type="InvokeError", data=None, stack_trace=None ) - invoke_details = InvokeDetails(durable_execution_arn="arn:test", error=error) + chained_invoke_details = ChainedInvokeDetails(error=error) operation = Operation( operation_id="op1", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, - invoke_details=invoke_details, + chained_invoke_details=chained_invoke_details, ) result = CheckpointedResult.create_from_operation(operation) assert result.operation == operation @@ -93,7 +91,7 @@ def test_checkpointed_result_create_from_operation_invoke_with_error(): def test_checkpointed_result_create_from_operation_invoke_no_details(): - """Test CheckpointedResult.create_from_operation with INVOKE operation but no invoke_details.""" + """Test CheckpointedResult.create_from_operation with INVOKE operation but no chained_invoke_details.""" operation = Operation( operation_id="op1", operation_type=OperationType.CHAINED_INVOKE, @@ -111,14 +109,12 @@ def test_checkpointed_result_create_from_operation_invoke_with_both_result_and_e error = ErrorObject( message="Invoke error", type="InvokeError", data=None, stack_trace=None ) - invoke_details = InvokeDetails( - durable_execution_arn="arn:test", result="invoke_result", error=error - ) + chained_invoke_details = ChainedInvokeDetails(result="invoke_result", error=error) operation = Operation( operation_id="op1", operation_type=OperationType.CHAINED_INVOKE, status=OperationStatus.FAILED, - invoke_details=invoke_details, + chained_invoke_details=chained_invoke_details, ) result = CheckpointedResult.create_from_operation(operation) assert result.operation == operation From a6e2a092d2258a43edb8242d7a0e48565dfe05ab Mon Sep 17 00:00:00 2001 From: Quinn Sinclair Date: Mon, 20 Oct 2025 00:20:12 +0000 Subject: [PATCH 4/4] [Chore] CI fixes Formatting etc --- src/aws_durable_execution_sdk_python/lambda_service.py | 8 ++++---- tests/lambda_service_test.py | 6 +++--- tests/operation/invoke_test.py | 2 +- tests/state_test.py | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/aws_durable_execution_sdk_python/lambda_service.py b/src/aws_durable_execution_sdk_python/lambda_service.py index 772b7f6..fb8d622 100644 --- a/src/aws_durable_execution_sdk_python/lambda_service.py +++ b/src/aws_durable_execution_sdk_python/lambda_service.py @@ -6,7 +6,7 @@ from dataclasses import dataclass, field from enum import Enum from pathlib import Path -from typing import TYPE_CHECKING, Any, Protocol, TypeAlias +from typing import TYPE_CHECKING, Any, Protocol import boto3 # type: ignore @@ -22,9 +22,9 @@ logger = logging.getLogger(__name__) -ReplayChildren: TypeAlias = bool -OperationPayload: TypeAlias = str -TimeoutSeconds: TypeAlias = int +type ReplayChildren = bool +type OperationPayload = str +type TimeoutSeconds = int # region model diff --git a/tests/lambda_service_test.py b/tests/lambda_service_test.py index 39489bb..ae70530 100644 --- a/tests/lambda_service_test.py +++ b/tests/lambda_service_test.py @@ -13,6 +13,8 @@ from aws_durable_execution_sdk_python.lambda_service import ( CallbackDetails, CallbackOptions, + ChainedInvokeDetails, + ChainedInvokeOptions, CheckpointOutput, CheckpointUpdatedExecutionState, ContextDetails, @@ -20,8 +22,6 @@ DurableServiceClient, ErrorObject, ExecutionDetails, - ChainedInvokeDetails, - ChainedInvokeOptions, LambdaClient, Operation, OperationAction, @@ -358,7 +358,7 @@ def test_invoke_details_minimal(): """Test ChainedInvokeDetails.from_dict with minimal required data.""" data = {"DurableExecutionArn": "arn:minimal"} details = ChainedInvokeDetails.from_dict(data) - assert hasattr(details, "durable_execution_arn") == False + assert hasattr(details, "durable_execution_arn") is False assert details.result is None assert details.error is None diff --git a/tests/operation/invoke_test.py b/tests/operation/invoke_test.py index 834958a..738b2dd 100644 --- a/tests/operation/invoke_test.py +++ b/tests/operation/invoke_test.py @@ -16,8 +16,8 @@ ) from aws_durable_execution_sdk_python.identifier import OperationIdentifier from aws_durable_execution_sdk_python.lambda_service import ( - ErrorObject, ChainedInvokeDetails, + ErrorObject, Operation, OperationAction, OperationStatus, diff --git a/tests/state_test.py b/tests/state_test.py index 5a6550a..3ace94e 100644 --- a/tests/state_test.py +++ b/tests/state_test.py @@ -7,10 +7,10 @@ from aws_durable_execution_sdk_python.exceptions import DurableExecutionsError from aws_durable_execution_sdk_python.lambda_service import ( CallbackDetails, + ChainedInvokeDetails, CheckpointOutput, CheckpointUpdatedExecutionState, ErrorObject, - ChainedInvokeDetails, LambdaClient, Operation, OperationAction, @@ -520,9 +520,9 @@ def test_checkpointed_result_is_timed_out_false_for_other_statuses(): status=status, ) result = CheckpointedResult.create_from_operation(operation) - assert result.is_timed_out() is False, ( - f"is_timed_out should be False for status {status}" - ) + assert ( + result.is_timed_out() is False + ), f"is_timed_out should be False for status {status}" def test_fetch_paginated_operations_with_marker():