localstack-core 4.10.1.dev42__py3-none-any.whl → 4.12.1.dev18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of localstack-core might be problematic. Click here for more details.
- localstack/aws/api/apigateway/__init__.py +42 -0
- localstack/aws/api/cloudformation/__init__.py +161 -0
- localstack/aws/api/ec2/__init__.py +1178 -12
- localstack/aws/api/iam/__init__.py +228 -0
- localstack/aws/api/kms/__init__.py +1 -0
- localstack/aws/api/lambda_/__init__.py +1034 -66
- localstack/aws/api/logs/__init__.py +500 -0
- localstack/aws/api/opensearch/__init__.py +100 -0
- localstack/aws/api/redshift/__init__.py +69 -0
- localstack/aws/api/resourcegroupstaggingapi/__init__.py +36 -0
- localstack/aws/api/route53/__init__.py +45 -0
- localstack/aws/api/route53resolver/__init__.py +1 -0
- localstack/aws/api/s3/__init__.py +64 -0
- localstack/aws/api/s3control/__init__.py +19 -0
- localstack/aws/api/secretsmanager/__init__.py +37 -23
- localstack/aws/api/stepfunctions/__init__.py +52 -10
- localstack/aws/api/sts/__init__.py +52 -0
- localstack/aws/connect.py +35 -15
- localstack/aws/handlers/logging.py +8 -4
- localstack/aws/handlers/service.py +11 -2
- localstack/aws/protocol/serializer.py +1 -1
- localstack/config.py +8 -0
- localstack/constants.py +3 -0
- localstack/deprecations.py +0 -6
- localstack/dev/kubernetes/__main__.py +39 -14
- localstack/runtime/analytics.py +11 -0
- localstack/services/acm/provider.py +17 -1
- localstack/services/apigateway/legacy/provider.py +28 -15
- localstack/services/cloudformation/engine/template_preparer.py +6 -2
- localstack/services/cloudformation/engine/v2/change_set_model.py +9 -0
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +15 -1
- localstack/services/cloudformation/engine/v2/change_set_resource_support_checker.py +114 -0
- localstack/services/cloudformation/provider.py +26 -1
- localstack/services/cloudformation/provider_utils.py +20 -0
- localstack/services/cloudformation/resource_provider.py +5 -4
- localstack/services/cloudformation/scaffolding/__main__.py +94 -22
- localstack/services/cloudformation/v2/provider.py +41 -0
- localstack/services/cloudwatch/provider.py +10 -3
- localstack/services/cloudwatch/provider_v2.py +6 -3
- localstack/services/configservice/provider.py +5 -1
- localstack/services/dynamodb/provider.py +1 -0
- localstack/services/dynamodb/v2/provider.py +1 -0
- localstack/services/dynamodbstreams/provider.py +6 -0
- localstack/services/dynamodbstreams/v2/provider.py +6 -0
- localstack/services/ec2/provider.py +6 -0
- localstack/services/es/provider.py +6 -0
- localstack/services/events/provider.py +4 -0
- localstack/services/events/v1/provider.py +9 -0
- localstack/services/firehose/provider.py +5 -0
- localstack/services/iam/provider.py +4 -0
- localstack/services/kinesis/packages.py +1 -1
- localstack/services/kms/models.py +16 -22
- localstack/services/kms/provider.py +4 -0
- localstack/services/lambda_/analytics.py +11 -2
- localstack/services/lambda_/api_utils.py +37 -20
- localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
- localstack/services/lambda_/invocation/assignment.py +4 -1
- localstack/services/lambda_/invocation/event_manager.py +15 -11
- localstack/services/lambda_/invocation/execution_environment.py +21 -2
- localstack/services/lambda_/invocation/lambda_models.py +31 -2
- localstack/services/lambda_/invocation/lambda_service.py +62 -3
- localstack/services/lambda_/invocation/models.py +9 -1
- localstack/services/lambda_/invocation/version_manager.py +18 -3
- localstack/services/lambda_/provider.py +307 -106
- localstack/services/lambda_/resource_providers/aws_lambda_function.py +33 -1
- localstack/services/lambda_/runtimes.py +3 -1
- localstack/services/logs/provider.py +9 -0
- localstack/services/opensearch/packages.py +34 -20
- localstack/services/opensearch/provider.py +53 -3
- localstack/services/resource_groups/provider.py +5 -1
- localstack/services/resourcegroupstaggingapi/provider.py +6 -1
- localstack/services/route53/provider.py +7 -0
- localstack/services/route53resolver/provider.py +5 -0
- localstack/services/s3/constants.py +5 -0
- localstack/services/s3/exceptions.py +9 -0
- localstack/services/s3/models.py +9 -1
- localstack/services/s3/provider.py +51 -43
- localstack/services/s3/utils.py +81 -15
- localstack/services/s3control/provider.py +107 -2
- localstack/services/s3control/validation.py +50 -0
- localstack/services/scheduler/provider.py +4 -2
- localstack/services/secretsmanager/provider.py +4 -0
- localstack/services/ses/provider.py +4 -0
- localstack/services/sns/constants.py +16 -1
- localstack/services/sns/provider.py +5 -0
- localstack/services/sns/publisher.py +15 -6
- localstack/services/sns/v2/models.py +9 -0
- localstack/services/sns/v2/provider.py +750 -19
- localstack/services/sns/v2/utils.py +12 -0
- localstack/services/sqs/constants.py +6 -0
- localstack/services/sqs/provider.py +9 -1
- localstack/services/sqs/resource_providers/aws_sqs_queue.py +61 -46
- localstack/services/ssm/provider.py +6 -0
- localstack/services/stepfunctions/asl/component/common/path/result_path.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/execute_state.py +0 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +0 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/lambda_eval_utils.py +8 -8
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/{mock_eval_utils.py → local_mock_eval_utils.py} +13 -9
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service.py +6 -6
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_callback.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +4 -0
- localstack/services/stepfunctions/asl/component/test_state/state/base_mock.py +118 -0
- localstack/services/stepfunctions/asl/component/test_state/state/common.py +82 -0
- localstack/services/stepfunctions/asl/component/test_state/state/execution.py +139 -0
- localstack/services/stepfunctions/asl/component/test_state/state/map.py +77 -0
- localstack/services/stepfunctions/asl/component/test_state/state/task.py +44 -0
- localstack/services/stepfunctions/asl/eval/environment.py +30 -22
- localstack/services/stepfunctions/asl/eval/states.py +1 -1
- localstack/services/stepfunctions/asl/eval/test_state/environment.py +49 -9
- localstack/services/stepfunctions/asl/eval/test_state/program_state.py +22 -0
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +5 -1
- localstack/services/stepfunctions/asl/parse/preprocessor.py +67 -24
- localstack/services/stepfunctions/asl/parse/test_state/asl_parser.py +5 -4
- localstack/services/stepfunctions/asl/parse/test_state/preprocessor.py +222 -31
- localstack/services/stepfunctions/asl/static_analyser/test_state/test_state_analyser.py +256 -22
- localstack/services/stepfunctions/backend/execution.py +10 -11
- localstack/services/stepfunctions/backend/execution_worker.py +5 -5
- localstack/services/stepfunctions/backend/test_state/execution.py +36 -0
- localstack/services/stepfunctions/backend/test_state/execution_worker.py +33 -1
- localstack/services/stepfunctions/backend/test_state/test_state_mock.py +127 -0
- localstack/services/stepfunctions/local_mocking/__init__.py +9 -0
- localstack/services/stepfunctions/{mocking → local_mocking}/mock_config.py +24 -17
- localstack/services/stepfunctions/provider.py +83 -25
- localstack/services/stepfunctions/test_state/mock_config.py +47 -0
- localstack/services/sts/provider.py +7 -0
- localstack/services/support/provider.py +5 -1
- localstack/services/swf/provider.py +5 -1
- localstack/services/transcribe/provider.py +7 -0
- localstack/testing/aws/lambda_utils.py +1 -1
- localstack/testing/aws/util.py +2 -1
- localstack/testing/config.py +1 -0
- localstack/testing/pytest/fixtures.py +28 -0
- localstack/testing/snapshots/transformer_utility.py +5 -0
- localstack/utils/analytics/publisher.py +37 -155
- localstack/utils/analytics/service_request_aggregator.py +6 -4
- localstack/utils/aws/arns.py +7 -0
- localstack/utils/aws/client_types.py +2 -4
- localstack/utils/batching.py +258 -0
- localstack/utils/bootstrap.py +2 -2
- localstack/utils/catalog/catalog.py +3 -2
- localstack/utils/collections.py +23 -11
- localstack/utils/container_utils/container_client.py +22 -13
- localstack/utils/container_utils/docker_cmd_client.py +6 -6
- localstack/version.py +2 -2
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/METADATA +7 -7
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/RECORD +155 -146
- localstack_core-4.12.1.dev18.dist-info/plux.json +1 -0
- localstack/services/stepfunctions/mocking/__init__.py +0 -0
- localstack/utils/batch_policy.py +0 -124
- localstack_core-4.10.1.dev42.dist-info/plux.json +0 -1
- /localstack/services/stepfunctions/{mocking → local_mocking}/mock_config_file.py +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/WHEEL +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import json
|
|
3
|
+
from typing import Final
|
|
4
|
+
|
|
5
|
+
from pydantic import (
|
|
6
|
+
ValidationError,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from localstack.aws.api.stepfunctions import (
|
|
10
|
+
HistoryEventType,
|
|
11
|
+
MockInput,
|
|
12
|
+
TaskFailedEventDetails,
|
|
13
|
+
TestStateConfiguration,
|
|
14
|
+
)
|
|
15
|
+
from localstack.services.stepfunctions.asl.component.common.error_name.error_name import ErrorName
|
|
16
|
+
from localstack.services.stepfunctions.asl.component.common.error_name.failure_event import (
|
|
17
|
+
FailureEvent,
|
|
18
|
+
FailureEventException,
|
|
19
|
+
)
|
|
20
|
+
from localstack.services.stepfunctions.asl.component.state.state_type import StateType
|
|
21
|
+
from localstack.services.stepfunctions.asl.eval.environment import Environment
|
|
22
|
+
from localstack.services.stepfunctions.asl.eval.event.event_detail import EventDetails
|
|
23
|
+
from localstack.services.stepfunctions.asl.eval.states import (
|
|
24
|
+
ContextObjectData,
|
|
25
|
+
)
|
|
26
|
+
from localstack.services.stepfunctions.test_state.mock_config import (
|
|
27
|
+
TestStateContextObjectValidator,
|
|
28
|
+
TestStateMockedResponse,
|
|
29
|
+
TestStateResponseReturn,
|
|
30
|
+
TestStateResponseThrow,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def eval_mocked_response_throw(env: Environment, mocked_response: TestStateResponseThrow) -> None:
|
|
35
|
+
task_failed_event_details = TaskFailedEventDetails(
|
|
36
|
+
error=mocked_response.error, cause=mocked_response.cause
|
|
37
|
+
)
|
|
38
|
+
error_name = ErrorName(mocked_response.error)
|
|
39
|
+
failure_event = FailureEvent(
|
|
40
|
+
env=env,
|
|
41
|
+
error_name=error_name,
|
|
42
|
+
event_type=HistoryEventType.TaskFailed, # TODO(gregfurman): Should this be state specific?
|
|
43
|
+
event_details=EventDetails(taskFailedEventDetails=task_failed_event_details),
|
|
44
|
+
)
|
|
45
|
+
raise FailureEventException(failure_event=failure_event)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestStateMock:
|
|
49
|
+
_mock_input: MockInput | None
|
|
50
|
+
_state_configuration: TestStateConfiguration | None
|
|
51
|
+
_result_stack: Final[list[TestStateMockedResponse]]
|
|
52
|
+
_context: Final[ContextObjectData | None]
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
mock_input: MockInput | None,
|
|
57
|
+
state_configuration: TestStateConfiguration | None,
|
|
58
|
+
context: str | None,
|
|
59
|
+
):
|
|
60
|
+
self._mock_input = mock_input
|
|
61
|
+
self._state_configuration = state_configuration
|
|
62
|
+
self._result_stack = []
|
|
63
|
+
self._context = None
|
|
64
|
+
|
|
65
|
+
if not mock_input:
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
self._context = None if context is None else self.parse_context(context)
|
|
69
|
+
|
|
70
|
+
if mock_result_raw := mock_input.get("result"):
|
|
71
|
+
mock = json.loads(mock_result_raw)
|
|
72
|
+
self._result_stack.append(TestStateResponseReturn(mock))
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
if mock_error_output := mock_input.get("errorOutput"):
|
|
76
|
+
mock = copy.deepcopy(mock_error_output)
|
|
77
|
+
self._result_stack.append(TestStateResponseThrow(**mock))
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
def is_mocked(self):
|
|
81
|
+
if self._mock_input or self._state_configuration:
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
def add_result(self, result: TestStateMockedResponse):
|
|
87
|
+
mock = copy.deepcopy(result)
|
|
88
|
+
self._result_stack.append(mock)
|
|
89
|
+
|
|
90
|
+
def get_next_result(self) -> TestStateMockedResponse:
|
|
91
|
+
if not self._result_stack:
|
|
92
|
+
return None
|
|
93
|
+
return self._result_stack.pop()
|
|
94
|
+
|
|
95
|
+
def get_context(self) -> ContextObjectData | None:
|
|
96
|
+
if self._context is not None:
|
|
97
|
+
return copy.deepcopy(self._context)
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def parse_context(context: str, state_type: StateType = None) -> ContextObjectData:
|
|
102
|
+
"""Parse and validate context JSON string."""
|
|
103
|
+
try:
|
|
104
|
+
validation_result = TestStateContextObjectValidator.model_validate_json(context)
|
|
105
|
+
return validation_result.model_dump(exclude_unset=True, exclude_none=True)
|
|
106
|
+
except ValidationError as e:
|
|
107
|
+
error = e.errors()[0]
|
|
108
|
+
path_str = ".".join(str(x) for x in error["loc"])
|
|
109
|
+
|
|
110
|
+
match error:
|
|
111
|
+
case {"type": "extra_forbidden", "loc": ("Map",)}:
|
|
112
|
+
raise ValueError("'Map' field is not supported when mocking a Context object")
|
|
113
|
+
|
|
114
|
+
case {"type": "extra_forbidden", "loc": (*_, forbidden_key)}:
|
|
115
|
+
raise ValueError(f"Field '{forbidden_key}' is not allowed")
|
|
116
|
+
|
|
117
|
+
case {"type": t} if t in ("string_type", "int_type", "dict_type", "model_type"):
|
|
118
|
+
expected_map = {
|
|
119
|
+
"string_type": "string",
|
|
120
|
+
"int_type": "integer",
|
|
121
|
+
"dict_type": "object",
|
|
122
|
+
"model_type": "object",
|
|
123
|
+
}
|
|
124
|
+
expected = expected_map.get(t, "valid type")
|
|
125
|
+
raise ValueError(f"{path_str} must be a {expected}")
|
|
126
|
+
case _:
|
|
127
|
+
raise ValueError(f"{error['msg']}")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
from typing import Any, Final
|
|
3
3
|
|
|
4
|
-
from localstack.services.stepfunctions.
|
|
4
|
+
from localstack.services.stepfunctions.local_mocking.mock_config_file import (
|
|
5
5
|
RawMockConfig,
|
|
6
6
|
RawResponseModel,
|
|
7
7
|
RawTestCase,
|
|
@@ -9,7 +9,7 @@ from localstack.services.stepfunctions.mocking.mock_config_file import (
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class LocalMockedResponse(abc.ABC):
|
|
13
13
|
range_start: Final[int]
|
|
14
14
|
range_end: Final[int]
|
|
15
15
|
|
|
@@ -28,7 +28,7 @@ class MockedResponse(abc.ABC):
|
|
|
28
28
|
self.range_end = range_end
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
class
|
|
31
|
+
class LocalMockedResponseReturn(LocalMockedResponse):
|
|
32
32
|
payload: Final[Any]
|
|
33
33
|
|
|
34
34
|
def __init__(self, range_start: int, range_end: int, payload: Any):
|
|
@@ -36,7 +36,7 @@ class MockedResponseReturn(MockedResponse):
|
|
|
36
36
|
self.payload = payload
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
class
|
|
39
|
+
class LocalMockedResponseThrow(LocalMockedResponse):
|
|
40
40
|
error: Final[str]
|
|
41
41
|
cause: Final[str]
|
|
42
42
|
|
|
@@ -49,10 +49,13 @@ class MockedResponseThrow(MockedResponse):
|
|
|
49
49
|
class StateMockedResponses:
|
|
50
50
|
state_name: Final[str]
|
|
51
51
|
mocked_response_name: Final[str]
|
|
52
|
-
mocked_responses: Final[list[
|
|
52
|
+
mocked_responses: Final[list[LocalMockedResponse]]
|
|
53
53
|
|
|
54
54
|
def __init__(
|
|
55
|
-
self,
|
|
55
|
+
self,
|
|
56
|
+
state_name: str,
|
|
57
|
+
mocked_response_name: str,
|
|
58
|
+
mocked_responses: list[LocalMockedResponse],
|
|
56
59
|
):
|
|
57
60
|
self.state_name = state_name
|
|
58
61
|
self.mocked_response_name = mocked_response_name
|
|
@@ -74,7 +77,7 @@ class StateMockedResponses:
|
|
|
74
77
|
last_range_end = mocked_response.range_end
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
class
|
|
80
|
+
class LocalMockTestCase:
|
|
78
81
|
state_machine_name: Final[str]
|
|
79
82
|
test_case_name: Final[str]
|
|
80
83
|
state_mocked_responses: Final[dict[str, StateMockedResponses]]
|
|
@@ -127,13 +130,15 @@ def _parse_mocked_response_range(string_definition: str) -> tuple[int, int]:
|
|
|
127
130
|
|
|
128
131
|
def _mocked_response_from_raw(
|
|
129
132
|
raw_response_model_range: str, raw_response_model: RawResponseModel
|
|
130
|
-
) ->
|
|
133
|
+
) -> LocalMockedResponse:
|
|
131
134
|
range_start, range_end = _parse_mocked_response_range(raw_response_model_range)
|
|
132
135
|
if raw_response_model.Return:
|
|
133
136
|
payload = raw_response_model.Return.model_dump()
|
|
134
|
-
return
|
|
137
|
+
return LocalMockedResponseReturn(
|
|
138
|
+
range_start=range_start, range_end=range_end, payload=payload
|
|
139
|
+
)
|
|
135
140
|
throw_definition = raw_response_model.Throw
|
|
136
|
-
return
|
|
141
|
+
return LocalMockedResponseThrow(
|
|
137
142
|
range_start=range_start,
|
|
138
143
|
range_end=range_end,
|
|
139
144
|
error=throw_definition.Error,
|
|
@@ -143,7 +148,7 @@ def _mocked_response_from_raw(
|
|
|
143
148
|
|
|
144
149
|
def _mocked_responses_from_raw(
|
|
145
150
|
mocked_response_name: str, raw_mock_config: RawMockConfig
|
|
146
|
-
) -> list[
|
|
151
|
+
) -> list[LocalMockedResponse]:
|
|
147
152
|
raw_response_models: dict[str, RawResponseModel] | None = raw_mock_config.MockedResponses.get(
|
|
148
153
|
mocked_response_name
|
|
149
154
|
)
|
|
@@ -151,9 +156,9 @@ def _mocked_responses_from_raw(
|
|
|
151
156
|
raise RuntimeError(
|
|
152
157
|
f"No definitions for mocked response '{mocked_response_name}' in the mock configuration file."
|
|
153
158
|
)
|
|
154
|
-
mocked_responses: list[
|
|
159
|
+
mocked_responses: list[LocalMockedResponse] = []
|
|
155
160
|
for raw_response_model_range, raw_response_model in raw_response_models.items():
|
|
156
|
-
mocked_response:
|
|
161
|
+
mocked_response: LocalMockedResponse = _mocked_response_from_raw(
|
|
157
162
|
raw_response_model_range=raw_response_model_range, raw_response_model=raw_response_model
|
|
158
163
|
)
|
|
159
164
|
mocked_responses.append(mocked_response)
|
|
@@ -175,7 +180,7 @@ def _state_mocked_responses_from_raw(
|
|
|
175
180
|
|
|
176
181
|
def _mock_test_case_from_raw(
|
|
177
182
|
state_machine_name: str, test_case_name: str, raw_mock_config: RawMockConfig
|
|
178
|
-
) ->
|
|
183
|
+
) -> LocalMockTestCase:
|
|
179
184
|
state_machine = raw_mock_config.StateMachines.get(state_machine_name)
|
|
180
185
|
if not state_machine:
|
|
181
186
|
raise RuntimeError(
|
|
@@ -195,18 +200,20 @@ def _mock_test_case_from_raw(
|
|
|
195
200
|
raw_mock_config=raw_mock_config,
|
|
196
201
|
)
|
|
197
202
|
state_mocked_responses_list.append(state_mocked_responses)
|
|
198
|
-
return
|
|
203
|
+
return LocalMockTestCase(
|
|
199
204
|
state_machine_name=state_machine_name,
|
|
200
205
|
test_case_name=test_case_name,
|
|
201
206
|
state_mocked_responses_list=state_mocked_responses_list,
|
|
202
207
|
)
|
|
203
208
|
|
|
204
209
|
|
|
205
|
-
def
|
|
210
|
+
def load_local_mock_test_case_for(
|
|
211
|
+
state_machine_name: str, test_case_name: str
|
|
212
|
+
) -> LocalMockTestCase | None:
|
|
206
213
|
raw_mock_config: RawMockConfig | None = _load_sfn_raw_mock_config()
|
|
207
214
|
if raw_mock_config is None:
|
|
208
215
|
return None
|
|
209
|
-
mock_test_case:
|
|
216
|
+
mock_test_case: LocalMockTestCase = _mock_test_case_from_raw(
|
|
210
217
|
state_machine_name=state_machine_name,
|
|
211
218
|
test_case_name=test_case_name,
|
|
212
219
|
raw_mock_config=raw_mock_config,
|
|
@@ -63,7 +63,6 @@ from localstack.aws.api.stepfunctions import (
|
|
|
63
63
|
Publish,
|
|
64
64
|
PublishStateMachineVersionOutput,
|
|
65
65
|
ResourceNotFound,
|
|
66
|
-
RevealSecrets,
|
|
67
66
|
ReverseOrder,
|
|
68
67
|
RevisionId,
|
|
69
68
|
RoutingConfigurationList,
|
|
@@ -89,6 +88,7 @@ from localstack.aws.api.stepfunctions import (
|
|
|
89
88
|
TaskDoesNotExist,
|
|
90
89
|
TaskTimedOut,
|
|
91
90
|
TaskToken,
|
|
91
|
+
TestStateInput,
|
|
92
92
|
TestStateOutput,
|
|
93
93
|
ToleratedFailureCount,
|
|
94
94
|
ToleratedFailurePercentage,
|
|
@@ -150,9 +150,10 @@ from localstack.services.stepfunctions.backend.store import SFNStore, sfn_stores
|
|
|
150
150
|
from localstack.services.stepfunctions.backend.test_state.execution import (
|
|
151
151
|
TestStateExecution,
|
|
152
152
|
)
|
|
153
|
-
from localstack.services.stepfunctions.
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
from localstack.services.stepfunctions.backend.test_state.test_state_mock import TestStateMock
|
|
154
|
+
from localstack.services.stepfunctions.local_mocking.mock_config import (
|
|
155
|
+
LocalMockTestCase,
|
|
156
|
+
load_local_mock_test_case_for,
|
|
156
157
|
)
|
|
157
158
|
from localstack.services.stepfunctions.stepfunctions_utils import (
|
|
158
159
|
assert_pagination_parameters_valid,
|
|
@@ -160,6 +161,7 @@ from localstack.services.stepfunctions.stepfunctions_utils import (
|
|
|
160
161
|
normalise_max_results,
|
|
161
162
|
)
|
|
162
163
|
from localstack.state import StateVisitor
|
|
164
|
+
from localstack.utils.aws import arns
|
|
163
165
|
from localstack.utils.aws.arns import (
|
|
164
166
|
ARN_PARTITION_REGEX,
|
|
165
167
|
stepfunctions_activity_arn,
|
|
@@ -772,14 +774,16 @@ class StepFunctionsProvider(StepfunctionsApi, ServiceLifecycleHook):
|
|
|
772
774
|
return state_machine_arn.split("#")[0]
|
|
773
775
|
|
|
774
776
|
@staticmethod
|
|
775
|
-
def
|
|
777
|
+
def _get_local_mock_test_case(
|
|
778
|
+
state_machine_arn: str, state_machine_name: str
|
|
779
|
+
) -> LocalMockTestCase | None:
|
|
776
780
|
"""Extract and load a mock test case from a state machine ARN if present."""
|
|
777
781
|
parts = state_machine_arn.split("#")
|
|
778
782
|
if len(parts) != 2:
|
|
779
783
|
return None
|
|
780
784
|
|
|
781
785
|
mock_test_case_name = parts[1]
|
|
782
|
-
mock_test_case =
|
|
786
|
+
mock_test_case = load_local_mock_test_case_for(
|
|
783
787
|
state_machine_name=state_machine_name, test_case_name=mock_test_case_name
|
|
784
788
|
)
|
|
785
789
|
if mock_test_case is None:
|
|
@@ -856,7 +860,9 @@ class StepFunctionsProvider(StepfunctionsApi, ServiceLifecycleHook):
|
|
|
856
860
|
configuration=state_machine_clone.cloud_watch_logging_configuration,
|
|
857
861
|
)
|
|
858
862
|
|
|
859
|
-
|
|
863
|
+
local_mock_test_case = self._get_local_mock_test_case(
|
|
864
|
+
state_machine_arn, state_machine_clone.name
|
|
865
|
+
)
|
|
860
866
|
|
|
861
867
|
execution = Execution(
|
|
862
868
|
name=exec_name,
|
|
@@ -872,7 +878,7 @@ class StepFunctionsProvider(StepfunctionsApi, ServiceLifecycleHook):
|
|
|
872
878
|
input_data=input_data,
|
|
873
879
|
trace_header=trace_header,
|
|
874
880
|
activity_store=self.get_store(context).activities,
|
|
875
|
-
|
|
881
|
+
local_mock_test_case=local_mock_test_case,
|
|
876
882
|
)
|
|
877
883
|
|
|
878
884
|
store.executions[exec_arn] = execution
|
|
@@ -932,7 +938,9 @@ class StepFunctionsProvider(StepfunctionsApi, ServiceLifecycleHook):
|
|
|
932
938
|
configuration=state_machine_clone.cloud_watch_logging_configuration,
|
|
933
939
|
)
|
|
934
940
|
|
|
935
|
-
|
|
941
|
+
local_mock_test_case = self._get_local_mock_test_case(
|
|
942
|
+
state_machine_arn, state_machine_clone.name
|
|
943
|
+
)
|
|
936
944
|
|
|
937
945
|
execution = SyncExecution(
|
|
938
946
|
name=exec_name,
|
|
@@ -947,7 +955,7 @@ class StepFunctionsProvider(StepfunctionsApi, ServiceLifecycleHook):
|
|
|
947
955
|
input_data=input_data,
|
|
948
956
|
trace_header=trace_header,
|
|
949
957
|
activity_store=self.get_store(context).activities,
|
|
950
|
-
|
|
958
|
+
local_mock_test_case=local_mock_test_case,
|
|
951
959
|
)
|
|
952
960
|
self.get_store(context).executions[exec_arn] = execution
|
|
953
961
|
|
|
@@ -1483,35 +1491,83 @@ class StepFunctionsProvider(StepfunctionsApi, ServiceLifecycleHook):
|
|
|
1483
1491
|
raise ResourceNotFound()
|
|
1484
1492
|
|
|
1485
1493
|
def test_state(
|
|
1486
|
-
self,
|
|
1487
|
-
context: RequestContext,
|
|
1488
|
-
definition: Definition,
|
|
1489
|
-
role_arn: Arn = None,
|
|
1490
|
-
input: SensitiveData = None,
|
|
1491
|
-
inspection_level: InspectionLevel = None,
|
|
1492
|
-
reveal_secrets: RevealSecrets = None,
|
|
1493
|
-
variables: SensitiveData = None,
|
|
1494
|
-
**kwargs,
|
|
1494
|
+
self, context: RequestContext, request: TestStateInput, **kwargs
|
|
1495
1495
|
) -> TestStateOutput:
|
|
1496
|
+
state_name = request.get("stateName")
|
|
1497
|
+
definition = request["definition"]
|
|
1498
|
+
|
|
1496
1499
|
StepFunctionsProvider._validate_definition(
|
|
1497
|
-
definition=definition,
|
|
1500
|
+
definition=definition,
|
|
1501
|
+
static_analysers=[TestStateStaticAnalyser(state_name)],
|
|
1498
1502
|
)
|
|
1499
1503
|
|
|
1504
|
+
# if StateName is present, we need to ensure the state being referenced exists in full definition.
|
|
1505
|
+
if state_name and not TestStateStaticAnalyser.is_state_in_definition(
|
|
1506
|
+
definition=definition, state_name=state_name
|
|
1507
|
+
):
|
|
1508
|
+
raise ValidationException("State not found in definition")
|
|
1509
|
+
|
|
1510
|
+
mock_input = request.get("mock")
|
|
1511
|
+
TestStateStaticAnalyser.validate_mock(test_state_input=request)
|
|
1512
|
+
|
|
1513
|
+
if state_configuration := request.get("stateConfiguration"):
|
|
1514
|
+
# TODO: Add validations for this i.e assert len(input) <= failureCount
|
|
1515
|
+
pass
|
|
1516
|
+
|
|
1517
|
+
if state_context := request.get("context"):
|
|
1518
|
+
# TODO: Add validation ensuring only present if 'mock' is specified
|
|
1519
|
+
# An error occurred (ValidationException) when calling the TestState operation: State type 'Pass' is not supported when a mock is specified
|
|
1520
|
+
pass
|
|
1521
|
+
|
|
1522
|
+
try:
|
|
1523
|
+
state_mock = TestStateMock(
|
|
1524
|
+
mock_input=mock_input,
|
|
1525
|
+
state_configuration=state_configuration,
|
|
1526
|
+
context=state_context,
|
|
1527
|
+
)
|
|
1528
|
+
except ValueError as e:
|
|
1529
|
+
LOG.error(e)
|
|
1530
|
+
raise ValidationException(f"Invalid Context object provided: {e}")
|
|
1531
|
+
|
|
1500
1532
|
name: Name | None = f"TestState-{short_uid()}"
|
|
1501
1533
|
arn = stepfunctions_state_machine_arn(
|
|
1502
1534
|
name=name, account_id=context.account_id, region_name=context.region
|
|
1503
1535
|
)
|
|
1536
|
+
role_arn = request.get("roleArn")
|
|
1537
|
+
if role_arn is None:
|
|
1538
|
+
TestStateStaticAnalyser.validate_role_arn_required(
|
|
1539
|
+
mock_input=mock_input, definition=definition, state_name=state_name
|
|
1540
|
+
)
|
|
1541
|
+
# HACK: Added dummy role ARN because it is a required field in Execution.
|
|
1542
|
+
# To allow optional roleArn for the test state but preserve the mandatory one for regular executions
|
|
1543
|
+
# we likely need to remove inheritance TestStateExecution(Execution) in favor of composition.
|
|
1544
|
+
# TestState execution starts to have too many simplifications compared to a regular execution
|
|
1545
|
+
# which renders the inheritance mechanism harmful.
|
|
1546
|
+
# TODO make role_arn optional in TestStateExecution
|
|
1547
|
+
role_arn = arns.iam_role_arn(
|
|
1548
|
+
role_name=f"RoleFor-{name}",
|
|
1549
|
+
account_id=context.account_id,
|
|
1550
|
+
region_name=context.region,
|
|
1551
|
+
)
|
|
1552
|
+
|
|
1504
1553
|
state_machine = TestStateMachine(
|
|
1505
1554
|
name=name,
|
|
1506
1555
|
arn=arn,
|
|
1507
1556
|
role_arn=role_arn,
|
|
1508
|
-
definition=definition,
|
|
1557
|
+
definition=request["definition"],
|
|
1509
1558
|
)
|
|
1510
|
-
exec_arn = stepfunctions_standard_execution_arn(state_machine.arn, name)
|
|
1511
1559
|
|
|
1512
|
-
|
|
1560
|
+
# HACK(gregfurman): The ARN that gets generated has a duplicate 'name' field in the
|
|
1561
|
+
# resource ARN. Just replace this duplication and extract the execution ID.
|
|
1562
|
+
exec_arn = stepfunctions_express_execution_arn(state_machine.arn, name)
|
|
1563
|
+
exec_arn = exec_arn.replace(f":{name}:{name}:", f":{name}:", 1)
|
|
1564
|
+
_, exec_name = exec_arn.rsplit(":", 1)
|
|
1565
|
+
|
|
1566
|
+
if input_json := request.get("input", {}):
|
|
1567
|
+
input_json = json.loads(input_json)
|
|
1568
|
+
|
|
1513
1569
|
execution = TestStateExecution(
|
|
1514
|
-
name=
|
|
1570
|
+
name=exec_name,
|
|
1515
1571
|
role_arn=role_arn,
|
|
1516
1572
|
exec_arn=exec_arn,
|
|
1517
1573
|
account_id=context.account_id,
|
|
@@ -1519,12 +1575,14 @@ class StepFunctionsProvider(StepfunctionsApi, ServiceLifecycleHook):
|
|
|
1519
1575
|
state_machine=state_machine,
|
|
1520
1576
|
start_date=datetime.datetime.now(tz=datetime.UTC),
|
|
1521
1577
|
input_data=input_json,
|
|
1578
|
+
state_name=state_name,
|
|
1522
1579
|
activity_store=self.get_store(context).activities,
|
|
1580
|
+
mock=state_mock,
|
|
1523
1581
|
)
|
|
1524
1582
|
execution.start()
|
|
1525
1583
|
|
|
1526
1584
|
test_state_output = execution.to_test_state_output(
|
|
1527
|
-
inspection_level=
|
|
1585
|
+
inspection_level=request.get("inspectionLevel", InspectionLevel.INFO)
|
|
1528
1586
|
)
|
|
1529
1587
|
|
|
1530
1588
|
return test_state_output
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
from typing import Any, Final
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr, create_model
|
|
5
|
+
|
|
6
|
+
from localstack.services.stepfunctions.asl.eval.states import (
|
|
7
|
+
ExecutionData,
|
|
8
|
+
StateData,
|
|
9
|
+
StateMachineData,
|
|
10
|
+
TaskData,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestStateMockedResponse(abc.ABC):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestStateResponseReturn(TestStateMockedResponse):
|
|
19
|
+
payload: Final[Any]
|
|
20
|
+
|
|
21
|
+
def __init__(self, payload: Any):
|
|
22
|
+
self.payload = payload
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestStateResponseThrow(TestStateMockedResponse):
|
|
26
|
+
error: Final[str]
|
|
27
|
+
cause: Final[str]
|
|
28
|
+
|
|
29
|
+
def __init__(self, error: str, cause: str):
|
|
30
|
+
self.error = error
|
|
31
|
+
self.cause = cause
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _to_strict_model(name: str, source: type):
|
|
35
|
+
type_map = {str: StrictStr, int: StrictInt}
|
|
36
|
+
fields = {k: (type_map.get(v, v) | None, None) for k, v in source.__annotations__.items()}
|
|
37
|
+
return create_model(name, __config__=ConfigDict(extra="forbid"), **fields)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
TestStateContextObjectValidator: Final[type[BaseModel]] = create_model(
|
|
41
|
+
"ContextValidator",
|
|
42
|
+
__config__=ConfigDict(extra="forbid"),
|
|
43
|
+
Execution=(_to_strict_model("Execution", ExecutionData) | None, None),
|
|
44
|
+
State=(_to_strict_model("State", StateData) | None, None),
|
|
45
|
+
StateMachine=(_to_strict_model("StateMachine", StateMachineData) | None, None),
|
|
46
|
+
Task=(_to_strict_model("Task", TaskData) | None, None),
|
|
47
|
+
)
|
|
@@ -23,6 +23,7 @@ from localstack.services.iam.iam_patches import apply_iam_patches
|
|
|
23
23
|
from localstack.services.moto import call_moto
|
|
24
24
|
from localstack.services.plugins import ServiceLifecycleHook
|
|
25
25
|
from localstack.services.sts.models import SessionConfig, sts_stores
|
|
26
|
+
from localstack.state import StateVisitor
|
|
26
27
|
from localstack.utils.aws.arns import extract_account_id_from_arn
|
|
27
28
|
from localstack.utils.aws.request_context import extract_access_key_id_from_auth_header
|
|
28
29
|
|
|
@@ -50,6 +51,12 @@ class StsProvider(StsApi, ServiceLifecycleHook):
|
|
|
50
51
|
def __init__(self):
|
|
51
52
|
apply_iam_patches()
|
|
52
53
|
|
|
54
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
55
|
+
from moto.sts.models import sts_backends
|
|
56
|
+
|
|
57
|
+
visitor.visit(sts_backends)
|
|
58
|
+
visitor.visit(sts_stores)
|
|
59
|
+
|
|
53
60
|
def get_caller_identity(self, context: RequestContext, **kwargs) -> GetCallerIdentityResponse:
|
|
54
61
|
response = call_moto(context)
|
|
55
62
|
if "user/moto" in response["Arn"] and "sts" in response["Arn"]:
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
2
|
|
|
3
3
|
from localstack.aws.api.support import SupportApi
|
|
4
|
+
from localstack.state import StateVisitor
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class SupportProvider(SupportApi, ABC):
|
|
7
|
-
|
|
8
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
9
|
+
from moto.support.models import support_backends
|
|
10
|
+
|
|
11
|
+
visitor.visit(support_backends)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
2
|
|
|
3
3
|
from localstack.aws.api.swf import SwfApi
|
|
4
|
+
from localstack.state import StateVisitor
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class SWFProvider(SwfApi, ABC):
|
|
7
|
-
|
|
8
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
9
|
+
from moto.swf.models import swf_backends
|
|
10
|
+
|
|
11
|
+
visitor.visit(swf_backends)
|
|
@@ -38,6 +38,7 @@ from localstack.services.s3.utils import (
|
|
|
38
38
|
)
|
|
39
39
|
from localstack.services.transcribe.models import TranscribeStore, transcribe_stores
|
|
40
40
|
from localstack.services.transcribe.packages import vosk_package
|
|
41
|
+
from localstack.state import StateVisitor
|
|
41
42
|
from localstack.utils.files import new_tmp_file
|
|
42
43
|
from localstack.utils.http import download
|
|
43
44
|
from localstack.utils.run import run
|
|
@@ -101,6 +102,12 @@ _DL_LOCK = threading.Lock()
|
|
|
101
102
|
|
|
102
103
|
|
|
103
104
|
class TranscribeProvider(TranscribeApi):
|
|
105
|
+
def accept_state_visitor(self, visitor: StateVisitor) -> None:
|
|
106
|
+
from moto.transcribe.models import transcribe_backends
|
|
107
|
+
|
|
108
|
+
visitor.visit(transcribe_backends)
|
|
109
|
+
visitor.visit(transcribe_stores)
|
|
110
|
+
|
|
104
111
|
def get_transcription_job(
|
|
105
112
|
self, context: RequestContext, transcription_job_name: TranscriptionJobName, **kwargs: Any
|
|
106
113
|
) -> GetTranscriptionJobResponse:
|
|
@@ -261,7 +261,7 @@ def concurrency_update_done(client, function_name, qualifier):
|
|
|
261
261
|
|
|
262
262
|
def get_invoke_init_type(
|
|
263
263
|
client, function_name, qualifier
|
|
264
|
-
) -> Literal["on-demand", "provisioned-concurrency"]:
|
|
264
|
+
) -> Literal["on-demand", "provisioned-concurrency", "lambda-managed-instances"]:
|
|
265
265
|
"""check the environment in the lambda for AWS_LAMBDA_INITIALIZATION_TYPE indicating ondemand/provisioned"""
|
|
266
266
|
invoke_result = client.invoke(FunctionName=function_name, Qualifier=qualifier)
|
|
267
267
|
return json.load(invoke_result["Payload"])
|
localstack/testing/aws/util.py
CHANGED
|
@@ -29,6 +29,7 @@ from localstack.testing.config import (
|
|
|
29
29
|
SECONDARY_TEST_AWS_SECRET_ACCESS_KEY,
|
|
30
30
|
SECONDARY_TEST_AWS_SESSION_TOKEN,
|
|
31
31
|
TEST_AWS_ACCESS_KEY_ID,
|
|
32
|
+
TEST_AWS_ENDPOINT_URL,
|
|
32
33
|
TEST_AWS_REGION_NAME,
|
|
33
34
|
TEST_AWS_SECRET_ACCESS_KEY,
|
|
34
35
|
)
|
|
@@ -240,7 +241,7 @@ def base_aws_client_factory(session: boto3.Session) -> ClientFactory:
|
|
|
240
241
|
|
|
241
242
|
# Prevent this fixture from using the region configured in system config
|
|
242
243
|
config = config.merge(botocore.config.Config(region_name=TEST_AWS_REGION_NAME))
|
|
243
|
-
return ExternalClientFactory(session=session, config=config)
|
|
244
|
+
return ExternalClientFactory(session=session, config=config, endpoint=TEST_AWS_ENDPOINT_URL)
|
|
244
245
|
|
|
245
246
|
|
|
246
247
|
def base_testing_aws_client(client_factory: ClientFactory) -> ServiceLevelClientFactory:
|
localstack/testing/config.py
CHANGED
|
@@ -9,6 +9,7 @@ TEST_AWS_ACCOUNT_ID = os.getenv("TEST_AWS_ACCOUNT_ID") or DEFAULT_AWS_ACCOUNT_ID
|
|
|
9
9
|
TEST_AWS_ACCESS_KEY_ID = os.getenv("TEST_AWS_ACCESS_KEY_ID") or "test"
|
|
10
10
|
TEST_AWS_SECRET_ACCESS_KEY = os.getenv("TEST_AWS_SECRET_ACCESS_KEY") or "test"
|
|
11
11
|
TEST_AWS_REGION_NAME = os.getenv("TEST_AWS_REGION_NAME") or "us-east-1"
|
|
12
|
+
TEST_AWS_ENDPOINT_URL = os.getenv("TEST_AWS_ENDPOINT_URL")
|
|
12
13
|
|
|
13
14
|
# Secondary test AWS profile - only used for testing against AWS
|
|
14
15
|
SECONDARY_TEST_AWS_PROFILE = os.getenv("SECONDARY_TEST_AWS_PROFILE")
|
|
@@ -1426,6 +1426,34 @@ def create_lambda_function(aws_client, wait_until_lambda_ready, lambda_su_role):
|
|
|
1426
1426
|
LOG.debug("Unable to delete log group %s in cleanup", log_group_name)
|
|
1427
1427
|
|
|
1428
1428
|
|
|
1429
|
+
@pytest.fixture
|
|
1430
|
+
def lambda_is_function_deleted(aws_client):
|
|
1431
|
+
"""Example usage:
|
|
1432
|
+
wait_until(lambda_is_function_deleted(function_name))
|
|
1433
|
+
wait_until(lambda_is_function_deleted(function_name, Qualifier="my-alias"))
|
|
1434
|
+
|
|
1435
|
+
function_name can be a function name, function ARN, or partial function ARN.
|
|
1436
|
+
"""
|
|
1437
|
+
return _lambda_is_function_deleted(aws_client.lambda_)
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
def _lambda_is_function_deleted(lambda_client):
|
|
1441
|
+
def _is_function_deleted(
|
|
1442
|
+
function_name: str,
|
|
1443
|
+
**kwargs,
|
|
1444
|
+
) -> Callable[[], bool]:
|
|
1445
|
+
def _inner() -> bool:
|
|
1446
|
+
try:
|
|
1447
|
+
lambda_client.get_function(FunctionName=function_name, **kwargs)
|
|
1448
|
+
return False
|
|
1449
|
+
except lambda_client.exceptions.ResourceNotFoundException:
|
|
1450
|
+
return True
|
|
1451
|
+
|
|
1452
|
+
return _inner
|
|
1453
|
+
|
|
1454
|
+
return _is_function_deleted
|
|
1455
|
+
|
|
1456
|
+
|
|
1429
1457
|
@pytest.fixture
|
|
1430
1458
|
def create_echo_http_server(aws_client, create_lambda_function):
|
|
1431
1459
|
from localstack.aws.api.lambda_ import Runtime
|
|
@@ -791,6 +791,11 @@ class TransformerUtility:
|
|
|
791
791
|
"x-amzn-RequestId",
|
|
792
792
|
replace_reference=False,
|
|
793
793
|
),
|
|
794
|
+
JsonpathTransformer(
|
|
795
|
+
"$..x-amzn-requestid",
|
|
796
|
+
"x-amzn-requestid",
|
|
797
|
+
replace_reference=False,
|
|
798
|
+
),
|
|
794
799
|
KeyValueBasedTransformer(_transform_stepfunctions_cause_details, "json-input"),
|
|
795
800
|
]
|
|
796
801
|
|