localstack-core 4.10.1.dev7__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.
Potentially problematic release.
This version of localstack-core might be problematic. Click here for more details.
- localstack/aws/api/acm/__init__.py +122 -122
- localstack/aws/api/apigateway/__init__.py +604 -561
- localstack/aws/api/cloudcontrol/__init__.py +63 -63
- localstack/aws/api/cloudformation/__init__.py +1201 -969
- localstack/aws/api/cloudwatch/__init__.py +375 -375
- localstack/aws/api/config/__init__.py +784 -786
- localstack/aws/api/dynamodb/__init__.py +753 -759
- localstack/aws/api/dynamodbstreams/__init__.py +74 -74
- localstack/aws/api/ec2/__init__.py +10062 -8826
- localstack/aws/api/es/__init__.py +453 -453
- localstack/aws/api/events/__init__.py +552 -552
- localstack/aws/api/firehose/__init__.py +541 -543
- localstack/aws/api/iam/__init__.py +866 -572
- localstack/aws/api/kinesis/__init__.py +235 -147
- localstack/aws/api/kms/__init__.py +341 -336
- localstack/aws/api/lambda_/__init__.py +974 -621
- localstack/aws/api/logs/__init__.py +988 -675
- localstack/aws/api/opensearch/__init__.py +903 -785
- localstack/aws/api/pipes/__init__.py +336 -336
- localstack/aws/api/redshift/__init__.py +1257 -1166
- localstack/aws/api/resource_groups/__init__.py +175 -175
- localstack/aws/api/resourcegroupstaggingapi/__init__.py +103 -67
- localstack/aws/api/route53/__init__.py +296 -254
- localstack/aws/api/route53resolver/__init__.py +397 -396
- localstack/aws/api/s3/__init__.py +1412 -1349
- localstack/aws/api/s3control/__init__.py +594 -594
- localstack/aws/api/scheduler/__init__.py +118 -118
- localstack/aws/api/secretsmanager/__init__.py +221 -216
- localstack/aws/api/ses/__init__.py +227 -227
- localstack/aws/api/sns/__init__.py +115 -115
- localstack/aws/api/sqs/__init__.py +100 -100
- localstack/aws/api/ssm/__init__.py +1977 -1971
- localstack/aws/api/stepfunctions/__init__.py +375 -333
- localstack/aws/api/sts/__init__.py +142 -66
- localstack/aws/api/support/__init__.py +112 -112
- localstack/aws/api/swf/__init__.py +378 -386
- localstack/aws/api/transcribe/__init__.py +425 -425
- localstack/aws/handlers/logging.py +8 -4
- localstack/aws/handlers/service.py +22 -3
- localstack/aws/protocol/parser.py +1 -1
- localstack/aws/protocol/serializer.py +1 -1
- localstack/aws/scaffold.py +15 -17
- localstack/cli/localstack.py +6 -1
- localstack/deprecations.py +0 -6
- localstack/dev/kubernetes/__main__.py +38 -3
- localstack/services/acm/provider.py +4 -0
- localstack/services/apigateway/helpers.py +5 -9
- localstack/services/apigateway/legacy/provider.py +60 -24
- localstack/services/apigateway/patches.py +0 -9
- localstack/services/cloudformation/engine/template_preparer.py +6 -2
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +12 -0
- localstack/services/cloudformation/provider.py +2 -2
- localstack/services/cloudformation/v2/provider.py +6 -6
- 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 +44 -24
- localstack/services/kms/provider.py +97 -16
- localstack/services/lambda_/api_utils.py +40 -21
- localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
- localstack/services/lambda_/invocation/assignment.py +4 -1
- localstack/services/lambda_/invocation/execution_environment.py +21 -2
- localstack/services/lambda_/invocation/lambda_models.py +27 -2
- localstack/services/lambda_/invocation/lambda_service.py +51 -3
- localstack/services/lambda_/invocation/models.py +9 -1
- localstack/services/lambda_/invocation/version_manager.py +18 -3
- localstack/services/lambda_/packages.py +1 -1
- localstack/services/lambda_/provider.py +240 -96
- localstack/services/lambda_/resource_providers/aws_lambda_function.py +33 -1
- localstack/services/lambda_/runtimes.py +10 -3
- localstack/services/logs/provider.py +45 -19
- localstack/services/opensearch/provider.py +53 -3
- localstack/services/resource_groups/provider.py +5 -1
- localstack/services/resourcegroupstaggingapi/provider.py +6 -1
- localstack/services/s3/provider.py +29 -16
- localstack/services/s3/utils.py +35 -14
- localstack/services/s3control/provider.py +101 -2
- localstack/services/s3control/validation.py +50 -0
- localstack/services/sns/constants.py +3 -1
- localstack/services/sns/publisher.py +15 -6
- localstack/services/sns/v2/models.py +30 -1
- localstack/services/sns/v2/provider.py +794 -31
- localstack/services/sns/v2/utils.py +20 -0
- localstack/services/sqs/models.py +37 -10
- 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 +170 -22
- localstack/services/stepfunctions/backend/execution.py +6 -6
- 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 +78 -27
- localstack/services/stepfunctions/test_state/mock_config.py +47 -0
- localstack/testing/pytest/fixtures.py +28 -0
- localstack/testing/snapshots/transformer_utility.py +7 -0
- localstack/testing/testselection/matching.py +0 -1
- 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 +0 -8
- localstack/utils/batching.py +258 -0
- localstack/utils/catalog/catalog_loader.py +111 -3
- localstack/utils/collections.py +23 -11
- localstack/utils/crypto.py +109 -0
- localstack/version.py +2 -2
- {localstack_core-4.10.1.dev7.dist-info → localstack_core-4.11.2.dev14.dist-info}/METADATA +7 -6
- {localstack_core-4.10.1.dev7.dist-info → localstack_core-4.11.2.dev14.dist-info}/RECORD +149 -141
- localstack_core-4.11.2.dev14.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.dev7.dist-info/plux.json +0 -1
- /localstack/services/stepfunctions/{mocking → local_mocking}/mock_config_file.py +0 -0
- {localstack_core-4.10.1.dev7.data → localstack_core-4.11.2.dev14.data}/scripts/localstack +0 -0
- {localstack_core-4.10.1.dev7.data → localstack_core-4.11.2.dev14.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.10.1.dev7.data → localstack_core-4.11.2.dev14.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.10.1.dev7.dist-info → localstack_core-4.11.2.dev14.dist-info}/WHEEL +0 -0
- {localstack_core-4.10.1.dev7.dist-info → localstack_core-4.11.2.dev14.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.10.1.dev7.dist-info → localstack_core-4.11.2.dev14.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.10.1.dev7.dist-info → localstack_core-4.11.2.dev14.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,104 @@
|
|
|
1
|
-
from localstack.aws.api
|
|
1
|
+
from localstack.aws.api import CommonServiceException, RequestContext
|
|
2
|
+
from localstack.aws.api.s3control import (
|
|
3
|
+
AccountId,
|
|
4
|
+
ListTagsForResourceResult,
|
|
5
|
+
S3ControlApi,
|
|
6
|
+
S3ResourceArn,
|
|
7
|
+
TagKeyList,
|
|
8
|
+
TagList,
|
|
9
|
+
TagResourceResult,
|
|
10
|
+
UntagResourceResult,
|
|
11
|
+
)
|
|
12
|
+
from localstack.aws.forwarder import NotImplementedAvoidFallbackError
|
|
13
|
+
from localstack.services.s3.models import s3_stores
|
|
14
|
+
from localstack.services.s3control.validation import validate_tags
|
|
15
|
+
from localstack.utils.tagging import TaggingService
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NoSuchResource(CommonServiceException):
|
|
19
|
+
def __init__(self, message=None):
|
|
20
|
+
super().__init__("NoSuchResource", status_code=404, message=message)
|
|
2
21
|
|
|
3
22
|
|
|
4
23
|
class S3ControlProvider(S3ControlApi):
|
|
5
|
-
|
|
24
|
+
"""
|
|
25
|
+
S3Control is a management interface for S3, and can access some of its internals with no public API
|
|
26
|
+
This requires us to access the s3 stores directly
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _get_tagging_service_for_bucket(
|
|
31
|
+
resource_arn: S3ResourceArn,
|
|
32
|
+
partition: str,
|
|
33
|
+
region: str,
|
|
34
|
+
account_id: str,
|
|
35
|
+
) -> TaggingService:
|
|
36
|
+
s3_prefix = f"arn:{partition}:s3:::"
|
|
37
|
+
if not resource_arn.startswith(s3_prefix):
|
|
38
|
+
# Moto does not support Tagging operations for S3 Control, so we should not forward those operations back
|
|
39
|
+
# to it
|
|
40
|
+
raise NotImplementedAvoidFallbackError(
|
|
41
|
+
"LocalStack only support Bucket tagging operations for S3Control"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
store = s3_stores[account_id][region]
|
|
45
|
+
bucket_name = resource_arn.removeprefix(s3_prefix)
|
|
46
|
+
if bucket_name not in store.global_bucket_map:
|
|
47
|
+
raise NoSuchResource("The specified resource doesn't exist.")
|
|
48
|
+
|
|
49
|
+
return store.TAGS
|
|
50
|
+
|
|
51
|
+
def tag_resource(
|
|
52
|
+
self,
|
|
53
|
+
context: RequestContext,
|
|
54
|
+
account_id: AccountId,
|
|
55
|
+
resource_arn: S3ResourceArn,
|
|
56
|
+
tags: TagList,
|
|
57
|
+
**kwargs,
|
|
58
|
+
) -> TagResourceResult:
|
|
59
|
+
# currently S3Control only supports tagging buckets
|
|
60
|
+
tagging_service = self._get_tagging_service_for_bucket(
|
|
61
|
+
resource_arn=resource_arn,
|
|
62
|
+
partition=context.partition,
|
|
63
|
+
region=context.region,
|
|
64
|
+
account_id=account_id,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
validate_tags(tags=tags)
|
|
68
|
+
tagging_service.tag_resource(resource_arn, tags)
|
|
69
|
+
|
|
70
|
+
return TagResourceResult()
|
|
71
|
+
|
|
72
|
+
def untag_resource(
|
|
73
|
+
self,
|
|
74
|
+
context: RequestContext,
|
|
75
|
+
account_id: AccountId,
|
|
76
|
+
resource_arn: S3ResourceArn,
|
|
77
|
+
tag_keys: TagKeyList,
|
|
78
|
+
**kwargs,
|
|
79
|
+
) -> UntagResourceResult:
|
|
80
|
+
# currently S3Control only supports tagging buckets
|
|
81
|
+
tagging_service = self._get_tagging_service_for_bucket(
|
|
82
|
+
resource_arn=resource_arn,
|
|
83
|
+
partition=context.partition,
|
|
84
|
+
region=context.region,
|
|
85
|
+
account_id=account_id,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
tagging_service.untag_resource(resource_arn, tag_keys)
|
|
89
|
+
|
|
90
|
+
return TagResourceResult()
|
|
91
|
+
|
|
92
|
+
def list_tags_for_resource(
|
|
93
|
+
self, context: RequestContext, account_id: AccountId, resource_arn: S3ResourceArn, **kwargs
|
|
94
|
+
) -> ListTagsForResourceResult:
|
|
95
|
+
# currently S3Control only supports tagging buckets
|
|
96
|
+
tagging_service = self._get_tagging_service_for_bucket(
|
|
97
|
+
resource_arn=resource_arn,
|
|
98
|
+
partition=context.partition,
|
|
99
|
+
region=context.region,
|
|
100
|
+
account_id=account_id,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
tags = tagging_service.list_tags_for_resource(resource_arn)
|
|
104
|
+
return ListTagsForResourceResult(Tags=tags["Tags"])
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from localstack.aws.api.s3 import InvalidTag
|
|
2
|
+
from localstack.aws.api.s3control import Tag, TagList
|
|
3
|
+
from localstack.services.s3.exceptions import MalformedXML
|
|
4
|
+
from localstack.services.s3.utils import TAG_REGEX
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def validate_tags(tags: TagList):
|
|
8
|
+
"""
|
|
9
|
+
Validate the tags provided. This is the same function as S3, but with different error messages
|
|
10
|
+
:param tags: a TagList object
|
|
11
|
+
:raises MalformedXML if the object does not conform to the schema
|
|
12
|
+
:raises InvalidTag if the tag key or value are outside the set of validations defined by S3 and S3Control
|
|
13
|
+
:return: None
|
|
14
|
+
"""
|
|
15
|
+
keys = set()
|
|
16
|
+
for tag in tags:
|
|
17
|
+
tag: Tag
|
|
18
|
+
if set(tag) != {"Key", "Value"}:
|
|
19
|
+
raise MalformedXML()
|
|
20
|
+
|
|
21
|
+
key = tag["Key"]
|
|
22
|
+
value = tag["Value"]
|
|
23
|
+
|
|
24
|
+
if key is None or value is None:
|
|
25
|
+
raise MalformedXML()
|
|
26
|
+
|
|
27
|
+
if key in keys:
|
|
28
|
+
raise InvalidTag(
|
|
29
|
+
"There are duplicate tag keys in your request. Remove the duplicate tag keys and try again.",
|
|
30
|
+
TagKey=key,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if key.startswith("aws:"):
|
|
34
|
+
raise InvalidTag(
|
|
35
|
+
'User-defined tag keys can\'t start with "aws:". This prefix is reserved for system tags. Remove "aws:" from your tag keys and try again.',
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if not TAG_REGEX.match(key):
|
|
39
|
+
raise InvalidTag(
|
|
40
|
+
"This request contains a tag key or value that isn't valid. Valid characters include the following: [a-zA-Z+-=._:/]. Tag keys can contain up to 128 characters. Tag values can contain up to 256 characters.",
|
|
41
|
+
TagKey=key,
|
|
42
|
+
)
|
|
43
|
+
elif not TAG_REGEX.match(value):
|
|
44
|
+
raise InvalidTag(
|
|
45
|
+
"This request contains a tag key or value that isn't valid. Valid characters include the following: [a-zA-Z+-=._:/]. Tag keys can contain up to 128 characters. Tag values can contain up to 256 characters.",
|
|
46
|
+
TagKey=key,
|
|
47
|
+
TagValue=value,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
keys.add(key)
|
|
@@ -28,6 +28,7 @@ VALID_SUBSCRIPTION_ATTR_NAME: list[str] = [
|
|
|
28
28
|
MSG_ATTR_NAME_REGEX = re.compile(r"^(?!\.)(?!.*\.$)(?!.*\.\.)[a-zA-Z0-9_\-.]+$")
|
|
29
29
|
ATTR_TYPE_REGEX = re.compile(r"^(String|Number|Binary)\..+$")
|
|
30
30
|
VALID_MSG_ATTR_NAME_CHARS = set(ascii_letters + digits + "." + "-" + "_")
|
|
31
|
+
E164_REGEX = re.compile(r"^\+?[1-9]\d{1,14}$")
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
GCM_URL = "https://fcm.googleapis.com/fcm/send"
|
|
@@ -42,6 +43,7 @@ SUBSCRIPTION_TOKENS_ENDPOINT = "/_aws/sns/subscription-tokens"
|
|
|
42
43
|
SNS_CERT_ENDPOINT = "/_aws/sns/SimpleNotificationService-6c6f63616c737461636b69736e696365.pem"
|
|
43
44
|
|
|
44
45
|
DUMMY_SUBSCRIPTION_PRINCIPAL = "arn:{partition}:iam::{account_id}:user/DummySNSPrincipal"
|
|
45
|
-
E164_REGEX = re.compile(r"^\+?[1-9]\d{1,14}$")
|
|
46
46
|
|
|
47
47
|
VALID_APPLICATION_PLATFORMS = list(get_args(SnsApplicationPlatforms))
|
|
48
|
+
|
|
49
|
+
MAXIMUM_MESSAGE_LENGTH = 262144
|
|
@@ -30,6 +30,7 @@ from localstack.services.sns.models import (
|
|
|
30
30
|
SnsStore,
|
|
31
31
|
SnsSubscription,
|
|
32
32
|
)
|
|
33
|
+
from localstack.services.sns.v2.utils import get_topic_subscriptions
|
|
33
34
|
from localstack.utils.aws.arns import (
|
|
34
35
|
PARTITION_NAMES,
|
|
35
36
|
extract_account_id_from_arn,
|
|
@@ -254,9 +255,11 @@ class LambdaTopicPublisher(TopicPublisher):
|
|
|
254
255
|
"UnsubscribeUrl": unsubscribe_url,
|
|
255
256
|
"MessageAttributes": message_attributes,
|
|
256
257
|
}
|
|
257
|
-
|
|
258
|
+
# TODO: remove v1 "signature_version" access once v1 is retired
|
|
258
259
|
signature_version = (
|
|
259
|
-
topic_attributes.get("signature_version", "
|
|
260
|
+
topic_attributes.get("signature_version", topic_attributes.get("SignatureVersion", "1"))
|
|
261
|
+
if topic_attributes
|
|
262
|
+
else "1"
|
|
260
263
|
)
|
|
261
264
|
canonical_string = compute_canonical_string(event_payload, message_context.type)
|
|
262
265
|
signature = get_message_signature(canonical_string, signature_version=signature_version)
|
|
@@ -558,7 +561,10 @@ class HttpTopicPublisher(TopicPublisher):
|
|
|
558
561
|
):
|
|
559
562
|
return sub_content_type
|
|
560
563
|
|
|
561
|
-
|
|
564
|
+
# TODO: remove lower case access once legacy v1 provider is removed
|
|
565
|
+
if json_topic_delivery_policy := topic_attributes.get(
|
|
566
|
+
"delivery_policy", topic_attributes.get("DeliveryPolicy")
|
|
567
|
+
):
|
|
562
568
|
topic_delivery_policy = json.loads(json_topic_delivery_policy)
|
|
563
569
|
if not (
|
|
564
570
|
topic_content_type := topic_delivery_policy.get(subscriber["Protocol"].lower())
|
|
@@ -1009,7 +1015,10 @@ def create_sns_message_body(
|
|
|
1009
1015
|
# FIFO topics do not add the signature in the message
|
|
1010
1016
|
if not subscriber.get("TopicArn", "").endswith(".fifo"):
|
|
1011
1017
|
signature_version = (
|
|
1012
|
-
|
|
1018
|
+
# we allow for both casings, depending on v1 or v2 provider
|
|
1019
|
+
topic_attributes.get("signature_version", topic_attributes.get("SignatureVersion", "1"))
|
|
1020
|
+
if topic_attributes
|
|
1021
|
+
else "1"
|
|
1013
1022
|
)
|
|
1014
1023
|
canonical_string = compute_canonical_string(data, message_type)
|
|
1015
1024
|
signature = get_message_signature(canonical_string, signature_version=signature_version)
|
|
@@ -1234,7 +1243,7 @@ class PublishDispatcher:
|
|
|
1234
1243
|
)
|
|
1235
1244
|
|
|
1236
1245
|
def publish_to_topic(self, ctx: SnsPublishContext, topic_arn: str) -> None:
|
|
1237
|
-
subscriptions = ctx.store
|
|
1246
|
+
subscriptions = get_topic_subscriptions(ctx.store, topic_arn)
|
|
1238
1247
|
for subscriber in subscriptions:
|
|
1239
1248
|
if self._should_publish(ctx.store.subscription_filter_policy, ctx.message, subscriber):
|
|
1240
1249
|
notifier = self.topic_notifiers[subscriber["Protocol"]]
|
|
@@ -1249,7 +1258,7 @@ class PublishDispatcher:
|
|
|
1249
1258
|
self._submit_notification(notifier, ctx, subscriber)
|
|
1250
1259
|
|
|
1251
1260
|
def publish_batch_to_topic(self, ctx: SnsBatchPublishContext, topic_arn: str) -> None:
|
|
1252
|
-
subscriptions = ctx.store
|
|
1261
|
+
subscriptions = get_topic_subscriptions(ctx.store, topic_arn)
|
|
1253
1262
|
for subscriber in subscriptions:
|
|
1254
1263
|
protocol = subscriber["Protocol"]
|
|
1255
1264
|
notifier = self.batch_topic_notifiers.get(protocol)
|
|
@@ -5,6 +5,7 @@ from enum import StrEnum
|
|
|
5
5
|
from typing import Literal, TypedDict
|
|
6
6
|
|
|
7
7
|
from localstack.aws.api.sns import (
|
|
8
|
+
Endpoint,
|
|
8
9
|
MessageAttributeMap,
|
|
9
10
|
PlatformApplication,
|
|
10
11
|
PublishBatchRequestEntry,
|
|
@@ -39,6 +40,12 @@ SnsApplicationPlatforms = Literal[
|
|
|
39
40
|
]
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
class EndpointAttributeNames(StrEnum):
|
|
44
|
+
CUSTOM_USER_DATA = "CustomUserData"
|
|
45
|
+
Token = "Token"
|
|
46
|
+
ENABLED = "Enabled"
|
|
47
|
+
|
|
48
|
+
|
|
42
49
|
SMS_ATTRIBUTE_NAMES = [
|
|
43
50
|
"DeliveryStatusIAMRole",
|
|
44
51
|
"DeliveryStatusSuccessSamplingRate",
|
|
@@ -143,6 +150,19 @@ class SnsMessage:
|
|
|
143
150
|
)
|
|
144
151
|
|
|
145
152
|
|
|
153
|
+
@dataclass
|
|
154
|
+
class PlatformEndpoint:
|
|
155
|
+
platform_application_arn: str
|
|
156
|
+
platform_endpoint: Endpoint
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass
|
|
160
|
+
class PlatformApplicationDetails:
|
|
161
|
+
platform_application: PlatformApplication
|
|
162
|
+
# maps all Endpoints of the PlatformApplication, from their Token to their ARN
|
|
163
|
+
platform_endpoints: dict[str, str]
|
|
164
|
+
|
|
165
|
+
|
|
146
166
|
class SnsStore(BaseStore):
|
|
147
167
|
topics: dict[str, Topic] = LocalAttribute(default=dict)
|
|
148
168
|
|
|
@@ -156,11 +176,20 @@ class SnsStore(BaseStore):
|
|
|
156
176
|
subscription_tokens: dict[str, str] = LocalAttribute(default=dict)
|
|
157
177
|
|
|
158
178
|
# maps platform application arns to platform applications
|
|
159
|
-
platform_applications: dict[str,
|
|
179
|
+
platform_applications: dict[str, PlatformApplicationDetails] = LocalAttribute(default=dict)
|
|
180
|
+
|
|
181
|
+
# maps endpoint arns to platform endpoints
|
|
182
|
+
platform_endpoints: dict[str, PlatformEndpoint] = LocalAttribute(default=dict)
|
|
183
|
+
|
|
184
|
+
# cache of topic ARN to platform endpoint messages (used primarily for testing)
|
|
185
|
+
platform_endpoint_messages: dict[str, list[dict]] = LocalAttribute(default=dict)
|
|
160
186
|
|
|
161
187
|
# topic/subscription independent default values for sending sms messages
|
|
162
188
|
sms_attributes: dict[str, str] = LocalAttribute(default=dict)
|
|
163
189
|
|
|
190
|
+
# list of sent SMS messages
|
|
191
|
+
sms_messages: list[dict] = LocalAttribute(default=list)
|
|
192
|
+
|
|
164
193
|
TAGS: TaggingService = CrossRegionAttribute(default=TaggingService)
|
|
165
194
|
|
|
166
195
|
|