localstack-core 4.10.1.dev42__py3-none-any.whl → 4.11.2.dev14__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.
Files changed (116) hide show
  1. localstack/aws/api/apigateway/__init__.py +42 -0
  2. localstack/aws/api/cloudformation/__init__.py +161 -0
  3. localstack/aws/api/ec2/__init__.py +1165 -12
  4. localstack/aws/api/iam/__init__.py +227 -0
  5. localstack/aws/api/kms/__init__.py +1 -0
  6. localstack/aws/api/lambda_/__init__.py +418 -66
  7. localstack/aws/api/logs/__init__.py +312 -0
  8. localstack/aws/api/opensearch/__init__.py +89 -0
  9. localstack/aws/api/redshift/__init__.py +69 -0
  10. localstack/aws/api/resourcegroupstaggingapi/__init__.py +36 -0
  11. localstack/aws/api/route53/__init__.py +42 -0
  12. localstack/aws/api/route53resolver/__init__.py +1 -0
  13. localstack/aws/api/s3/__init__.py +62 -0
  14. localstack/aws/api/secretsmanager/__init__.py +28 -23
  15. localstack/aws/api/stepfunctions/__init__.py +52 -10
  16. localstack/aws/api/sts/__init__.py +52 -0
  17. localstack/aws/handlers/logging.py +8 -4
  18. localstack/aws/handlers/service.py +11 -2
  19. localstack/aws/protocol/serializer.py +1 -1
  20. localstack/deprecations.py +0 -6
  21. localstack/services/acm/provider.py +4 -0
  22. localstack/services/apigateway/legacy/provider.py +28 -15
  23. localstack/services/cloudformation/engine/template_preparer.py +6 -2
  24. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +12 -0
  25. localstack/services/cloudwatch/provider.py +10 -3
  26. localstack/services/cloudwatch/provider_v2.py +6 -3
  27. localstack/services/configservice/provider.py +5 -1
  28. localstack/services/dynamodb/provider.py +1 -0
  29. localstack/services/dynamodb/v2/provider.py +1 -0
  30. localstack/services/dynamodbstreams/provider.py +6 -0
  31. localstack/services/dynamodbstreams/v2/provider.py +6 -0
  32. localstack/services/ec2/provider.py +6 -0
  33. localstack/services/es/provider.py +6 -0
  34. localstack/services/events/provider.py +4 -0
  35. localstack/services/events/v1/provider.py +9 -0
  36. localstack/services/firehose/provider.py +5 -0
  37. localstack/services/iam/provider.py +4 -0
  38. localstack/services/kms/models.py +10 -20
  39. localstack/services/kms/provider.py +4 -0
  40. localstack/services/lambda_/api_utils.py +37 -20
  41. localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
  42. localstack/services/lambda_/invocation/assignment.py +4 -1
  43. localstack/services/lambda_/invocation/execution_environment.py +21 -2
  44. localstack/services/lambda_/invocation/lambda_models.py +27 -2
  45. localstack/services/lambda_/invocation/lambda_service.py +51 -3
  46. localstack/services/lambda_/invocation/models.py +9 -1
  47. localstack/services/lambda_/invocation/version_manager.py +18 -3
  48. localstack/services/lambda_/provider.py +239 -95
  49. localstack/services/lambda_/resource_providers/aws_lambda_function.py +33 -1
  50. localstack/services/lambda_/runtimes.py +3 -1
  51. localstack/services/logs/provider.py +9 -0
  52. localstack/services/opensearch/provider.py +53 -3
  53. localstack/services/resource_groups/provider.py +5 -1
  54. localstack/services/resourcegroupstaggingapi/provider.py +6 -1
  55. localstack/services/s3/provider.py +28 -15
  56. localstack/services/s3/utils.py +35 -14
  57. localstack/services/s3control/provider.py +101 -2
  58. localstack/services/s3control/validation.py +50 -0
  59. localstack/services/sns/constants.py +3 -1
  60. localstack/services/sns/publisher.py +15 -6
  61. localstack/services/sns/v2/models.py +6 -0
  62. localstack/services/sns/v2/provider.py +650 -19
  63. localstack/services/sns/v2/utils.py +12 -0
  64. localstack/services/stepfunctions/asl/component/common/path/result_path.py +1 -1
  65. localstack/services/stepfunctions/asl/component/state/state_execution/execute_state.py +0 -1
  66. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +0 -1
  67. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/lambda_eval_utils.py +8 -8
  68. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/{mock_eval_utils.py → local_mock_eval_utils.py} +13 -9
  69. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service.py +6 -6
  70. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_callback.py +1 -1
  71. localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +4 -0
  72. localstack/services/stepfunctions/asl/component/test_state/state/base_mock.py +118 -0
  73. localstack/services/stepfunctions/asl/component/test_state/state/common.py +82 -0
  74. localstack/services/stepfunctions/asl/component/test_state/state/execution.py +139 -0
  75. localstack/services/stepfunctions/asl/component/test_state/state/map.py +77 -0
  76. localstack/services/stepfunctions/asl/component/test_state/state/task.py +44 -0
  77. localstack/services/stepfunctions/asl/eval/environment.py +30 -22
  78. localstack/services/stepfunctions/asl/eval/states.py +1 -1
  79. localstack/services/stepfunctions/asl/eval/test_state/environment.py +49 -9
  80. localstack/services/stepfunctions/asl/eval/test_state/program_state.py +22 -0
  81. localstack/services/stepfunctions/asl/jsonata/jsonata.py +5 -1
  82. localstack/services/stepfunctions/asl/parse/preprocessor.py +67 -24
  83. localstack/services/stepfunctions/asl/parse/test_state/asl_parser.py +5 -4
  84. localstack/services/stepfunctions/asl/parse/test_state/preprocessor.py +222 -31
  85. localstack/services/stepfunctions/asl/static_analyser/test_state/test_state_analyser.py +170 -22
  86. localstack/services/stepfunctions/backend/execution.py +6 -6
  87. localstack/services/stepfunctions/backend/execution_worker.py +5 -5
  88. localstack/services/stepfunctions/backend/test_state/execution.py +36 -0
  89. localstack/services/stepfunctions/backend/test_state/execution_worker.py +33 -1
  90. localstack/services/stepfunctions/backend/test_state/test_state_mock.py +127 -0
  91. localstack/services/stepfunctions/local_mocking/__init__.py +9 -0
  92. localstack/services/stepfunctions/{mocking → local_mocking}/mock_config.py +24 -17
  93. localstack/services/stepfunctions/provider.py +78 -27
  94. localstack/services/stepfunctions/test_state/mock_config.py +47 -0
  95. localstack/testing/pytest/fixtures.py +28 -0
  96. localstack/testing/snapshots/transformer_utility.py +5 -0
  97. localstack/utils/analytics/publisher.py +37 -155
  98. localstack/utils/analytics/service_request_aggregator.py +6 -4
  99. localstack/utils/aws/arns.py +7 -0
  100. localstack/utils/batching.py +258 -0
  101. localstack/utils/collections.py +23 -11
  102. localstack/version.py +2 -2
  103. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/METADATA +5 -5
  104. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/RECORD +113 -105
  105. localstack_core-4.11.2.dev14.dist-info/plux.json +1 -0
  106. localstack/services/stepfunctions/mocking/__init__.py +0 -0
  107. localstack/utils/batch_policy.py +0 -124
  108. localstack_core-4.10.1.dev42.dist-info/plux.json +0 -1
  109. /localstack/services/stepfunctions/{mocking → local_mocking}/mock_config_file.py +0 -0
  110. {localstack_core-4.10.1.dev42.data → localstack_core-4.11.2.dev14.data}/scripts/localstack +0 -0
  111. {localstack_core-4.10.1.dev42.data → localstack_core-4.11.2.dev14.data}/scripts/localstack-supervisor +0 -0
  112. {localstack_core-4.10.1.dev42.data → localstack_core-4.11.2.dev14.data}/scripts/localstack.bat +0 -0
  113. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/WHEEL +0 -0
  114. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/entry_points.txt +0 -0
  115. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/licenses/LICENSE.txt +0 -0
  116. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@ from botocore.utils import InvalidArnException
6
6
 
7
7
  from localstack.aws.api.sns import InvalidParameterException
8
8
  from localstack.services.sns.constants import E164_REGEX, VALID_SUBSCRIPTION_ATTR_NAME
9
+ from localstack.services.sns.v2.models import SnsStore, SnsSubscription
9
10
  from localstack.utils.aws.arns import ArnData, parse_arn
10
11
  from localstack.utils.strings import short_uid, to_bytes, to_str
11
12
 
@@ -136,3 +137,14 @@ def get_region_from_subscription_token(token: str) -> str:
136
137
  return bytes.fromhex(region).decode("utf-8")
137
138
  except (IndexError, ValueError, TypeError, UnicodeDecodeError):
138
139
  raise InvalidParameterException("Invalid parameter: Token")
140
+
141
+
142
+ def get_topic_subscriptions(store: SnsStore, topic_arn: str) -> list[SnsSubscription]:
143
+ # TODO: delete this once the legacy v1 implementation has been removed
144
+ if hasattr(store, "topic_subscriptions"):
145
+ sub_arns = store.topic_subscriptions.get(topic_arn, [])
146
+ else:
147
+ sub_arns: list[str] = store.topics[topic_arn].get("subscriptions", [])
148
+
149
+ subscriptions = [store.subscriptions[k] for k in sub_arns if k in store.subscriptions]
150
+ return subscriptions
@@ -18,7 +18,7 @@ class ResultPath(EvalComponent):
18
18
  def _eval_body(self, env: Environment) -> None:
19
19
  state_input = env.states.get_input()
20
20
 
21
- # Discard task output if there is one, and set the output ot be the state's input.
21
+ # Discard task output if there is one, and set the output to be the state's input.
22
22
  if self.result_path_src is None:
23
23
  env.stack.clear()
24
24
  env.stack.append(state_input)
@@ -251,7 +251,6 @@ class ExecutionState(CommonStateField, abc.ABC):
251
251
  )
252
252
  error_output = self._construct_error_output_value(failure_event=failure_event)
253
253
  env.states.set_error_output(error_output)
254
- env.states.set_result(error_output)
255
254
 
256
255
  if self.retry:
257
256
  retry_outcome: RetryOutcome = self._handle_retry(
@@ -311,7 +311,6 @@ class StateMap(ExecutionState):
311
311
  failure_event: FailureEvent = self._from_error(env=env, ex=ex)
312
312
  error_output = self._construct_error_output_value(failure_event=failure_event)
313
313
  env.states.set_error_output(error_output)
314
- env.states.set_result(error_output)
315
314
 
316
315
  if self.retry:
317
316
  retry_outcome: RetryOutcome = self._handle_retry(
@@ -6,13 +6,13 @@ from localstack.aws.api.lambda_ import InvocationResponse
6
6
  from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.credentials import (
7
7
  StateCredentials,
8
8
  )
9
- from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.mock_eval_utils import (
10
- eval_mocked_response,
9
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.local_mock_eval_utils import (
10
+ eval_local_mocked_response,
11
11
  )
12
12
  from localstack.services.stepfunctions.asl.eval.environment import Environment
13
13
  from localstack.services.stepfunctions.asl.utils.boto_client import boto_client_for
14
14
  from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
15
- from localstack.services.stepfunctions.mocking.mock_config import MockedResponse
15
+ from localstack.services.stepfunctions.local_mocking.mock_config import LocalMockedResponse
16
16
  from localstack.utils.collections import select_from_typed_dict
17
17
  from localstack.utils.strings import to_bytes
18
18
 
@@ -42,9 +42,9 @@ def _from_payload(payload_streaming_body: IO[bytes]) -> Any | str:
42
42
  return decoded_data
43
43
 
44
44
 
45
- def _mocked_invoke_lambda_function(env: Environment) -> InvocationResponse:
46
- mocked_response: MockedResponse = env.get_current_mocked_response()
47
- eval_mocked_response(env=env, mocked_response=mocked_response)
45
+ def _local_mocked_invoke_lambda_function(env: Environment) -> InvocationResponse:
46
+ mocked_response: LocalMockedResponse = env.get_current_local_mocked_response()
47
+ eval_local_mocked_response(env=env, mocked_response=mocked_response)
48
48
  invocation_resp: InvocationResponse = env.stack.pop()
49
49
  return invocation_resp
50
50
 
@@ -68,8 +68,8 @@ def _invoke_lambda_function(
68
68
  def execute_lambda_function_integration(
69
69
  env: Environment, parameters: dict, region: str, state_credentials: StateCredentials
70
70
  ) -> None:
71
- if env.is_mocked_mode():
72
- invocation_response: InvocationResponse = _mocked_invoke_lambda_function(env=env)
71
+ if env.is_local_mocked_mode():
72
+ invocation_response: InvocationResponse = _local_mocked_invoke_lambda_function(env=env)
73
73
  else:
74
74
  invocation_response: InvocationResponse = _invoke_lambda_function(
75
75
  parameters=parameters, region=region, state_credentials=state_credentials
@@ -10,14 +10,16 @@ from localstack.services.stepfunctions.asl.component.common.error_name.failure_e
10
10
  )
11
11
  from localstack.services.stepfunctions.asl.eval.environment import Environment
12
12
  from localstack.services.stepfunctions.asl.eval.event.event_detail import EventDetails
13
- from localstack.services.stepfunctions.mocking.mock_config import (
14
- MockedResponse,
15
- MockedResponseReturn,
16
- MockedResponseThrow,
13
+ from localstack.services.stepfunctions.local_mocking.mock_config import (
14
+ LocalMockedResponse,
15
+ LocalMockedResponseReturn,
16
+ LocalMockedResponseThrow,
17
17
  )
18
18
 
19
19
 
20
- def _eval_mocked_response_throw(env: Environment, mocked_response: MockedResponseThrow) -> None:
20
+ def _eval_mocked_response_throw(
21
+ env: Environment, mocked_response: LocalMockedResponseThrow
22
+ ) -> None:
21
23
  task_failed_event_details = TaskFailedEventDetails(
22
24
  error=mocked_response.error, cause=mocked_response.cause
23
25
  )
@@ -31,15 +33,17 @@ def _eval_mocked_response_throw(env: Environment, mocked_response: MockedRespons
31
33
  raise FailureEventException(failure_event=failure_event)
32
34
 
33
35
 
34
- def _eval_mocked_response_return(env: Environment, mocked_response: MockedResponseReturn) -> None:
36
+ def _eval_mocked_response_return(
37
+ env: Environment, mocked_response: LocalMockedResponseReturn
38
+ ) -> None:
35
39
  payload_copy = copy.deepcopy(mocked_response.payload)
36
40
  env.stack.append(payload_copy)
37
41
 
38
42
 
39
- def eval_mocked_response(env: Environment, mocked_response: MockedResponse) -> None:
40
- if isinstance(mocked_response, MockedResponseReturn):
43
+ def eval_local_mocked_response(env: Environment, mocked_response: LocalMockedResponse) -> None:
44
+ if isinstance(mocked_response, LocalMockedResponseReturn):
41
45
  _eval_mocked_response_return(env=env, mocked_response=mocked_response)
42
- elif isinstance(mocked_response, MockedResponseThrow):
46
+ elif isinstance(mocked_response, LocalMockedResponseThrow):
43
47
  _eval_mocked_response_throw(env=env, mocked_response=mocked_response)
44
48
  else:
45
49
  raise RuntimeError(f"Invalid MockedResponse type '{type(mocked_response)}'")
@@ -33,8 +33,8 @@ from localstack.services.stepfunctions.asl.component.common.error_name.states_er
33
33
  from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.credentials import (
34
34
  StateCredentials,
35
35
  )
36
- from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.mock_eval_utils import (
37
- eval_mocked_response,
36
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.local_mock_eval_utils import (
37
+ eval_local_mocked_response,
38
38
  )
39
39
  from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.service.resource import (
40
40
  ResourceRuntimePart,
@@ -47,7 +47,7 @@ from localstack.services.stepfunctions.asl.component.state.state_props import St
47
47
  from localstack.services.stepfunctions.asl.eval.environment import Environment
48
48
  from localstack.services.stepfunctions.asl.eval.event.event_detail import EventDetails
49
49
  from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
50
- from localstack.services.stepfunctions.mocking.mock_config import MockedResponse
50
+ from localstack.services.stepfunctions.local_mocking.mock_config import LocalMockedResponse
51
51
  from localstack.services.stepfunctions.quotas import is_within_size_quota
52
52
  from localstack.utils.strings import camel_to_snake_case, snake_to_camel_case, to_bytes, to_str
53
53
 
@@ -356,9 +356,9 @@ class StateTaskService(StateTask, abc.ABC):
356
356
  normalised_parameters = copy.deepcopy(raw_parameters)
357
357
  self._normalise_parameters(normalised_parameters)
358
358
 
359
- if env.is_mocked_mode():
360
- mocked_response: MockedResponse = env.get_current_mocked_response()
361
- eval_mocked_response(env=env, mocked_response=mocked_response)
359
+ if env.is_local_mocked_mode():
360
+ mocked_response: LocalMockedResponse = env.get_current_local_mocked_response()
361
+ eval_local_mocked_response(env=env, mocked_response=mocked_response)
362
362
  else:
363
363
  self._eval_service_task(
364
364
  env=env,
@@ -346,7 +346,7 @@ class StateTaskServiceCallback(StateTaskService, abc.ABC):
346
346
  )
347
347
  ),
348
348
  )
349
- if not env.is_mocked_mode():
349
+ if not env.is_local_mocked_mode() and not env.is_test_state_mocked_mode():
350
350
  self._eval_integration_pattern(
351
351
  env=env,
352
352
  resource_runtime_part=resource_runtime_part,
@@ -7,6 +7,9 @@ from localstack.services.stepfunctions.asl.component.common.error_name.failure_e
7
7
  FailureEventException,
8
8
  )
9
9
  from localstack.services.stepfunctions.asl.component.state.state import CommonStateField
10
+ from localstack.services.stepfunctions.asl.component.state.state_continue_with import (
11
+ ContinueWithEnd,
12
+ )
10
13
  from localstack.services.stepfunctions.asl.component.state.state_fail.cause_decl import CauseDecl
11
14
  from localstack.services.stepfunctions.asl.component.state.state_fail.error_decl import ErrorDecl
12
15
  from localstack.services.stepfunctions.asl.component.state.state_props import StateProps
@@ -27,6 +30,7 @@ class StateFail(CommonStateField):
27
30
  super().from_state_props(state_props)
28
31
  self.cause = state_props.get(CauseDecl)
29
32
  self.error = state_props.get(ErrorDecl)
33
+ self.continue_with = ContinueWithEnd()
30
34
 
31
35
  def _eval_state(self, env: Environment) -> None:
32
36
  task_failed_event_details = TaskFailedEventDetails()
@@ -0,0 +1,118 @@
1
+ import abc
2
+ import copy
3
+ from typing import Generic, TypeVar
4
+
5
+ from localstack.services.stepfunctions.asl.component.eval_component import EvalComponent
6
+ from localstack.services.stepfunctions.asl.component.state.state import CommonStateField
7
+ from localstack.services.stepfunctions.asl.component.state.state_continue_with import (
8
+ ContinueWithNext,
9
+ )
10
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
11
+ from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
12
+ from localstack.services.stepfunctions.backend.test_state.test_state_mock import (
13
+ TestStateResponseReturn,
14
+ TestStateResponseThrow,
15
+ eval_mocked_response_throw,
16
+ )
17
+
18
+ T = TypeVar("T", bound=CommonStateField)
19
+
20
+
21
+ class MockedBaseState(Generic[T], abc.ABC):
22
+ is_single_state: bool
23
+ _wrapped: T
24
+
25
+ def __init__(self, wrapped: T):
26
+ super().__init__()
27
+ self._wrapped = wrapped
28
+ self.apply_patches()
29
+
30
+ def apply_patches(self):
31
+ self._apply_patches()
32
+
33
+ original_eval_body = self._wrapped._eval_body
34
+ self._wrapped._eval_body = self.wrap_with_post_return(
35
+ original_eval_body, self.stop_execution
36
+ )
37
+
38
+ @abc.abstractmethod
39
+ def _apply_patches(self): ...
40
+
41
+ @classmethod
42
+ def wrap(cls, state: T, is_single_state: bool = False) -> T:
43
+ cls.is_single_state = is_single_state
44
+ cls._wrapped = state
45
+ return cls(state)._wrapped
46
+
47
+ def __getattr__(self, attr: str):
48
+ return getattr(self._wrapped, attr)
49
+
50
+ @classmethod
51
+ def before_mock(self, env: TestStateEnvironment):
52
+ return
53
+
54
+ @classmethod
55
+ def do_mock(self, env: TestStateEnvironment):
56
+ mocked_response = env.mock.get_next_result()
57
+ if not mocked_response:
58
+ return
59
+
60
+ if isinstance(mocked_response, TestStateResponseThrow):
61
+ eval_mocked_response_throw(env, mocked_response)
62
+ return
63
+
64
+ if isinstance(mocked_response, TestStateResponseReturn):
65
+ result_copy = copy.deepcopy(mocked_response.payload)
66
+ env.stack.append(result_copy)
67
+
68
+ @classmethod
69
+ def after_mock(self, env: TestStateEnvironment):
70
+ return
71
+
72
+ @classmethod
73
+ def wrap_with_mock(cls, original_method):
74
+ def wrapper(env: TestStateEnvironment, *args, **kwargs):
75
+ if not env.mock.is_mocked():
76
+ original_method(env, *args, **kwargs)
77
+ return
78
+
79
+ cls.before_mock(env)
80
+ try:
81
+ cls.do_mock(env)
82
+ finally:
83
+ cls.after_mock(env)
84
+
85
+ return wrapper
86
+
87
+ @staticmethod
88
+ def wrap_with_post_return(method, post_return_fn):
89
+ def wrapper(env: TestStateEnvironment, *args, **kwargs):
90
+ try:
91
+ method(env, *args, **kwargs)
92
+ finally:
93
+ post_return_fn(env)
94
+
95
+ return wrapper
96
+
97
+ @staticmethod
98
+ def _eval_with_inspect(component: EvalComponent, key: str):
99
+ if not component:
100
+ return
101
+
102
+ eval_body_fn = component._eval_body
103
+
104
+ def _update(env: TestStateEnvironment, *args, **kwargs):
105
+ # if inspectionData already populated, don't execute again
106
+ if key in env.inspection_data:
107
+ return
108
+
109
+ eval_body_fn(env, *args, **kwargs)
110
+ result = env.stack[-1]
111
+ env.inspection_data[key] = to_json_str(result)
112
+
113
+ component._eval_body = MockedBaseState.wrap_with_post_return(eval_body_fn, _update)
114
+
115
+ def stop_execution(self, env: TestStateEnvironment):
116
+ if isinstance(self._wrapped.continue_with, ContinueWithNext):
117
+ if next_state := self._wrapped.continue_with.next_state:
118
+ env.set_choice_selected(next_state.name)
@@ -0,0 +1,82 @@
1
+ from localstack.services.stepfunctions.asl.component.state.state import CommonStateField
2
+ from localstack.services.stepfunctions.asl.component.state.state_choice.state_choice import (
3
+ StateChoice,
4
+ )
5
+ from localstack.services.stepfunctions.asl.component.state.state_continue_with import (
6
+ ContinueWithEnd,
7
+ )
8
+ from localstack.services.stepfunctions.asl.component.state.state_fail.state_fail import StateFail
9
+ from localstack.services.stepfunctions.asl.component.state.state_pass.state_pass import StatePass
10
+ from localstack.services.stepfunctions.asl.component.state.state_succeed.state_succeed import (
11
+ StateSucceed,
12
+ )
13
+ from localstack.services.stepfunctions.asl.component.test_state.state.base_mock import (
14
+ MockedBaseState,
15
+ )
16
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
17
+
18
+
19
+ class MockedCommonState(MockedBaseState[CommonStateField]):
20
+ def add_inspection_data(self, env: TestStateEnvironment):
21
+ state = self._wrapped
22
+
23
+ if not isinstance(state, StatePass):
24
+ if not self.is_single_state:
25
+ return
26
+
27
+ if "afterInputPath" not in env.inspection_data:
28
+ env.inspection_data["afterInputPath"] = env.states.get_input()
29
+ return
30
+
31
+ # If not a terminal state, only populate inspection data from pre-processor.
32
+ if not isinstance(self._wrapped.continue_with, ContinueWithEnd):
33
+ return
34
+
35
+ if state.result:
36
+ # TODO: investigate interactions between these inspectionData field types.
37
+ # i.e parity tests shows that if "Result" is defined, 'afterInputPath' and 'afterParameters'
38
+ # cannot be present in the inspection data.
39
+ env.inspection_data.pop("afterInputPath", None)
40
+ env.inspection_data.pop("afterParameters", None)
41
+
42
+ if "afterResultSelector" not in env.inspection_data:
43
+ env.inspection_data["afterResultSelector"] = state.result.result_obj
44
+
45
+ if "afterResultPath" not in env.inspection_data:
46
+ env.inspection_data["afterResultPath"] = env.inspection_data.get(
47
+ "afterResultSelector", env.states.get_input()
48
+ )
49
+ return
50
+
51
+ if "afterInputPath" not in env.inspection_data:
52
+ env.inspection_data["afterInputPath"] = env.states.get_input()
53
+
54
+ if "afterParameters" not in env.inspection_data:
55
+ env.inspection_data["afterParameters"] = env.inspection_data.get(
56
+ "afterInputPath", env.states.get_input()
57
+ )
58
+
59
+ if "afterResultSelector" not in env.inspection_data:
60
+ env.inspection_data["afterResultSelector"] = env.inspection_data["afterParameters"]
61
+
62
+ if "afterResultPath" not in env.inspection_data:
63
+ env.inspection_data["afterResultPath"] = env.inspection_data.get(
64
+ "afterResultSelector", env.states.get_input()
65
+ )
66
+
67
+ def _apply_patches(self):
68
+ if not isinstance(self._wrapped, (StatePass, StateFail, StateChoice, StateSucceed)):
69
+ raise ValueError("Needs to be a Pass, Fail, Choice, or Succeed state.")
70
+
71
+ original_eval_body = self.wrap_with_mock(self._wrapped._eval_body)
72
+
73
+ def mock_eval_execution(env: TestStateEnvironment):
74
+ original_eval_body(env)
75
+ env.set_choice_selected(env.next_state_name)
76
+
77
+ mock_eval_execution = self.wrap_with_post_return(
78
+ method=mock_eval_execution,
79
+ post_return_fn=self.add_inspection_data,
80
+ )
81
+
82
+ self._wrapped._eval_body = mock_eval_execution
@@ -0,0 +1,139 @@
1
+ from collections.abc import Callable
2
+ from functools import partial
3
+
4
+ from localstack.services.stepfunctions.asl.component.common.catch.catcher_outcome import (
5
+ CatcherOutcomeCaught,
6
+ )
7
+ from localstack.services.stepfunctions.asl.component.common.error_name.failure_event import (
8
+ FailureEvent,
9
+ )
10
+ from localstack.services.stepfunctions.asl.component.common.query_language import (
11
+ QueryLanguageMode,
12
+ )
13
+ from localstack.services.stepfunctions.asl.component.common.retry.retrier_decl import RetrierDecl
14
+ from localstack.services.stepfunctions.asl.component.common.retry.retrier_outcome import (
15
+ RetrierOutcome,
16
+ )
17
+ from localstack.services.stepfunctions.asl.component.common.retry.retry_outcome import RetryOutcome
18
+ from localstack.services.stepfunctions.asl.component.state.state_execution.execute_state import (
19
+ ExecutionState,
20
+ )
21
+ from localstack.services.stepfunctions.asl.component.test_state.state.base_mock import (
22
+ MockedBaseState,
23
+ )
24
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
25
+ from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
26
+
27
+
28
+ class MockedStateExecution(MockedBaseState[ExecutionState]):
29
+ def add_inspection_data(self, env: TestStateEnvironment):
30
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
31
+ if "afterResultSelector" not in env.inspection_data:
32
+ # HACK: A DistributedItemProcessorEvalInput is added to the stack and never popped off
33
+ # during an error case. So we need to check the inspected value is correct before
34
+ # adding it to our inspectionData.
35
+ if isinstance(env.stack[-1], (dict, str, int, float)):
36
+ env.inspection_data["afterResultSelector"] = to_json_str(env.stack[-1])
37
+
38
+ if catch := self._wrapped.catch:
39
+ for ind, catcher in enumerate(catch.catchers):
40
+ original_fn = catcher._eval_body
41
+ catcher._eval_body = self.with_catch_state_id(original_fn, ind)
42
+
43
+ if retry := self._wrapped.retry:
44
+ for ind, retrier in enumerate(retry.retriers):
45
+ original_fn = retrier._eval_body
46
+ retrier._eval_body = self.with_retry_state_id(retrier, ind)
47
+
48
+ def _apply_patches(self):
49
+ if not isinstance(self._wrapped, ExecutionState):
50
+ raise ValueError("Can only apply MockedStateExecution patches to an ExecutionState")
51
+ state = self._wrapped
52
+
53
+ if state.query_language.query_language_mode == QueryLanguageMode.JSONPath:
54
+ self._eval_with_inspect(self._wrapped.input_path, "afterInputPath")
55
+ self._eval_with_inspect(self._wrapped.result_path, "afterResultPath")
56
+
57
+ self._eval_with_inspect(self._wrapped.result_selector, "afterResultSelector")
58
+ original_eval_execution = self._wrapped._eval_execution
59
+
60
+ if self._wrapped.catch:
61
+ original_fn = self._wrapped._handle_catch
62
+ self._wrapped._handle_catch = partial(self._handle_catch, original_fn)
63
+
64
+ if self._wrapped.retry:
65
+ original_fn = self._wrapped._handle_retry
66
+ self._wrapped._handle_retry = partial(self._handle_retry, original_fn)
67
+
68
+ self._wrapped._eval_execution = self.wrap_with_post_return(
69
+ method=original_eval_execution,
70
+ post_return_fn=self.add_inspection_data,
71
+ )
72
+
73
+ @staticmethod
74
+ def with_catch_state_id(
75
+ original_eval_body: Callable[[TestStateEnvironment], None], state_id: int
76
+ ) -> Callable[[TestStateEnvironment], None]:
77
+ def _wrapped(env: TestStateEnvironment):
78
+ original_eval_body(env)
79
+
80
+ if isinstance(env.stack[-1], CatcherOutcomeCaught):
81
+ if not (error_details := env.inspection_data.get("errorDetails")):
82
+ error_details = env.inspection_data["errorDetails"] = {}
83
+
84
+ error_details["catchIndex"] = state_id
85
+
86
+ return _wrapped
87
+
88
+ @staticmethod
89
+ def with_retry_state_id(
90
+ retrier: RetrierDecl, state_id: int
91
+ ) -> Callable[[TestStateEnvironment], None]:
92
+ original_retrier_eval_body = retrier._eval_body
93
+
94
+ def _wrapped(env: TestStateEnvironment):
95
+ if (retry_count := env.mock._state_configuration.get("retrierRetryCount", 0)) > 0:
96
+ retrier.max_attempts._store_attempt_number(env, retry_count - 1)
97
+
98
+ original_retrier_eval_body(env)
99
+
100
+ if not (error_details := env.inspection_data.get("errorDetails")):
101
+ error_details = env.inspection_data["errorDetails"] = {}
102
+
103
+ error_details["retryIndex"] = state_id
104
+ if env.stack[-1] == RetrierOutcome.Executed:
105
+ # TODO(gregfurman): Ideally, retryBackoffIntervalSeconds should be written to inspectionData
106
+ # within the retrier.backoff_rate decleration (perhaps at _access_next_multiplier).
107
+ rate = retrier.backoff_rate.rate
108
+ interval = retrier.interval_seconds.seconds
109
+ error_details["retryBackoffIntervalSeconds"] = int(interval * (rate**retry_count))
110
+
111
+ return _wrapped
112
+
113
+ @staticmethod
114
+ def _handle_catch(
115
+ original_handle_catch: Callable[[TestStateEnvironment, FailureEvent], None],
116
+ env: TestStateEnvironment,
117
+ failure_event: FailureEvent,
118
+ ) -> None:
119
+ original_handle_catch(env, failure_event)
120
+
121
+ spec: dict[str, str] = ExecutionState._construct_error_output_value(failure_event)
122
+ error, cause = spec.get("Error"), spec.get("Cause")
123
+
124
+ env.set_caught_error(env.next_state_name, error, cause)
125
+
126
+ @staticmethod
127
+ def _handle_retry(
128
+ original_handle_retry: Callable[[TestStateEnvironment, FailureEvent], RetryOutcome],
129
+ env: TestStateEnvironment,
130
+ failure_event: FailureEvent,
131
+ ) -> RetryOutcome:
132
+ res = original_handle_retry(env, failure_event)
133
+
134
+ spec: dict[str, str] = ExecutionState._construct_error_output_value(failure_event)
135
+ error, cause = spec.get("Error"), spec.get("Cause")
136
+
137
+ if res == RetryOutcome.CanRetry:
138
+ env.set_retriable_error(error, cause)
139
+ return res
@@ -0,0 +1,77 @@
1
+ from localstack.services.stepfunctions.asl.component.common.error_name.states_error_name_type import (
2
+ StatesErrorNameType,
3
+ )
4
+ from localstack.services.stepfunctions.asl.component.common.query_language import (
5
+ QueryLanguageMode,
6
+ )
7
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_map.state_map import (
8
+ StateMap,
9
+ )
10
+ from localstack.services.stepfunctions.asl.component.test_state.state.base_mock import (
11
+ MockedBaseState,
12
+ )
13
+ from localstack.services.stepfunctions.asl.component.test_state.state.execution import (
14
+ MockedStateExecution,
15
+ )
16
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
17
+ from localstack.services.stepfunctions.backend.test_state.test_state_mock import (
18
+ TestStateResponseThrow,
19
+ )
20
+
21
+
22
+ class MockedStateMap(MockedBaseState[StateMap]):
23
+ def add_inspection_data(self, env: TestStateEnvironment):
24
+ if tolerated_failure_percentage := env.inspection_data.get("toleratedFailurePercentage"):
25
+ env.inspection_data["toleratedFailurePercentage"] = float(tolerated_failure_percentage)
26
+
27
+ if tolerated_failure_count := env.inspection_data.get("toleratedFailureCount"):
28
+ env.inspection_data["toleratedFailureCount"] = int(tolerated_failure_count)
29
+
30
+ @classmethod
31
+ def before_mock(cls, env: TestStateEnvironment):
32
+ if not env.mock or not env.mock._state_configuration:
33
+ return
34
+
35
+ if not cls._wrapped.catch and not cls._wrapped.retry:
36
+ return
37
+
38
+ if failure_count := env.mock._state_configuration.get("mapIterationFailureCount"):
39
+ max_failure_count = (
40
+ cls._wrapped.tolerated_failure_count_decl._eval_tolerated_failure_count(env)
41
+ )
42
+ if failure_count > max_failure_count:
43
+ error_response = TestStateResponseThrow(
44
+ error=StatesErrorNameType.StatesExceedToleratedFailureThreshold.to_name(),
45
+ cause="The specified tolerated failure threshold was exceeded",
46
+ )
47
+ env.mock.add_result(error_response)
48
+ return
49
+
50
+ def _apply_patches(self):
51
+ self._wrapped = MockedStateExecution.wrap(self._wrapped)
52
+
53
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
54
+ self._eval_with_inspect(self._wrapped.items_path, "afterInputPath")
55
+ self._eval_with_inspect(self._wrapped.item_selector, "afterItemsSelector")
56
+
57
+ original_eval_max_concurrency = self._wrapped.max_concurrency_decl._eval_max_concurrency
58
+ original_iteration_component_eval_body = self._wrapped.iteration_component._eval_body
59
+ original_eval_execution = self._wrapped._eval_execution
60
+
61
+ # HACK(gregfurman): Ideally we should be using the "$$.Map.Item.Index" to access each item of the
62
+ # mocked result list. This is turning out to be quite complicated, so instead just patch the
63
+ # StateMap's max concurrency decleration to always eval to '1' -- making the map run in serial.
64
+ def mock_max_concurrency(env: TestStateEnvironment) -> int:
65
+ # always set concurrency to 1 but inspection data is accurate to original
66
+ env.inspection_data["maxConcurrency"] = original_eval_max_concurrency(env)
67
+ return 1
68
+
69
+ self._wrapped._eval_execution = self.wrap_with_post_return(
70
+ method=original_eval_execution,
71
+ post_return_fn=self.add_inspection_data,
72
+ )
73
+
74
+ self._wrapped.max_concurrency_decl._eval_max_concurrency = mock_max_concurrency
75
+ self._wrapped.iteration_component._eval_body = self.wrap_with_mock(
76
+ original_iteration_component_eval_body
77
+ )
@@ -0,0 +1,44 @@
1
+ from localstack.services.stepfunctions.asl.component.common.query_language import (
2
+ QueryLanguageMode,
3
+ )
4
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.service.state_task_service import (
5
+ StateTaskService,
6
+ )
7
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.state_task import (
8
+ StateTask,
9
+ )
10
+ from localstack.services.stepfunctions.asl.component.test_state.state.base_mock import (
11
+ MockedBaseState,
12
+ )
13
+ from localstack.services.stepfunctions.asl.component.test_state.state.execution import (
14
+ MockedStateExecution,
15
+ )
16
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
17
+ from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
18
+
19
+
20
+ class MockedStateTask(MockedBaseState[StateTask]):
21
+ def add_inspection_data(self, env: TestStateEnvironment):
22
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
23
+ if "afterParameters" not in env.inspection_data:
24
+ env.inspection_data["afterParameters"] = to_json_str(env.states.get_input())
25
+
26
+ def _apply_patches(self):
27
+ self._wrapped = MockedStateExecution.wrap(self._wrapped)
28
+
29
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
30
+ self._eval_with_inspect(self._wrapped.parargs, "afterParameters")
31
+
32
+ if isinstance(self._wrapped, StateTaskService):
33
+ self._wrapped._eval_service_task = self.wrap_with_mock(self._wrapped._eval_service_task)
34
+
35
+ original_eval_execution = self._wrapped._eval_execution
36
+
37
+ def mock_eval_execution(env: TestStateEnvironment, *args, **kwargs):
38
+ original_eval_execution(env, *args, **kwargs)
39
+ result = to_json_str(env.stack[-1])
40
+ env.inspection_data["result"] = result
41
+
42
+ self._wrapped._eval_execution = self.wrap_with_post_return(
43
+ mock_eval_execution, self.add_inspection_data
44
+ )