localstack-core 4.6.1.dev60__py3-none-any.whl → 4.10.1.dev12__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 (672) hide show
  1. localstack/aws/api/apigateway/__init__.py +87 -110
  2. localstack/aws/api/cloudformation/__init__.py +18 -4
  3. localstack/aws/api/cloudwatch/__init__.py +41 -1
  4. localstack/aws/api/config/__init__.py +4 -0
  5. localstack/aws/api/core.py +8 -5
  6. localstack/aws/api/dynamodb/__init__.py +30 -0
  7. localstack/aws/api/ec2/__init__.py +1545 -66
  8. localstack/aws/api/events/__init__.py +12 -16
  9. localstack/aws/api/iam/__init__.py +7 -0
  10. localstack/aws/api/kinesis/__init__.py +19 -0
  11. localstack/aws/api/kms/__init__.py +6 -0
  12. localstack/aws/api/lambda_/__init__.py +36 -23
  13. localstack/aws/api/logs/__init__.py +20 -8
  14. localstack/aws/api/opensearch/__init__.py +16 -0
  15. localstack/aws/api/pipes/__init__.py +24 -32
  16. localstack/aws/api/redshift/__init__.py +9 -3
  17. localstack/aws/api/route53/__init__.py +5 -0
  18. localstack/aws/api/s3/__init__.py +12 -0
  19. localstack/aws/api/s3control/__init__.py +56 -0
  20. localstack/aws/api/scheduler/__init__.py +14 -16
  21. localstack/aws/api/ssm/__init__.py +2 -0
  22. localstack/aws/api/stepfunctions/__init__.py +88 -114
  23. localstack/aws/api/support/__init__.py +8 -9
  24. localstack/aws/api/transcribe/__init__.py +17 -0
  25. localstack/aws/chain.py +2 -2
  26. localstack/aws/client.py +14 -11
  27. localstack/aws/connect.py +42 -41
  28. localstack/aws/forwarder.py +57 -9
  29. localstack/aws/gateway.py +1 -3
  30. localstack/aws/handlers/analytics.py +2 -3
  31. localstack/aws/handlers/cors.py +4 -5
  32. localstack/aws/handlers/internal_requests.py +6 -1
  33. localstack/aws/handlers/logging.py +13 -4
  34. localstack/aws/handlers/metric_handler.py +44 -5
  35. localstack/aws/handlers/service.py +48 -28
  36. localstack/aws/mocking.py +18 -27
  37. localstack/aws/patches.py +2 -2
  38. localstack/aws/protocol/op_router.py +11 -10
  39. localstack/aws/protocol/parser.py +475 -49
  40. localstack/aws/protocol/serializer.py +723 -106
  41. localstack/aws/protocol/service_router.py +133 -33
  42. localstack/aws/protocol/validate.py +6 -6
  43. localstack/aws/scaffold.py +9 -10
  44. localstack/aws/serving/edge.py +5 -6
  45. localstack/aws/serving/hypercorn.py +2 -2
  46. localstack/aws/serving/twisted.py +1 -2
  47. localstack/aws/serving/werkzeug.py +2 -2
  48. localstack/aws/skeleton.py +12 -11
  49. localstack/aws/spec-patches.json +58 -0
  50. localstack/aws/spec.py +66 -46
  51. localstack/cli/exceptions.py +1 -1
  52. localstack/cli/localstack.py +11 -11
  53. localstack/cli/lpm.py +4 -5
  54. localstack/cli/plugins.py +1 -1
  55. localstack/cli/profiles.py +1 -2
  56. localstack/config.py +44 -30
  57. localstack/constants.py +4 -29
  58. localstack/deprecations.py +5 -5
  59. localstack/dev/kubernetes/__main__.py +130 -7
  60. localstack/dev/run/__main__.py +5 -5
  61. localstack/dev/run/configurators.py +1 -4
  62. localstack/dev/run/paths.py +6 -6
  63. localstack/dns/models.py +2 -1
  64. localstack/dns/plugins.py +5 -1
  65. localstack/dns/server.py +16 -6
  66. localstack/http/dispatcher.py +1 -2
  67. localstack/http/response.py +2 -2
  68. localstack/http/router.py +1 -1
  69. localstack/http/trace.py +2 -1
  70. localstack/logging/format.py +6 -6
  71. localstack/packages/api.py +13 -12
  72. localstack/packages/core.py +4 -4
  73. localstack/packages/debugpy.py +1 -3
  74. localstack/packages/ffmpeg.py +1 -2
  75. localstack/packages/java.py +38 -12
  76. localstack/packages/plugins.py +0 -8
  77. localstack/runtime/analytics.py +3 -0
  78. localstack/runtime/hooks.py +1 -1
  79. localstack/runtime/init.py +8 -9
  80. localstack/runtime/main.py +5 -5
  81. localstack/runtime/patches.py +2 -2
  82. localstack/runtime/shutdown.py +2 -1
  83. localstack/services/apigateway/exporter.py +1 -2
  84. localstack/services/apigateway/helpers.py +7 -10
  85. localstack/services/apigateway/legacy/context.py +21 -21
  86. localstack/services/apigateway/legacy/helpers.py +27 -28
  87. localstack/services/apigateway/legacy/integration.py +11 -10
  88. localstack/services/apigateway/legacy/invocations.py +6 -5
  89. localstack/services/apigateway/legacy/provider.py +148 -68
  90. localstack/services/apigateway/legacy/router_asf.py +2 -2
  91. localstack/services/apigateway/legacy/templates.py +6 -6
  92. localstack/services/apigateway/models.py +16 -16
  93. localstack/services/apigateway/next_gen/execute_api/api.py +2 -2
  94. localstack/services/apigateway/next_gen/execute_api/context.py +25 -25
  95. localstack/services/apigateway/next_gen/execute_api/handlers/api_key_validation.py +2 -5
  96. localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
  97. localstack/services/apigateway/next_gen/execute_api/handlers/parse.py +1 -2
  98. localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +2 -3
  99. localstack/services/apigateway/next_gen/execute_api/header_utils.py +1 -1
  100. localstack/services/apigateway/next_gen/execute_api/helpers.py +2 -2
  101. localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +9 -6
  102. localstack/services/apigateway/next_gen/execute_api/integrations/http.py +12 -12
  103. localstack/services/apigateway/next_gen/execute_api/router.py +31 -0
  104. localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
  105. localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
  106. localstack/services/apigateway/next_gen/execute_api/variables.py +63 -63
  107. localstack/services/apigateway/next_gen/provider.py +5 -0
  108. localstack/services/apigateway/resource_providers/aws_apigateway_account.py +3 -3
  109. localstack/services/apigateway/resource_providers/aws_apigateway_account_plugin.py +1 -3
  110. localstack/services/apigateway/resource_providers/aws_apigateway_apikey.py +14 -14
  111. localstack/services/apigateway/resource_providers/aws_apigateway_apikey_plugin.py +1 -3
  112. localstack/services/apigateway/resource_providers/aws_apigateway_basepathmapping.py +5 -5
  113. localstack/services/apigateway/resource_providers/aws_apigateway_basepathmapping_plugin.py +1 -3
  114. localstack/services/apigateway/resource_providers/aws_apigateway_deployment.py +46 -46
  115. localstack/services/apigateway/resource_providers/aws_apigateway_deployment_plugin.py +1 -3
  116. localstack/services/apigateway/resource_providers/aws_apigateway_domainname.py +18 -18
  117. localstack/services/apigateway/resource_providers/aws_apigateway_domainname_plugin.py +1 -3
  118. localstack/services/apigateway/resource_providers/aws_apigateway_gatewayresponse.py +7 -7
  119. localstack/services/apigateway/resource_providers/aws_apigateway_gatewayresponse_plugin.py +1 -3
  120. localstack/services/apigateway/resource_providers/aws_apigateway_method.py +36 -36
  121. localstack/services/apigateway/resource_providers/aws_apigateway_method_plugin.py +1 -3
  122. localstack/services/apigateway/resource_providers/aws_apigateway_model.py +6 -6
  123. localstack/services/apigateway/resource_providers/aws_apigateway_model_plugin.py +1 -3
  124. localstack/services/apigateway/resource_providers/aws_apigateway_requestvalidator.py +6 -6
  125. localstack/services/apigateway/resource_providers/aws_apigateway_requestvalidator_plugin.py +1 -3
  126. localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +6 -6
  127. localstack/services/apigateway/resource_providers/aws_apigateway_resource_plugin.py +1 -3
  128. localstack/services/apigateway/resource_providers/aws_apigateway_restapi.py +26 -26
  129. localstack/services/apigateway/resource_providers/aws_apigateway_restapi_plugin.py +1 -3
  130. localstack/services/apigateway/resource_providers/aws_apigateway_stage.py +33 -33
  131. localstack/services/apigateway/resource_providers/aws_apigateway_stage_plugin.py +1 -3
  132. localstack/services/apigateway/resource_providers/aws_apigateway_usageplan.py +18 -18
  133. localstack/services/apigateway/resource_providers/aws_apigateway_usageplan_plugin.py +1 -3
  134. localstack/services/apigateway/resource_providers/aws_apigateway_usageplankey.py +5 -5
  135. localstack/services/apigateway/resource_providers/aws_apigateway_usageplankey_plugin.py +1 -3
  136. localstack/services/cdk/resource_providers/cdk_metadata.py +4 -3
  137. localstack/services/cdk/resource_providers/cdk_metadata_plugin.py +1 -3
  138. localstack/services/certificatemanager/resource_providers/aws_certificatemanager_certificate.py +14 -14
  139. localstack/services/certificatemanager/resource_providers/aws_certificatemanager_certificate_plugin.py +1 -3
  140. localstack/services/cloudformation/api_utils.py +4 -8
  141. localstack/services/cloudformation/cfn_utils.py +2 -2
  142. localstack/services/cloudformation/deployment_utils.py +14 -12
  143. localstack/services/cloudformation/engine/changes.py +3 -3
  144. localstack/services/cloudformation/engine/entities.py +27 -17
  145. localstack/services/cloudformation/engine/parameters.py +4 -4
  146. localstack/services/cloudformation/engine/template_deployer.py +15 -14
  147. localstack/services/cloudformation/engine/template_utils.py +34 -12
  148. localstack/services/cloudformation/engine/transformers.py +11 -8
  149. localstack/services/cloudformation/engine/types.py +5 -4
  150. localstack/services/cloudformation/engine/v2/change_set_model.py +336 -39
  151. localstack/services/cloudformation/engine/v2/change_set_model_describer.py +96 -17
  152. localstack/services/cloudformation/engine/v2/change_set_model_executor.py +289 -136
  153. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +315 -146
  154. localstack/services/cloudformation/engine/v2/change_set_model_transform.py +380 -105
  155. localstack/services/cloudformation/engine/v2/change_set_model_validator.py +183 -0
  156. localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +6 -0
  157. localstack/services/cloudformation/engine/v2/resolving.py +102 -0
  158. localstack/services/cloudformation/engine/yaml_parser.py +9 -2
  159. localstack/services/cloudformation/provider.py +11 -6
  160. localstack/services/cloudformation/provider_utils.py +34 -4
  161. localstack/services/cloudformation/resource_provider.py +31 -21
  162. localstack/services/cloudformation/resource_providers/aws_cloudformation_macro.py +7 -7
  163. localstack/services/cloudformation/resource_providers/aws_cloudformation_macro_plugin.py +1 -3
  164. localstack/services/cloudformation/resource_providers/aws_cloudformation_stack.py +9 -9
  165. localstack/services/cloudformation/resource_providers/aws_cloudformation_stack_plugin.py +1 -3
  166. localstack/services/cloudformation/resource_providers/aws_cloudformation_waitcondition.py +6 -6
  167. localstack/services/cloudformation/resource_providers/aws_cloudformation_waitcondition_plugin.py +1 -3
  168. localstack/services/cloudformation/resource_providers/aws_cloudformation_waitconditionhandle.py +2 -2
  169. localstack/services/cloudformation/resource_providers/aws_cloudformation_waitconditionhandle_plugin.py +1 -3
  170. localstack/services/cloudformation/resources.py +24149 -0
  171. localstack/services/cloudformation/scaffolding/__main__.py +13 -12
  172. localstack/services/cloudformation/scaffolding/propgen.py +2 -2
  173. localstack/services/cloudformation/service_models.py +2 -2
  174. localstack/services/cloudformation/stores.py +33 -22
  175. localstack/services/cloudformation/v2/entities.py +103 -88
  176. localstack/services/cloudformation/v2/provider.py +872 -131
  177. localstack/services/cloudformation/v2/types.py +38 -0
  178. localstack/services/cloudformation/v2/utils.py +4 -1
  179. localstack/services/cloudwatch/alarm_scheduler.py +11 -8
  180. localstack/services/cloudwatch/cloudwatch_database_helper.py +4 -5
  181. localstack/services/cloudwatch/models.py +5 -7
  182. localstack/services/cloudwatch/provider.py +30 -25
  183. localstack/services/cloudwatch/provider_v2.py +27 -32
  184. localstack/services/cloudwatch/resource_providers/aws_cloudwatch_alarm.py +40 -40
  185. localstack/services/cloudwatch/resource_providers/aws_cloudwatch_alarm_plugin.py +1 -3
  186. localstack/services/cloudwatch/resource_providers/aws_cloudwatch_compositealarm.py +12 -12
  187. localstack/services/cloudwatch/resource_providers/aws_cloudwatch_compositealarm_plugin.py +1 -3
  188. localstack/services/dynamodb/packages.py +3 -3
  189. localstack/services/dynamodb/provider.py +159 -18
  190. localstack/services/dynamodb/resource_providers/aws_dynamodb_globaltable.py +63 -63
  191. localstack/services/dynamodb/resource_providers/aws_dynamodb_globaltable_plugin.py +1 -3
  192. localstack/services/dynamodb/resource_providers/aws_dynamodb_table.py +57 -57
  193. localstack/services/dynamodb/resource_providers/aws_dynamodb_table_plugin.py +1 -3
  194. localstack/services/dynamodb/server.py +2 -2
  195. localstack/services/dynamodb/utils.py +13 -14
  196. localstack/services/dynamodb/v2/provider.py +157 -18
  197. localstack/services/dynamodbstreams/dynamodbstreams_api.py +2 -2
  198. localstack/services/dynamodbstreams/models.py +1 -3
  199. localstack/services/ec2/patches.py +26 -5
  200. localstack/services/ec2/provider.py +3 -3
  201. localstack/services/ec2/resource_providers/aws_ec2_dhcpoptions.py +10 -10
  202. localstack/services/ec2/resource_providers/aws_ec2_dhcpoptions_plugin.py +1 -3
  203. localstack/services/ec2/resource_providers/aws_ec2_instance.py +96 -96
  204. localstack/services/ec2/resource_providers/aws_ec2_instance_plugin.py +1 -3
  205. localstack/services/ec2/resource_providers/aws_ec2_internetgateway.py +5 -5
  206. localstack/services/ec2/resource_providers/aws_ec2_internetgateway_plugin.py +1 -3
  207. localstack/services/ec2/resource_providers/aws_ec2_keypair.py +10 -10
  208. localstack/services/ec2/resource_providers/aws_ec2_keypair_plugin.py +1 -3
  209. localstack/services/ec2/resource_providers/aws_ec2_natgateway.py +13 -13
  210. localstack/services/ec2/resource_providers/aws_ec2_natgateway_plugin.py +1 -3
  211. localstack/services/ec2/resource_providers/aws_ec2_networkacl.py +6 -6
  212. localstack/services/ec2/resource_providers/aws_ec2_networkacl_plugin.py +1 -3
  213. localstack/services/ec2/resource_providers/aws_ec2_prefixlist.py +14 -14
  214. localstack/services/ec2/resource_providers/aws_ec2_prefixlist_plugin.py +1 -3
  215. localstack/services/ec2/resource_providers/aws_ec2_route.py +15 -15
  216. localstack/services/ec2/resource_providers/aws_ec2_route_plugin.py +1 -3
  217. localstack/services/ec2/resource_providers/aws_ec2_routetable.py +6 -6
  218. localstack/services/ec2/resource_providers/aws_ec2_routetable_plugin.py +1 -3
  219. localstack/services/ec2/resource_providers/aws_ec2_securitygroup.py +29 -29
  220. localstack/services/ec2/resource_providers/aws_ec2_securitygroup_plugin.py +1 -3
  221. localstack/services/ec2/resource_providers/aws_ec2_subnet.py +19 -19
  222. localstack/services/ec2/resource_providers/aws_ec2_subnet_plugin.py +1 -3
  223. localstack/services/ec2/resource_providers/aws_ec2_subnetroutetableassociation.py +4 -4
  224. localstack/services/ec2/resource_providers/aws_ec2_subnetroutetableassociation_plugin.py +1 -3
  225. localstack/services/ec2/resource_providers/aws_ec2_transitgateway.py +16 -16
  226. localstack/services/ec2/resource_providers/aws_ec2_transitgateway_plugin.py +1 -3
  227. localstack/services/ec2/resource_providers/aws_ec2_transitgatewayattachment.py +9 -9
  228. localstack/services/ec2/resource_providers/aws_ec2_transitgatewayattachment_plugin.py +1 -3
  229. localstack/services/ec2/resource_providers/aws_ec2_vpc.py +15 -15
  230. localstack/services/ec2/resource_providers/aws_ec2_vpc_plugin.py +1 -3
  231. localstack/services/ec2/resource_providers/aws_ec2_vpcendpoint.py +29 -22
  232. localstack/services/ec2/resource_providers/aws_ec2_vpcendpoint_plugin.py +1 -3
  233. localstack/services/ec2/resource_providers/aws_ec2_vpcgatewayattachment.py +5 -5
  234. localstack/services/ec2/resource_providers/aws_ec2_vpcgatewayattachment_plugin.py +1 -3
  235. localstack/services/ecr/resource_providers/aws_ecr_repository.py +22 -19
  236. localstack/services/ecr/resource_providers/aws_ecr_repository_plugin.py +1 -3
  237. localstack/services/edge.py +6 -6
  238. localstack/services/es/provider.py +21 -21
  239. localstack/services/events/archive.py +2 -2
  240. localstack/services/events/connection.py +5 -5
  241. localstack/services/events/event_bus.py +9 -9
  242. localstack/services/events/event_rule_engine.py +31 -13
  243. localstack/services/events/models.py +29 -30
  244. localstack/services/events/provider.py +29 -26
  245. localstack/services/events/replay.py +3 -3
  246. localstack/services/events/resource_providers/aws_events_apidestination.py +8 -8
  247. localstack/services/events/resource_providers/aws_events_apidestination_plugin.py +1 -3
  248. localstack/services/events/resource_providers/aws_events_connection.py +27 -27
  249. localstack/services/events/resource_providers/aws_events_connection_plugin.py +1 -3
  250. localstack/services/events/resource_providers/aws_events_eventbus.py +9 -9
  251. localstack/services/events/resource_providers/aws_events_eventbus_plugin.py +1 -3
  252. localstack/services/events/resource_providers/aws_events_eventbuspolicy.py +11 -11
  253. localstack/services/events/resource_providers/aws_events_eventbuspolicy_plugin.py +1 -3
  254. localstack/services/events/resource_providers/aws_events_rule.py +82 -82
  255. localstack/services/events/resource_providers/aws_events_rule_plugin.py +1 -3
  256. localstack/services/events/rule.py +15 -15
  257. localstack/services/events/target.py +21 -13
  258. localstack/services/events/utils.py +7 -7
  259. localstack/services/events/v1/models.py +1 -3
  260. localstack/services/events/v1/provider.py +15 -15
  261. localstack/services/firehose/models.py +1 -3
  262. localstack/services/firehose/provider.py +25 -16
  263. localstack/services/iam/iam_patches.py +5 -6
  264. localstack/services/iam/provider.py +14 -119
  265. localstack/services/iam/resource_providers/aws_iam_accesskey.py +6 -6
  266. localstack/services/iam/resource_providers/aws_iam_accesskey_plugin.py +1 -3
  267. localstack/services/iam/resource_providers/aws_iam_group.py +9 -9
  268. localstack/services/iam/resource_providers/aws_iam_group_plugin.py +1 -3
  269. localstack/services/iam/resource_providers/aws_iam_instanceprofile.py +5 -5
  270. localstack/services/iam/resource_providers/aws_iam_instanceprofile_plugin.py +1 -3
  271. localstack/services/iam/resource_providers/aws_iam_managedpolicy.py +9 -9
  272. localstack/services/iam/resource_providers/aws_iam_managedpolicy_plugin.py +1 -3
  273. localstack/services/iam/resource_providers/aws_iam_policy.py +7 -7
  274. localstack/services/iam/resource_providers/aws_iam_policy_plugin.py +1 -3
  275. localstack/services/iam/resource_providers/aws_iam_role.py +16 -16
  276. localstack/services/iam/resource_providers/aws_iam_role_plugin.py +1 -3
  277. localstack/services/iam/resource_providers/aws_iam_servercertificate.py +10 -10
  278. localstack/services/iam/resource_providers/aws_iam_servercertificate_plugin.py +1 -3
  279. localstack/services/iam/resource_providers/aws_iam_servicelinkedrole.py +5 -5
  280. localstack/services/iam/resource_providers/aws_iam_servicelinkedrole_plugin.py +1 -3
  281. localstack/services/iam/resource_providers/aws_iam_user.py +17 -17
  282. localstack/services/iam/resource_providers/aws_iam_user_plugin.py +1 -3
  283. localstack/services/iam/resources/policy_simulator.py +133 -0
  284. localstack/services/kinesis/kinesis_mock_server.py +7 -8
  285. localstack/services/kinesis/models.py +17 -5
  286. localstack/services/kinesis/packages.py +3 -3
  287. localstack/services/kinesis/provider.py +86 -3
  288. localstack/services/kinesis/resource_providers/aws_kinesis_stream.py +13 -13
  289. localstack/services/kinesis/resource_providers/aws_kinesis_stream_plugin.py +1 -3
  290. localstack/services/kinesis/resource_providers/aws_kinesis_streamconsumer.py +7 -7
  291. localstack/services/kinesis/resource_providers/aws_kinesis_streamconsumer_plugin.py +1 -3
  292. localstack/services/kinesisfirehose/resource_providers/aws_kinesisfirehose_deliverystream.py +191 -191
  293. localstack/services/kinesisfirehose/resource_providers/aws_kinesisfirehose_deliverystream_plugin.py +1 -3
  294. localstack/services/kms/models.py +21 -18
  295. localstack/services/kms/provider.py +62 -26
  296. localstack/services/kms/resource_providers/aws_kms_alias.py +3 -3
  297. localstack/services/kms/resource_providers/aws_kms_alias_plugin.py +1 -3
  298. localstack/services/kms/resource_providers/aws_kms_key.py +14 -14
  299. localstack/services/kms/resource_providers/aws_kms_key_plugin.py +1 -3
  300. localstack/services/kms/utils.py +4 -3
  301. localstack/services/lambda_/api_utils.py +19 -16
  302. localstack/services/lambda_/custom_endpoints.py +2 -2
  303. localstack/services/lambda_/event_source_mapping/esm_worker_factory.py +4 -7
  304. localstack/services/lambda_/event_source_mapping/pipe_utils.py +2 -2
  305. localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
  306. localstack/services/lambda_/event_source_mapping/senders/sender_utils.py +2 -1
  307. localstack/services/lambda_/hooks.py +6 -1
  308. localstack/services/lambda_/invocation/assignment.py +1 -2
  309. localstack/services/lambda_/invocation/docker_runtime_executor.py +7 -11
  310. localstack/services/lambda_/invocation/event_manager.py +1 -1
  311. localstack/services/lambda_/invocation/execution_environment.py +4 -4
  312. localstack/services/lambda_/invocation/executor_endpoint.py +8 -11
  313. localstack/services/lambda_/invocation/internal_sqs_queue.py +6 -10
  314. localstack/services/lambda_/invocation/lambda_models.py +33 -30
  315. localstack/services/lambda_/invocation/lambda_service.py +12 -5
  316. localstack/services/lambda_/invocation/logs.py +2 -3
  317. localstack/services/lambda_/invocation/runtime_executor.py +3 -3
  318. localstack/services/lambda_/invocation/version_manager.py +31 -8
  319. localstack/services/lambda_/ldm.py +14 -0
  320. localstack/services/lambda_/packages.py +3 -4
  321. localstack/services/lambda_/provider.py +9 -26
  322. localstack/services/lambda_/provider_utils.py +1 -1
  323. localstack/services/lambda_/resource_providers/aws_lambda_codesigningconfig.py +8 -8
  324. localstack/services/lambda_/resource_providers/aws_lambda_codesigningconfig_plugin.py +1 -3
  325. localstack/services/lambda_/resource_providers/aws_lambda_eventinvokeconfig.py +11 -11
  326. localstack/services/lambda_/resource_providers/aws_lambda_eventinvokeconfig_plugin.py +1 -3
  327. localstack/services/lambda_/resource_providers/aws_lambda_eventsourcemapping.py +39 -39
  328. localstack/services/lambda_/resource_providers/aws_lambda_eventsourcemapping_plugin.py +1 -3
  329. localstack/services/lambda_/resource_providers/aws_lambda_function.py +54 -54
  330. localstack/services/lambda_/resource_providers/aws_lambda_function_plugin.py +1 -3
  331. localstack/services/lambda_/resource_providers/aws_lambda_layerversion.py +11 -11
  332. localstack/services/lambda_/resource_providers/aws_lambda_layerversion_plugin.py +1 -3
  333. localstack/services/lambda_/resource_providers/aws_lambda_layerversionpermission.py +6 -6
  334. localstack/services/lambda_/resource_providers/aws_lambda_layerversionpermission_plugin.py +1 -3
  335. localstack/services/lambda_/resource_providers/aws_lambda_permission.py +10 -10
  336. localstack/services/lambda_/resource_providers/aws_lambda_permission_plugin.py +1 -3
  337. localstack/services/lambda_/resource_providers/aws_lambda_url.py +15 -15
  338. localstack/services/lambda_/resource_providers/aws_lambda_url_plugin.py +1 -3
  339. localstack/services/lambda_/resource_providers/aws_lambda_version.py +8 -8
  340. localstack/services/lambda_/resource_providers/aws_lambda_version_plugin.py +1 -3
  341. localstack/services/lambda_/resource_providers/lambda_alias.py +12 -12
  342. localstack/services/lambda_/resource_providers/lambda_alias_plugin.py +1 -3
  343. localstack/services/lambda_/runtimes.py +1 -3
  344. localstack/services/lambda_/urlrouter.py +14 -1
  345. localstack/services/logs/models.py +1 -3
  346. localstack/services/logs/provider.py +39 -22
  347. localstack/services/logs/resource_providers/aws_logs_loggroup.py +9 -9
  348. localstack/services/logs/resource_providers/aws_logs_loggroup_plugin.py +1 -3
  349. localstack/services/logs/resource_providers/aws_logs_logstream.py +4 -4
  350. localstack/services/logs/resource_providers/aws_logs_logstream_plugin.py +1 -3
  351. localstack/services/logs/resource_providers/aws_logs_subscriptionfilter.py +7 -7
  352. localstack/services/logs/resource_providers/aws_logs_subscriptionfilter_plugin.py +1 -3
  353. localstack/services/moto.py +5 -4
  354. localstack/services/opensearch/cluster.py +28 -20
  355. localstack/services/opensearch/cluster_manager.py +13 -14
  356. localstack/services/opensearch/models.py +1 -3
  357. localstack/services/opensearch/packages.py +27 -9
  358. localstack/services/opensearch/provider.py +15 -10
  359. localstack/services/opensearch/resource_providers/aws_elasticsearch_domain.py +61 -61
  360. localstack/services/opensearch/resource_providers/aws_elasticsearch_domain_plugin.py +1 -3
  361. localstack/services/opensearch/resource_providers/aws_opensearchservice_domain.py +89 -89
  362. localstack/services/opensearch/resource_providers/aws_opensearchservice_domain_plugin.py +1 -3
  363. localstack/services/opensearch/versions.py +57 -10
  364. localstack/services/plugins.py +37 -32
  365. localstack/services/providers.py +10 -2
  366. localstack/services/redshift/provider.py +0 -21
  367. localstack/services/redshift/resource_providers/aws_redshift_cluster.py +57 -57
  368. localstack/services/redshift/resource_providers/aws_redshift_cluster_plugin.py +1 -3
  369. localstack/services/resource_groups/resource_providers/aws_resourcegroups_group.py +21 -21
  370. localstack/services/resource_groups/resource_providers/aws_resourcegroups_group_plugin.py +1 -3
  371. localstack/services/route53/models.py +1 -3
  372. localstack/services/route53/provider.py +1 -2
  373. localstack/services/route53/resource_providers/aws_route53_healthcheck.py +6 -6
  374. localstack/services/route53/resource_providers/aws_route53_healthcheck_plugin.py +1 -3
  375. localstack/services/route53/resource_providers/aws_route53_recordset.py +27 -27
  376. localstack/services/route53/resource_providers/aws_route53_recordset_plugin.py +1 -3
  377. localstack/services/route53resolver/models.py +8 -10
  378. localstack/services/route53resolver/provider.py +12 -12
  379. localstack/services/s3/codec.py +2 -2
  380. localstack/services/s3/constants.py +5 -2
  381. localstack/services/s3/cors.py +8 -8
  382. localstack/services/s3/models.py +73 -73
  383. localstack/services/s3/notifications.py +74 -58
  384. localstack/services/s3/presigned_url.py +41 -59
  385. localstack/services/s3/provider.py +86 -29
  386. localstack/services/s3/resource_providers/aws_s3_bucket.py +180 -180
  387. localstack/services/s3/resource_providers/aws_s3_bucket_plugin.py +1 -3
  388. localstack/services/s3/resource_providers/aws_s3_bucketpolicy.py +4 -4
  389. localstack/services/s3/resource_providers/aws_s3_bucketpolicy_plugin.py +1 -3
  390. localstack/services/s3/storage/core.py +4 -3
  391. localstack/services/s3/storage/ephemeral.py +7 -6
  392. localstack/services/s3/utils.py +51 -31
  393. localstack/services/s3/validation.py +47 -33
  394. localstack/services/s3/website_hosting.py +9 -7
  395. localstack/services/scheduler/resource_providers/aws_scheduler_schedule.py +60 -60
  396. localstack/services/scheduler/resource_providers/aws_scheduler_schedule_plugin.py +1 -3
  397. localstack/services/scheduler/resource_providers/aws_scheduler_schedulegroup.py +9 -9
  398. localstack/services/scheduler/resource_providers/aws_scheduler_schedulegroup_plugin.py +1 -3
  399. localstack/services/secretsmanager/provider.py +10 -12
  400. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_resourcepolicy.py +5 -5
  401. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_resourcepolicy_plugin.py +1 -3
  402. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_rotationschedule.py +21 -21
  403. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_rotationschedule_plugin.py +1 -3
  404. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secret.py +23 -23
  405. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secret_plugin.py +1 -3
  406. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secrettargetattachment.py +5 -5
  407. localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secrettargetattachment_plugin.py +1 -3
  408. localstack/services/ses/models.py +8 -1
  409. localstack/services/ses/provider.py +128 -55
  410. localstack/services/ses/resource_providers/aws_ses_emailidentity.py +21 -21
  411. localstack/services/ses/resource_providers/aws_ses_emailidentity_plugin.py +1 -3
  412. localstack/services/sns/constants.py +7 -1
  413. localstack/services/sns/executor.py +9 -2
  414. localstack/services/sns/models.py +25 -25
  415. localstack/services/sns/provider.py +9 -7
  416. localstack/services/sns/publisher.py +37 -23
  417. localstack/services/sns/resource_providers/aws_sns_subscription.py +13 -13
  418. localstack/services/sns/resource_providers/aws_sns_subscription_plugin.py +1 -3
  419. localstack/services/sns/resource_providers/aws_sns_topic.py +16 -16
  420. localstack/services/sns/resource_providers/aws_sns_topic_plugin.py +1 -3
  421. localstack/services/sns/resource_providers/aws_sns_topicpolicy.py +4 -4
  422. localstack/services/sns/resource_providers/aws_sns_topicpolicy_plugin.py +1 -3
  423. localstack/services/sns/v2/models.py +167 -0
  424. localstack/services/sns/v2/provider.py +867 -0
  425. localstack/services/sns/v2/utils.py +130 -0
  426. localstack/services/sqs/constants.py +2 -2
  427. localstack/services/sqs/developer_api.py +205 -0
  428. localstack/services/sqs/models.py +71 -36
  429. localstack/services/sqs/provider.py +56 -332
  430. localstack/services/sqs/query_api.py +8 -5
  431. localstack/services/sqs/resource_providers/aws_sqs_queue.py +21 -21
  432. localstack/services/sqs/resource_providers/aws_sqs_queue_plugin.py +1 -3
  433. localstack/services/sqs/resource_providers/aws_sqs_queuepolicy.py +7 -5
  434. localstack/services/sqs/resource_providers/aws_sqs_queuepolicy_plugin.py +1 -3
  435. localstack/services/sqs/utils.py +123 -4
  436. localstack/services/ssm/provider.py +3 -4
  437. localstack/services/ssm/resource_providers/aws_ssm_maintenancewindow.py +15 -15
  438. localstack/services/ssm/resource_providers/aws_ssm_maintenancewindow_plugin.py +1 -3
  439. localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtarget.py +10 -10
  440. localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtarget_plugin.py +1 -3
  441. localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtask.py +48 -48
  442. localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtask_plugin.py +1 -3
  443. localstack/services/ssm/resource_providers/aws_ssm_parameter.py +10 -10
  444. localstack/services/ssm/resource_providers/aws_ssm_parameter_plugin.py +1 -3
  445. localstack/services/ssm/resource_providers/aws_ssm_patchbaseline.py +29 -29
  446. localstack/services/ssm/resource_providers/aws_ssm_patchbaseline_plugin.py +1 -3
  447. localstack/services/stepfunctions/asl/antlt4utils/antlr4utils.py +3 -4
  448. localstack/services/stepfunctions/asl/component/common/assign/assign_decl.py +1 -1
  449. localstack/services/stepfunctions/asl/component/common/assign/assign_decl_binding.py +1 -1
  450. localstack/services/stepfunctions/asl/component/common/assign/assign_template_value_array.py +1 -1
  451. localstack/services/stepfunctions/asl/component/common/assign/assign_template_value_object.py +1 -1
  452. localstack/services/stepfunctions/asl/component/common/catch/catcher_decl.py +9 -9
  453. localstack/services/stepfunctions/asl/component/common/error_name/custom_error_name.py +2 -2
  454. localstack/services/stepfunctions/asl/component/common/error_name/error_name.py +4 -4
  455. localstack/services/stepfunctions/asl/component/common/error_name/failure_event.py +8 -8
  456. localstack/services/stepfunctions/asl/component/common/error_name/states_error_name_type.py +1 -1
  457. localstack/services/stepfunctions/asl/component/common/jsonata/jsonata_template_binding.py +2 -2
  458. localstack/services/stepfunctions/asl/component/common/jsonata/jsonata_template_value_array.py +1 -1
  459. localstack/services/stepfunctions/asl/component/common/jsonata/jsonata_template_value_object.py +1 -1
  460. localstack/services/stepfunctions/asl/component/common/path/input_path.py +4 -4
  461. localstack/services/stepfunctions/asl/component/common/path/output_path.py +4 -4
  462. localstack/services/stepfunctions/asl/component/common/path/result_path.py +3 -3
  463. localstack/services/stepfunctions/asl/component/common/payload/payloadvalue/payloadarr/payload_arr.py +1 -1
  464. localstack/services/stepfunctions/asl/component/common/payload/payloadvalue/payloadbinding/payload_binding.py +3 -3
  465. localstack/services/stepfunctions/asl/component/common/payload/payloadvalue/payloadtmpl/payload_tmpl.py +1 -1
  466. localstack/services/stepfunctions/asl/component/common/retry/retrier_decl.py +8 -8
  467. localstack/services/stepfunctions/asl/component/common/string/string_expression.py +3 -3
  468. localstack/services/stepfunctions/asl/component/common/timeouts/timeout.py +3 -3
  469. localstack/services/stepfunctions/asl/component/eval_component.py +2 -3
  470. localstack/services/stepfunctions/asl/component/intrinsic/argument/argument.py +4 -4
  471. localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/array/array_partition.py +1 -1
  472. localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/generic/string_format.py +1 -1
  473. localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/states_function_array.py +1 -1
  474. localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/states_function_format.py +1 -1
  475. localstack/services/stepfunctions/asl/component/intrinsic/jsonata.py +3 -3
  476. localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
  477. localstack/services/stepfunctions/asl/component/program/program.py +9 -9
  478. localstack/services/stepfunctions/asl/component/program/states.py +1 -1
  479. localstack/services/stepfunctions/asl/component/state/state.py +10 -11
  480. localstack/services/stepfunctions/asl/component/state/state_choice/choice_rule.py +11 -11
  481. localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
  482. localstack/services/stepfunctions/asl/component/state/state_choice/comparison/operator/implementations/is_operator.py +2 -2
  483. localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +3 -5
  484. localstack/services/stepfunctions/asl/component/state/state_execution/execute_state.py +9 -9
  485. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/item_reader_decl.py +7 -7
  486. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/reader_config/reader_config_decl.py +5 -5
  487. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/resource_eval/resource_eval_s3.py +2 -1
  488. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/resource_eval/resource_output_transformer/resource_output_transformer_csv.py +2 -2
  489. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/distributed_iteration_component.py +8 -8
  490. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/inline_iteration_component.py +7 -7
  491. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/itemprocessor/distributed_item_processor_worker.py +4 -4
  492. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/itemprocessor/inline_item_processor_worker.py +5 -5
  493. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/itemprocessor/map_run_record.py +10 -10
  494. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iteration_component.py +3 -3
  495. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iteration_declaration.py +3 -3
  496. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iteration_worker.py +2 -2
  497. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iterator/distributed_iterator_worker.py +3 -5
  498. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iterator/inline_iterator.py +1 -2
  499. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iterator/inline_iterator_worker.py +5 -5
  500. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/job.py +9 -9
  501. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/result_writer/resource_eval/resource_eval_s3.py +2 -1
  502. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +12 -13
  503. localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/branch_worker.py +3 -3
  504. localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/branches_decl.py +7 -7
  505. localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +2 -3
  506. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/lambda_eval_utils.py +7 -7
  507. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/resource.py +3 -3
  508. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service.py +10 -10
  509. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_api_gateway.py +18 -18
  510. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_aws_sdk.py +1 -1
  511. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_batch.py +8 -7
  512. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_callback.py +18 -17
  513. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_dynamodb.py +2 -2
  514. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_ecs.py +5 -4
  515. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_events.py +4 -4
  516. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_glue.py +7 -6
  517. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_lambda.py +4 -4
  518. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_sfn.py +9 -8
  519. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_sns.py +2 -2
  520. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_sqs.py +4 -4
  521. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +5 -5
  522. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task_lambda.py +2 -2
  523. localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +3 -5
  524. localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +5 -7
  525. localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
  526. localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
  527. localstack/services/stepfunctions/asl/component/state/state_wait/wait_function/timestamp.py +2 -2
  528. localstack/services/stepfunctions/asl/eval/callback/callback.py +18 -18
  529. localstack/services/stepfunctions/asl/eval/environment.py +22 -24
  530. localstack/services/stepfunctions/asl/eval/evaluation_details.py +3 -5
  531. localstack/services/stepfunctions/asl/eval/event/event_manager.py +10 -10
  532. localstack/services/stepfunctions/asl/eval/event/logging.py +3 -3
  533. localstack/services/stepfunctions/asl/eval/program_state.py +8 -8
  534. localstack/services/stepfunctions/asl/eval/states.py +12 -12
  535. localstack/services/stepfunctions/asl/eval/test_state/environment.py +3 -5
  536. localstack/services/stepfunctions/asl/eval/variable_store.py +6 -6
  537. localstack/services/stepfunctions/asl/jsonata/jsonata.py +7 -6
  538. localstack/services/stepfunctions/asl/parse/asl_parser.py +1 -1
  539. localstack/services/stepfunctions/asl/parse/intrinsic/preprocessor.py +2 -3
  540. localstack/services/stepfunctions/asl/parse/preprocessor.py +44 -44
  541. localstack/services/stepfunctions/asl/parse/typed_props.py +2 -2
  542. localstack/services/stepfunctions/asl/static_analyser/intrinsic/variable_names_intrinsic_static_analyser.py +1 -1
  543. localstack/services/stepfunctions/asl/static_analyser/variable_references_static_analyser.py +2 -2
  544. localstack/services/stepfunctions/asl/utils/encoding.py +2 -2
  545. localstack/services/stepfunctions/asl/utils/json_path.py +2 -2
  546. localstack/services/stepfunctions/backend/activity.py +4 -4
  547. localstack/services/stepfunctions/backend/alias.py +8 -8
  548. localstack/services/stepfunctions/backend/execution.py +29 -30
  549. localstack/services/stepfunctions/backend/execution_worker.py +7 -7
  550. localstack/services/stepfunctions/backend/state_machine.py +28 -28
  551. localstack/services/stepfunctions/backend/test_state/execution.py +3 -4
  552. localstack/services/stepfunctions/backend/test_state/execution_worker.py +1 -3
  553. localstack/services/stepfunctions/mocking/mock_config.py +9 -9
  554. localstack/services/stepfunctions/mocking/mock_config_file.py +10 -10
  555. localstack/services/stepfunctions/packages.py +14 -5
  556. localstack/services/stepfunctions/provider.py +34 -44
  557. localstack/services/stepfunctions/quotas.py +2 -3
  558. localstack/services/stepfunctions/resource_providers/aws_stepfunctions_activity.py +6 -6
  559. localstack/services/stepfunctions/resource_providers/aws_stepfunctions_activity_plugin.py +1 -3
  560. localstack/services/stepfunctions/resource_providers/aws_stepfunctions_statemachine.py +25 -25
  561. localstack/services/stepfunctions/resource_providers/aws_stepfunctions_statemachine_plugin.py +1 -3
  562. localstack/services/stepfunctions/stepfunctions_utils.py +1 -2
  563. localstack/services/stores.py +8 -8
  564. localstack/services/transcribe/packages.py +1 -3
  565. localstack/services/transcribe/provider.py +8 -3
  566. localstack/state/codecs.py +61 -0
  567. localstack/state/core.py +11 -5
  568. localstack/state/inspect.py +4 -4
  569. localstack/state/pickle.py +36 -23
  570. localstack/testing/aws/asf_utils.py +3 -2
  571. localstack/testing/aws/cloudformation_utils.py +1 -1
  572. localstack/testing/aws/lambda_utils.py +15 -14
  573. localstack/testing/aws/util.py +3 -2
  574. localstack/testing/pytest/cloudformation/fixtures.py +68 -18
  575. localstack/testing/pytest/container.py +5 -5
  576. localstack/testing/pytest/filters.py +1 -3
  577. localstack/testing/pytest/fixtures.py +188 -49
  578. localstack/testing/pytest/in_memory_localstack.py +1 -3
  579. localstack/testing/pytest/marking.py +42 -15
  580. localstack/testing/pytest/path_filter.py +1 -1
  581. localstack/testing/pytest/stepfunctions/fixtures.py +4 -4
  582. localstack/testing/pytest/stepfunctions/utils.py +11 -10
  583. localstack/testing/pytest/util.py +1 -1
  584. localstack/testing/pytest/validation_tracking.py +3 -4
  585. localstack/testing/scenario/provisioning.py +11 -10
  586. localstack/testing/snapshots/transformer_utility.py +8 -3
  587. localstack/testing/testselection/matching.py +2 -2
  588. localstack/testing/testselection/opt_out.py +1 -1
  589. localstack/testing/testselection/scripts/filter_by_test_selection.py +1 -1
  590. localstack/testing/testselection/scripts/generate_test_selection.py +1 -1
  591. localstack/testing/testselection/testselection.py +2 -2
  592. localstack/utils/analytics/cli.py +2 -3
  593. localstack/utils/analytics/client.py +5 -5
  594. localstack/utils/analytics/events.py +2 -2
  595. localstack/utils/analytics/metadata.py +6 -4
  596. localstack/utils/analytics/metrics/counter.py +11 -18
  597. localstack/utils/analytics/metrics/registry.py +2 -2
  598. localstack/utils/analytics/publisher.py +4 -5
  599. localstack/utils/analytics/service_providers.py +19 -0
  600. localstack/utils/analytics/service_request_aggregator.py +4 -4
  601. localstack/utils/archives.py +12 -12
  602. localstack/utils/asyncio.py +2 -2
  603. localstack/utils/aws/arns.py +26 -31
  604. localstack/utils/aws/aws_responses.py +21 -28
  605. localstack/utils/aws/aws_stack.py +7 -12
  606. localstack/utils/aws/dead_letter_queue.py +4 -9
  607. localstack/utils/aws/message_forwarding.py +8 -11
  608. localstack/utils/aws/request_context.py +5 -6
  609. localstack/utils/aws/resources.py +1 -1
  610. localstack/utils/aws/templating.py +4 -4
  611. localstack/utils/batch_policy.py +4 -4
  612. localstack/utils/bootstrap.py +37 -30
  613. localstack/utils/catalog/catalog.py +139 -0
  614. localstack/utils/catalog/catalog_loader.py +119 -0
  615. localstack/utils/catalog/common.py +58 -0
  616. localstack/utils/catalog/plugins.py +28 -0
  617. localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
  618. localstack/utils/collections.py +33 -27
  619. localstack/utils/config_listener.py +2 -2
  620. localstack/utils/container_networking.py +5 -6
  621. localstack/utils/container_utils/container_client.py +156 -160
  622. localstack/utils/container_utils/docker_cmd_client.py +97 -81
  623. localstack/utils/container_utils/docker_sdk_client.py +75 -72
  624. localstack/utils/crypto.py +12 -13
  625. localstack/utils/diagnose.py +11 -12
  626. localstack/utils/docker_utils.py +11 -7
  627. localstack/utils/files.py +34 -15
  628. localstack/utils/functions.py +5 -4
  629. localstack/utils/http.py +14 -14
  630. localstack/utils/iputils.py +2 -1
  631. localstack/utils/json.py +21 -7
  632. localstack/utils/kinesis/kinesis_connector.py +2 -1
  633. localstack/utils/net.py +25 -17
  634. localstack/utils/no_exit_argument_parser.py +2 -2
  635. localstack/utils/numbers.py +9 -2
  636. localstack/utils/objects.py +15 -14
  637. localstack/utils/patch.py +14 -7
  638. localstack/utils/platform.py +2 -2
  639. localstack/utils/run.py +15 -14
  640. localstack/utils/scheduler.py +13 -12
  641. localstack/utils/server/tcp_proxy.py +2 -2
  642. localstack/utils/serving.py +3 -4
  643. localstack/utils/strings.py +15 -16
  644. localstack/utils/sync.py +126 -1
  645. localstack/utils/tagging.py +10 -8
  646. localstack/utils/testutil.py +17 -17
  647. localstack/utils/threads.py +3 -3
  648. localstack/utils/time.py +12 -4
  649. localstack/utils/urls.py +1 -3
  650. localstack/utils/xml.py +1 -1
  651. localstack/utils/xray/traceid.py +1 -1
  652. localstack/version.py +16 -3
  653. {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -13
  654. {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +663 -655
  655. {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
  656. localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
  657. localstack/packages/terraform.py +0 -47
  658. localstack/services/cloudformation/deploy.html +0 -144
  659. localstack/services/cloudformation/deploy_ui.py +0 -47
  660. localstack/services/cloudformation/plugins.py +0 -12
  661. localstack/services/lambda_/lambda_debug_mode/ldm.py +0 -375
  662. localstack/services/lambda_/lambda_debug_mode/ldm_config_file.py +0 -178
  663. localstack/services/lambda_/lambda_debug_mode/ldm_types.py +0 -11
  664. localstack/services/lambda_/lambda_debug_mode/ldm_utils.py +0 -43
  665. localstack_core-4.6.1.dev60.dist-info/plux.json +0 -1
  666. /localstack/{services/lambda_/lambda_debug_mode/__init__.py → testing/pytest/cloudformation/transformers.py} +0 -0
  667. {localstack_core-4.6.1.dev60.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
  668. {localstack_core-4.6.1.dev60.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
  669. {localstack_core-4.6.1.dev60.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
  670. {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
  671. {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
  672. {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,867 @@
1
+ import contextlib
2
+ import copy
3
+ import json
4
+ import logging
5
+ import re
6
+
7
+ from botocore.utils import InvalidArnException
8
+
9
+ from localstack.aws.api import CommonServiceException, RequestContext
10
+ from localstack.aws.api.sns import (
11
+ AmazonResourceName,
12
+ ConfirmSubscriptionResponse,
13
+ CreatePlatformApplicationResponse,
14
+ CreateTopicResponse,
15
+ GetPlatformApplicationAttributesResponse,
16
+ GetSMSAttributesResponse,
17
+ GetSubscriptionAttributesResponse,
18
+ GetTopicAttributesResponse,
19
+ InvalidParameterException,
20
+ ListEndpointsByPlatformApplicationResponse,
21
+ ListPlatformApplicationsResponse,
22
+ ListString,
23
+ ListSubscriptionsByTopicResponse,
24
+ ListSubscriptionsResponse,
25
+ ListTagsForResourceResponse,
26
+ ListTopicsResponse,
27
+ MapStringToString,
28
+ NotFoundException,
29
+ PlatformApplication,
30
+ SetSMSAttributesResponse,
31
+ SnsApi,
32
+ String,
33
+ SubscribeResponse,
34
+ Subscription,
35
+ SubscriptionAttributesMap,
36
+ TagKeyList,
37
+ TagList,
38
+ TagResourceResponse,
39
+ TopicAttributesMap,
40
+ UntagResourceResponse,
41
+ attributeName,
42
+ attributeValue,
43
+ authenticateOnUnsubscribe,
44
+ endpoint,
45
+ nextToken,
46
+ protocol,
47
+ subscriptionARN,
48
+ topicARN,
49
+ topicName,
50
+ )
51
+ from localstack.services.sns import constants as sns_constants
52
+ from localstack.services.sns.certificate import SNS_SERVER_CERT
53
+ from localstack.services.sns.constants import (
54
+ DUMMY_SUBSCRIPTION_PRINCIPAL,
55
+ VALID_APPLICATION_PLATFORMS,
56
+ )
57
+ from localstack.services.sns.filter import FilterPolicyValidator
58
+ from localstack.services.sns.publisher import PublishDispatcher, SnsPublishContext
59
+ from localstack.services.sns.v2.models import (
60
+ SMS_ATTRIBUTE_NAMES,
61
+ SMS_DEFAULT_SENDER_REGEX,
62
+ SMS_TYPES,
63
+ SnsMessage,
64
+ SnsMessageType,
65
+ SnsStore,
66
+ SnsSubscription,
67
+ Topic,
68
+ sns_stores,
69
+ )
70
+ from localstack.services.sns.v2.utils import (
71
+ create_subscription_arn,
72
+ encode_subscription_token_with_region,
73
+ get_next_page_token_from_arn,
74
+ get_region_from_subscription_token,
75
+ is_valid_e164_number,
76
+ parse_and_validate_platform_application_arn,
77
+ parse_and_validate_topic_arn,
78
+ validate_subscription_attribute,
79
+ )
80
+ from localstack.utils.aws.arns import (
81
+ get_partition,
82
+ parse_arn,
83
+ sns_platform_application_arn,
84
+ sns_topic_arn,
85
+ )
86
+ from localstack.utils.collections import PaginatedList, select_from_typed_dict
87
+
88
+ # set up logger
89
+ LOG = logging.getLogger(__name__)
90
+
91
+ SNS_TOPIC_NAME_PATTERN_FIFO = r"^[a-zA-Z0-9_-]{1,256}\.fifo$"
92
+ SNS_TOPIC_NAME_PATTERN = r"^[a-zA-Z0-9_-]{1,256}$"
93
+
94
+
95
+ class SnsProvider(SnsApi):
96
+ def __init__(self) -> None:
97
+ super().__init__()
98
+ self._publisher = PublishDispatcher()
99
+ self._signature_cert_pem: str = SNS_SERVER_CERT
100
+
101
+ ## Topic Operations
102
+
103
+ def create_topic(
104
+ self,
105
+ context: RequestContext,
106
+ name: topicName,
107
+ attributes: TopicAttributesMap | None = None,
108
+ tags: TagList | None = None,
109
+ data_protection_policy: attributeValue | None = None,
110
+ **kwargs,
111
+ ) -> CreateTopicResponse:
112
+ store = self.get_store(context.account_id, context.region)
113
+ topic_arn = sns_topic_arn(
114
+ topic_name=name, region_name=context.region, account_id=context.account_id
115
+ )
116
+ topic: Topic = store.topics.get(topic_arn)
117
+ attributes = attributes or {}
118
+ if topic:
119
+ attrs = topic["attributes"]
120
+ for k, v in attributes.values():
121
+ if not attrs.get(k) or not attrs.get(k) == v:
122
+ # TODO:
123
+ raise InvalidParameterException("Fix this Exception message and type")
124
+ tag_resource_success = _check_matching_tags(topic_arn, tags, store)
125
+ if not tag_resource_success:
126
+ raise InvalidParameterException(
127
+ "Invalid parameter: Tags Reason: Topic already exists with different tags"
128
+ )
129
+ return CreateTopicResponse(TopicArn=topic_arn)
130
+
131
+ attributes = attributes or {}
132
+ if attributes.get("FifoTopic") and attributes["FifoTopic"].lower() == "true":
133
+ fifo_match = re.match(SNS_TOPIC_NAME_PATTERN_FIFO, name)
134
+ if not fifo_match:
135
+ # TODO: check this with a separate test
136
+ raise InvalidParameterException(
137
+ "Fifo Topic names must end with .fifo and must be made up of only uppercase and lowercase ASCII letters, numbers, underscores, and hyphens, and must be between 1 and 256 characters long."
138
+ )
139
+ else:
140
+ # AWS does not seem to save explicit settings of fifo = false
141
+
142
+ attributes.pop("FifoTopic", None)
143
+ name_match = re.match(SNS_TOPIC_NAME_PATTERN, name)
144
+ if not name_match:
145
+ raise InvalidParameterException("Invalid parameter: Topic Name")
146
+
147
+ topic = _create_topic(name=name, attributes=attributes, context=context)
148
+ if tags:
149
+ self.tag_resource(context=context, resource_arn=topic_arn, tags=tags)
150
+
151
+ store.topics[topic_arn] = topic
152
+
153
+ return CreateTopicResponse(TopicArn=topic_arn)
154
+
155
+ def get_topic_attributes(
156
+ self, context: RequestContext, topic_arn: topicARN, **kwargs
157
+ ) -> GetTopicAttributesResponse:
158
+ topic: Topic = self._get_topic(arn=topic_arn, context=context)
159
+ if topic:
160
+ attributes = topic["attributes"]
161
+ return GetTopicAttributesResponse(Attributes=attributes)
162
+ else:
163
+ raise NotFoundException("Topic does not exist")
164
+
165
+ def delete_topic(self, context: RequestContext, topic_arn: topicARN, **kwargs) -> None:
166
+ store = self.get_store(context.account_id, context.region)
167
+
168
+ store.topics.pop(topic_arn, None)
169
+
170
+ def list_topics(
171
+ self, context: RequestContext, next_token: nextToken | None = None, **kwargs
172
+ ) -> ListTopicsResponse:
173
+ store = self.get_store(context.account_id, context.region)
174
+ topics = [{"TopicArn": t["arn"]} for t in list(store.topics.values())]
175
+ topics = PaginatedList(topics)
176
+ page, nxt = topics.get_page(
177
+ token_generator=lambda x: get_next_page_token_from_arn(x["TopicArn"]),
178
+ next_token=next_token,
179
+ page_size=100,
180
+ )
181
+ topics = {"Topics": page, "NextToken": nxt}
182
+ return ListTopicsResponse(**topics)
183
+
184
+ def set_topic_attributes(
185
+ self,
186
+ context: RequestContext,
187
+ topic_arn: topicARN,
188
+ attribute_name: attributeName,
189
+ attribute_value: attributeValue | None = None,
190
+ **kwargs,
191
+ ) -> None:
192
+ topic: Topic = self._get_topic(arn=topic_arn, context=context)
193
+ if attribute_name == "FifoTopic":
194
+ raise InvalidParameterException("Invalid parameter: AttributeName")
195
+ topic["attributes"][attribute_name] = attribute_value
196
+
197
+ ## Subscribe operations
198
+
199
+ def subscribe(
200
+ self,
201
+ context: RequestContext,
202
+ topic_arn: topicARN,
203
+ protocol: protocol,
204
+ endpoint: endpoint | None = None,
205
+ attributes: SubscriptionAttributesMap | None = None,
206
+ return_subscription_arn: bool | None = None,
207
+ **kwargs,
208
+ ) -> SubscribeResponse:
209
+ parsed_topic_arn = parse_and_validate_topic_arn(topic_arn)
210
+ if context.region != parsed_topic_arn["region"]:
211
+ raise InvalidParameterException("Invalid parameter: TopicArn")
212
+
213
+ store = self.get_store(account_id=parsed_topic_arn["account"], region=context.region)
214
+
215
+ if topic_arn not in store.topics:
216
+ raise NotFoundException("Topic does not exist")
217
+
218
+ topic_subscriptions = store.topics[topic_arn]["subscriptions"]
219
+ if not endpoint:
220
+ # TODO: check AWS behaviour (because endpoint is optional)
221
+ raise NotFoundException("Endpoint not specified in subscription")
222
+ if protocol not in sns_constants.SNS_PROTOCOLS:
223
+ raise InvalidParameterException(
224
+ f"Invalid parameter: Amazon SNS does not support this protocol string: {protocol}"
225
+ )
226
+ elif protocol in ["http", "https"] and not endpoint.startswith(f"{protocol}://"):
227
+ raise InvalidParameterException(
228
+ "Invalid parameter: Endpoint must match the specified protocol"
229
+ )
230
+ elif protocol == "sms" and not is_valid_e164_number(endpoint):
231
+ raise InvalidParameterException(f"Invalid SMS endpoint: {endpoint}")
232
+
233
+ elif protocol == "sqs":
234
+ try:
235
+ parse_arn(endpoint)
236
+ except InvalidArnException:
237
+ raise InvalidParameterException("Invalid parameter: SQS endpoint ARN")
238
+
239
+ elif protocol == "application":
240
+ # TODO: This needs to be implemented once applications are ported from moto to the new provider
241
+ raise NotImplementedError(
242
+ "This functionality needs yet to be ported to the new SNS provider"
243
+ )
244
+
245
+ if ".fifo" in endpoint and ".fifo" not in topic_arn:
246
+ # TODO: move to sqs protocol block if possible
247
+ raise InvalidParameterException(
248
+ "Invalid parameter: Invalid parameter: Endpoint Reason: FIFO SQS Queues can not be subscribed to standard SNS topics"
249
+ )
250
+
251
+ sub_attributes = copy.deepcopy(attributes) if attributes else None
252
+ if sub_attributes:
253
+ for attr_name, attr_value in sub_attributes.items():
254
+ validate_subscription_attribute(
255
+ attribute_name=attr_name,
256
+ attribute_value=attr_value,
257
+ topic_arn=topic_arn,
258
+ endpoint=endpoint,
259
+ is_subscribe_call=True,
260
+ )
261
+ if raw_msg_delivery := sub_attributes.get("RawMessageDelivery"):
262
+ sub_attributes["RawMessageDelivery"] = raw_msg_delivery.lower()
263
+
264
+ # An endpoint may only be subscribed to a topic once. Subsequent
265
+ # subscribe calls do nothing (subscribe is idempotent), except if its attributes are different.
266
+ for existing_topic_subscription in topic_subscriptions:
267
+ sub = store.subscriptions.get(existing_topic_subscription, {})
268
+ if sub.get("Endpoint") == endpoint:
269
+ if sub_attributes:
270
+ # validate the subscription attributes aren't different
271
+ for attr in sns_constants.VALID_SUBSCRIPTION_ATTR_NAME:
272
+ # if a new attribute is present and different from an existent one, raise
273
+ if (new_attr := sub_attributes.get(attr)) and sub.get(attr) != new_attr:
274
+ raise InvalidParameterException(
275
+ "Invalid parameter: Attributes Reason: Subscription already exists with different attributes"
276
+ )
277
+
278
+ return SubscribeResponse(SubscriptionArn=sub["SubscriptionArn"])
279
+ principal = DUMMY_SUBSCRIPTION_PRINCIPAL.format(
280
+ partition=get_partition(context.region), account_id=context.account_id
281
+ )
282
+ subscription_arn = create_subscription_arn(topic_arn)
283
+ subscription = SnsSubscription(
284
+ # http://docs.aws.amazon.com/cli/latest/reference/sns/get-subscription-attributes.html
285
+ TopicArn=topic_arn,
286
+ Endpoint=endpoint,
287
+ Protocol=protocol,
288
+ SubscriptionArn=subscription_arn,
289
+ PendingConfirmation="true",
290
+ Owner=context.account_id,
291
+ RawMessageDelivery="false", # default value, will be overridden if set
292
+ FilterPolicyScope="MessageAttributes", # default value, will be overridden if set
293
+ SubscriptionPrincipal=principal, # dummy value, could be fetched with a call to STS?
294
+ )
295
+ if sub_attributes:
296
+ subscription.update(sub_attributes)
297
+ if "FilterPolicy" in sub_attributes:
298
+ filter_policy = (
299
+ json.loads(sub_attributes["FilterPolicy"])
300
+ if sub_attributes["FilterPolicy"]
301
+ else None
302
+ )
303
+ if filter_policy:
304
+ validator = FilterPolicyValidator(
305
+ scope=subscription.get("FilterPolicyScope", "MessageAttributes"),
306
+ is_subscribe_call=True,
307
+ )
308
+ validator.validate_filter_policy(filter_policy)
309
+
310
+ store.subscription_filter_policy[subscription_arn] = filter_policy
311
+
312
+ store.subscriptions[subscription_arn] = subscription
313
+
314
+ topic_subscriptions.append(subscription_arn)
315
+
316
+ # store the token and subscription arn
317
+ # TODO: the token is a 288 hex char string
318
+ subscription_token = encode_subscription_token_with_region(region=context.region)
319
+ store.subscription_tokens[subscription_token] = subscription_arn
320
+
321
+ response_subscription_arn = subscription_arn
322
+ # Send out confirmation message for HTTP(S), fix for https://github.com/localstack/localstack/issues/881
323
+ if protocol in ["http", "https"]:
324
+ message_ctx = SnsMessage(
325
+ type=SnsMessageType.SubscriptionConfirmation,
326
+ token=subscription_token,
327
+ message=f"You have chosen to subscribe to the topic {topic_arn}.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
328
+ )
329
+ publish_ctx = SnsPublishContext(
330
+ message=message_ctx,
331
+ store=store,
332
+ request_headers=context.request.headers,
333
+ # TODO: add topic attributes once they are ported from moto to LocalStack
334
+ # topic_attributes=vars(self._get_topic(topic_arn, context)),
335
+ )
336
+ self._publisher.publish_to_topic_subscriber(
337
+ ctx=publish_ctx,
338
+ topic_arn=topic_arn,
339
+ subscription_arn=subscription_arn,
340
+ )
341
+ if not return_subscription_arn:
342
+ response_subscription_arn = "pending confirmation"
343
+
344
+ elif protocol not in ["email", "email-json"]:
345
+ # Only HTTP(S) and email subscriptions are not auto validated
346
+ # Except if the endpoint and the topic are not in the same AWS account, then you'd need to manually confirm
347
+ # the subscription with the token
348
+ # TODO: revisit for multi-account
349
+ # TODO: test with AWS for email & email-json confirmation message
350
+ # we need to add the following check:
351
+ # if parsed_topic_arn["account"] == endpoint account (depending on the type, SQS, lambda, parse the arn)
352
+ subscription["PendingConfirmation"] = "false"
353
+ subscription["ConfirmationWasAuthenticated"] = "true"
354
+
355
+ return SubscribeResponse(SubscriptionArn=response_subscription_arn)
356
+
357
+ def unsubscribe(
358
+ self, context: RequestContext, subscription_arn: subscriptionARN, **kwargs
359
+ ) -> None:
360
+ if subscription_arn is None:
361
+ raise InvalidParameterException(
362
+ "Invalid parameter: SubscriptionArn Reason: no value for required parameter",
363
+ )
364
+ count = len(subscription_arn.split(":"))
365
+ try:
366
+ parsed_arn = parse_arn(subscription_arn)
367
+ except InvalidArnException:
368
+ # TODO: check for invalid SubscriptionGUID
369
+ raise InvalidParameterException(
370
+ f"Invalid parameter: SubscriptionArn Reason: An ARN must have at least 6 elements, not {count}"
371
+ )
372
+
373
+ account_id = parsed_arn["account"]
374
+ region_name = parsed_arn["region"]
375
+
376
+ store = self.get_store(account_id=account_id, region=region_name)
377
+ if count == 6 and subscription_arn not in store.subscriptions:
378
+ raise InvalidParameterException("Invalid parameter: SubscriptionId")
379
+
380
+ # TODO: here was a moto_backend.unsubscribe call, check correct functionality and remove this comment
381
+ # before switching to v2 for production
382
+
383
+ # pop the subscription at the end, to avoid race condition by iterating over the topic subscriptions
384
+ subscription = store.subscriptions.get(subscription_arn)
385
+
386
+ if not subscription:
387
+ # unsubscribe is idempotent, so unsubscribing from a non-existing topic does nothing
388
+ return
389
+
390
+ if subscription["Protocol"] in ["http", "https"]:
391
+ # TODO: actually validate this (re)subscribe behaviour somehow (localhost.run?)
392
+ # we might need to save the sub token in the store
393
+ # TODO: AWS only sends the UnsubscribeConfirmation if the call is unauthenticated or the requester is not
394
+ # the owner
395
+ subscription_token = encode_subscription_token_with_region(region=context.region)
396
+ message_ctx = SnsMessage(
397
+ type=SnsMessageType.UnsubscribeConfirmation,
398
+ token=subscription_token,
399
+ message=f"You have chosen to deactivate subscription {subscription_arn}.\nTo cancel this operation and restore the subscription, visit the SubscribeURL included in this message.",
400
+ )
401
+ publish_ctx = SnsPublishContext(
402
+ message=message_ctx,
403
+ store=store,
404
+ request_headers=context.request.headers,
405
+ # TODO: add the topic attributes once we ported them from moto to LocalStack
406
+ # topic_attributes=vars(moto_topic),
407
+ )
408
+ self._publisher.publish_to_topic_subscriber(
409
+ publish_ctx,
410
+ topic_arn=subscription["TopicArn"],
411
+ subscription_arn=subscription_arn,
412
+ )
413
+
414
+ with contextlib.suppress(KeyError):
415
+ store.topics[subscription["TopicArn"]]["subscriptions"].remove(subscription_arn)
416
+ store.subscription_filter_policy.pop(subscription_arn, None)
417
+ store.subscriptions.pop(subscription_arn, None)
418
+
419
+ def get_subscription_attributes(
420
+ self, context: RequestContext, subscription_arn: subscriptionARN, **kwargs
421
+ ) -> GetSubscriptionAttributesResponse:
422
+ store = self.get_store(account_id=context.account_id, region=context.region)
423
+ sub = store.subscriptions.get(subscription_arn)
424
+ if not sub:
425
+ raise NotFoundException("Subscription does not exist")
426
+ removed_attrs = ["sqs_queue_url"]
427
+ if "FilterPolicyScope" in sub and not sub.get("FilterPolicy"):
428
+ removed_attrs.append("FilterPolicyScope")
429
+ removed_attrs.append("FilterPolicy")
430
+ elif "FilterPolicy" in sub and "FilterPolicyScope" not in sub:
431
+ sub["FilterPolicyScope"] = "MessageAttributes"
432
+
433
+ attributes = {k: v for k, v in sub.items() if k not in removed_attrs}
434
+ return GetSubscriptionAttributesResponse(Attributes=attributes)
435
+
436
+ def set_subscription_attributes(
437
+ self,
438
+ context: RequestContext,
439
+ subscription_arn: subscriptionARN,
440
+ attribute_name: attributeName,
441
+ attribute_value: attributeValue = None,
442
+ **kwargs,
443
+ ) -> None:
444
+ store = self.get_store(account_id=context.account_id, region=context.region)
445
+ sub = store.subscriptions.get(subscription_arn)
446
+ if not sub:
447
+ raise NotFoundException("Subscription does not exist")
448
+
449
+ validate_subscription_attribute(
450
+ attribute_name=attribute_name,
451
+ attribute_value=attribute_value,
452
+ topic_arn=sub["TopicArn"],
453
+ endpoint=sub["Endpoint"],
454
+ )
455
+ if attribute_name == "RawMessageDelivery":
456
+ attribute_value = attribute_value.lower()
457
+
458
+ elif attribute_name == "FilterPolicy":
459
+ filter_policy = json.loads(attribute_value) if attribute_value else None
460
+ if filter_policy:
461
+ validator = FilterPolicyValidator(
462
+ scope=sub.get("FilterPolicyScope", "MessageAttributes"),
463
+ is_subscribe_call=False,
464
+ )
465
+ validator.validate_filter_policy(filter_policy)
466
+
467
+ store.subscription_filter_policy[subscription_arn] = filter_policy
468
+
469
+ sub[attribute_name] = attribute_value
470
+
471
+ def confirm_subscription(
472
+ self,
473
+ context: RequestContext,
474
+ topic_arn: topicARN,
475
+ token: String,
476
+ authenticate_on_unsubscribe: authenticateOnUnsubscribe = None,
477
+ **kwargs,
478
+ ) -> ConfirmSubscriptionResponse:
479
+ # TODO: validate format on the token (seems to be 288 hex chars)
480
+ # this request can come from any http client, it might not be signed (we would need to implement
481
+ # `authenticate_on_unsubscribe` to force a signing client to do this request.
482
+ # so, the region and account_id might not be in the request. Use the ones from the topic_arn
483
+ try:
484
+ parsed_arn = parse_arn(topic_arn)
485
+ except InvalidArnException:
486
+ raise InvalidParameterException("Invalid parameter: Topic")
487
+
488
+ store = self.get_store(account_id=parsed_arn["account"], region=parsed_arn["region"])
489
+
490
+ # it seems SNS is able to know what the region of the topic should be, even though a wrong topic is accepted
491
+ if parsed_arn["region"] != get_region_from_subscription_token(token):
492
+ raise InvalidParameterException("Invalid parameter: Topic")
493
+
494
+ subscription_arn = store.subscription_tokens.get(token)
495
+ if not subscription_arn:
496
+ raise InvalidParameterException("Invalid parameter: Token")
497
+
498
+ subscription = store.subscriptions.get(subscription_arn)
499
+ if not subscription:
500
+ # subscription could have been deleted in the meantime
501
+ raise InvalidParameterException("Invalid parameter: Token")
502
+
503
+ # ConfirmSubscription is idempotent
504
+ if subscription.get("PendingConfirmation") == "false":
505
+ return ConfirmSubscriptionResponse(SubscriptionArn=subscription_arn)
506
+
507
+ subscription["PendingConfirmation"] = "false"
508
+ subscription["ConfirmationWasAuthenticated"] = "true"
509
+
510
+ return ConfirmSubscriptionResponse(SubscriptionArn=subscription_arn)
511
+
512
+ def list_subscriptions(
513
+ self, context: RequestContext, next_token: nextToken = None, **kwargs
514
+ ) -> ListSubscriptionsResponse:
515
+ store = self.get_store(context.account_id, context.region)
516
+ subscriptions = [
517
+ select_from_typed_dict(Subscription, sub) for sub in list(store.subscriptions.values())
518
+ ]
519
+ paginated_subscriptions = PaginatedList(subscriptions)
520
+ page, next_token = paginated_subscriptions.get_page(
521
+ token_generator=lambda x: get_next_page_token_from_arn(x["SubscriptionArn"]),
522
+ page_size=100,
523
+ next_token=next_token,
524
+ )
525
+
526
+ response = ListSubscriptionsResponse(Subscriptions=page)
527
+ if next_token:
528
+ response["NextToken"] = next_token
529
+ return response
530
+
531
+ def list_subscriptions_by_topic(
532
+ self, context: RequestContext, topic_arn: topicARN, next_token: nextToken = None, **kwargs
533
+ ) -> ListSubscriptionsByTopicResponse:
534
+ topic: Topic = self._get_topic(topic_arn, context)
535
+ parsed_topic_arn = parse_and_validate_topic_arn(topic_arn)
536
+ store = self.get_store(parsed_topic_arn["account"], parsed_topic_arn["region"])
537
+ sub_arns: list[str] = topic.get("subscriptions", [])
538
+ subscriptions = [store.subscriptions[k] for k in sub_arns if k in store.subscriptions]
539
+
540
+ paginated_subscriptions = PaginatedList(subscriptions)
541
+ page, next_token = paginated_subscriptions.get_page(
542
+ token_generator=lambda x: get_next_page_token_from_arn(x["SubscriptionArn"]),
543
+ page_size=100,
544
+ next_token=next_token,
545
+ )
546
+
547
+ response = ListSubscriptionsResponse(Subscriptions=page)
548
+ if next_token:
549
+ response["NextToken"] = next_token
550
+ return response
551
+
552
+ #
553
+ # PlatformApplications
554
+ #
555
+ def create_platform_application(
556
+ self,
557
+ context: RequestContext,
558
+ name: String,
559
+ platform: String,
560
+ attributes: MapStringToString,
561
+ **kwargs,
562
+ ) -> CreatePlatformApplicationResponse:
563
+ _validate_platform_application_name(name)
564
+ if platform not in VALID_APPLICATION_PLATFORMS:
565
+ raise InvalidParameterException(
566
+ f"Invalid parameter: Platform Reason: {platform} is not supported"
567
+ )
568
+
569
+ _validate_platform_application_attributes(attributes)
570
+
571
+ # attribute validation specific to create_platform_application
572
+ if "PlatformCredential" in attributes and "PlatformPrincipal" not in attributes:
573
+ raise InvalidParameterException(
574
+ "Invalid parameter: Attributes Reason: PlatformCredential attribute provided without PlatformPrincipal"
575
+ )
576
+
577
+ elif "PlatformPrincipal" in attributes and "PlatformCredential" not in attributes:
578
+ raise InvalidParameterException(
579
+ "Invalid parameter: Attributes Reason: PlatformPrincipal attribute provided without PlatformCredential"
580
+ )
581
+
582
+ store = self.get_store(context.account_id, context.region)
583
+ # We are not validating the access data here like AWS does (against ADM and the like)
584
+ attributes.pop("PlatformPrincipal")
585
+ attributes.pop("PlatformCredential")
586
+ _attributes = {"Enabled": "true"}
587
+ _attributes.update(attributes)
588
+ application_arn = sns_platform_application_arn(
589
+ platform_application_name=name,
590
+ platform=platform,
591
+ account_id=context.account_id,
592
+ region_name=context.region,
593
+ )
594
+ platform_application = PlatformApplication(
595
+ PlatformApplicationArn=application_arn, Attributes=_attributes
596
+ )
597
+ store.platform_applications[application_arn] = platform_application
598
+ return CreatePlatformApplicationResponse(**platform_application)
599
+
600
+ def delete_platform_application(
601
+ self, context: RequestContext, platform_application_arn: String, **kwargs
602
+ ) -> None:
603
+ store = self.get_store(context.account_id, context.region)
604
+ store.platform_applications.pop(platform_application_arn, None)
605
+
606
+ def list_platform_applications(
607
+ self, context: RequestContext, next_token: String | None = None, **kwargs
608
+ ) -> ListPlatformApplicationsResponse:
609
+ store = self.get_store(context.account_id, context.region)
610
+ platform_applications = store.platform_applications.values()
611
+ paginated_applications = PaginatedList(platform_applications)
612
+ page, token = paginated_applications.get_page(
613
+ token_generator=lambda x: get_next_page_token_from_arn(x["PlatformApplicationArn"]),
614
+ page_size=100,
615
+ next_token=next_token,
616
+ )
617
+
618
+ response = ListPlatformApplicationsResponse(PlatformApplications=page)
619
+ if token:
620
+ response["NextToken"] = token
621
+ return response
622
+
623
+ def get_platform_application_attributes(
624
+ self, context: RequestContext, platform_application_arn: String, **kwargs
625
+ ) -> GetPlatformApplicationAttributesResponse:
626
+ platform_application = self._get_platform_application(platform_application_arn, context)
627
+ attributes = platform_application["Attributes"]
628
+ return GetPlatformApplicationAttributesResponse(Attributes=attributes)
629
+
630
+ def set_platform_application_attributes(
631
+ self,
632
+ context: RequestContext,
633
+ platform_application_arn: String,
634
+ attributes: MapStringToString,
635
+ **kwargs,
636
+ ) -> None:
637
+ parse_and_validate_platform_application_arn(platform_application_arn)
638
+ _validate_platform_application_attributes(attributes)
639
+
640
+ platform_application = self._get_platform_application(platform_application_arn, context)
641
+ platform_application["Attributes"].update(attributes)
642
+
643
+ #
644
+ # Platform Endpoints
645
+ #
646
+
647
+ def list_endpoints_by_platform_application(
648
+ self,
649
+ context: RequestContext,
650
+ platform_application_arn: String,
651
+ next_token: String | None = None,
652
+ **kwargs,
653
+ ) -> ListEndpointsByPlatformApplicationResponse:
654
+ # TODO: stub so cleanup fixture won't fail
655
+ return ListEndpointsByPlatformApplicationResponse(Endpoints=[])
656
+
657
+ #
658
+ # Sms operations
659
+ #
660
+
661
+ def set_sms_attributes(
662
+ self, context: RequestContext, attributes: MapStringToString, **kwargs
663
+ ) -> SetSMSAttributesResponse:
664
+ store = self.get_store(context.account_id, context.region)
665
+ _validate_sms_attributes(attributes)
666
+ _set_sms_attribute_default(store)
667
+ store.sms_attributes.update(attributes or {})
668
+ return SetSMSAttributesResponse()
669
+
670
+ def get_sms_attributes(
671
+ self, context: RequestContext, attributes: ListString | None = None, **kwargs
672
+ ) -> GetSMSAttributesResponse:
673
+ store = self.get_store(context.account_id, context.region)
674
+ _set_sms_attribute_default(store)
675
+ store_attributes = store.sms_attributes
676
+ return_attributes = {}
677
+ for k, v in store_attributes.items():
678
+ if not attributes or k in attributes:
679
+ return_attributes[k] = store_attributes[k]
680
+
681
+ return GetSMSAttributesResponse(attributes=return_attributes)
682
+
683
+ def list_tags_for_resource(
684
+ self, context: RequestContext, resource_arn: AmazonResourceName, **kwargs
685
+ ) -> ListTagsForResourceResponse:
686
+ store = sns_stores[context.account_id][context.region]
687
+ tags = store.TAGS.list_tags_for_resource(resource_arn)
688
+ return ListTagsForResourceResponse(Tags=tags.get("Tags"))
689
+
690
+ def tag_resource(
691
+ self, context: RequestContext, resource_arn: AmazonResourceName, tags: TagList, **kwargs
692
+ ) -> TagResourceResponse:
693
+ unique_tag_keys = {tag["Key"] for tag in tags}
694
+ if len(unique_tag_keys) < len(tags):
695
+ raise InvalidParameterException("Invalid parameter: Duplicated keys are not allowed.")
696
+ store = sns_stores[context.account_id][context.region]
697
+ store.TAGS.tag_resource(resource_arn, tags)
698
+ return TagResourceResponse()
699
+
700
+ def untag_resource(
701
+ self,
702
+ context: RequestContext,
703
+ resource_arn: AmazonResourceName,
704
+ tag_keys: TagKeyList,
705
+ **kwargs,
706
+ ) -> UntagResourceResponse:
707
+ store = sns_stores[context.account_id][context.region]
708
+ store.TAGS.untag_resource(resource_arn, tag_keys)
709
+ return UntagResourceResponse()
710
+
711
+ @staticmethod
712
+ def get_store(account_id: str, region: str) -> SnsStore:
713
+ return sns_stores[account_id][region]
714
+
715
+ # TODO: reintroduce multi-region parameter (latest before final migration from v1)
716
+ @staticmethod
717
+ def _get_topic(arn: str, context: RequestContext) -> Topic:
718
+ """
719
+ :param arn: the Topic ARN
720
+ :param context: the RequestContext of the request
721
+ :return: the model Topic
722
+ """
723
+ arn_data = parse_and_validate_topic_arn(arn)
724
+ if context.region != arn_data["region"]:
725
+ raise InvalidParameterException("Invalid parameter: TopicArn")
726
+ try:
727
+ store = SnsProvider.get_store(context.account_id, context.region)
728
+ return store.topics[arn]
729
+ except KeyError:
730
+ raise NotFoundException("Topic does not exist")
731
+
732
+ @staticmethod
733
+ def _get_platform_application(
734
+ platform_application_arn: str, context: RequestContext
735
+ ) -> PlatformApplication:
736
+ parse_and_validate_platform_application_arn(platform_application_arn)
737
+ try:
738
+ store = SnsProvider.get_store(context.account_id, context.region)
739
+ return store.platform_applications[platform_application_arn]
740
+ except KeyError:
741
+ raise NotFoundException("PlatformApplication does not exist")
742
+
743
+
744
+ def _create_topic(name: str, attributes: dict, context: RequestContext) -> Topic:
745
+ topic_arn = sns_topic_arn(
746
+ topic_name=name, region_name=context.region, account_id=context.account_id
747
+ )
748
+ topic: Topic = {
749
+ "name": name,
750
+ "arn": topic_arn,
751
+ "attributes": {},
752
+ "subscriptions": [],
753
+ }
754
+ attrs = _default_attributes(topic, context)
755
+ attrs.update(attributes or {})
756
+ topic["attributes"] = attrs
757
+
758
+ return topic
759
+
760
+
761
+ def _default_attributes(topic: Topic, context: RequestContext) -> TopicAttributesMap:
762
+ default_attributes = {
763
+ "DisplayName": "",
764
+ "Owner": context.account_id,
765
+ "Policy": _create_default_topic_policy(topic, context),
766
+ "SubscriptionsConfirmed": "0",
767
+ "SubscriptionsDeleted": "0",
768
+ "SubscriptionsPending": "0",
769
+ "TopicArn": topic["arn"],
770
+ }
771
+ if topic["name"].endswith(".fifo"):
772
+ default_attributes.update(
773
+ {
774
+ "ContentBasedDeduplication": "false",
775
+ "FifoTopic": "false",
776
+ "SignatureVersion": "2",
777
+ }
778
+ )
779
+ return default_attributes
780
+
781
+
782
+ def _create_default_topic_policy(topic: Topic, context: RequestContext) -> str:
783
+ return json.dumps(
784
+ {
785
+ "Version": "2008-10-17",
786
+ "Id": "__default_policy_ID",
787
+ "Statement": [
788
+ {
789
+ "Effect": "Allow",
790
+ "Sid": "__default_statement_ID",
791
+ "Principal": {"AWS": "*"},
792
+ "Action": [
793
+ "SNS:GetTopicAttributes",
794
+ "SNS:SetTopicAttributes",
795
+ "SNS:AddPermission",
796
+ "SNS:RemovePermission",
797
+ "SNS:DeleteTopic",
798
+ "SNS:Subscribe",
799
+ "SNS:ListSubscriptionsByTopic",
800
+ "SNS:Publish",
801
+ ],
802
+ "Resource": topic["arn"],
803
+ "Condition": {"StringEquals": {"AWS:SourceOwner": context.account_id}},
804
+ }
805
+ ],
806
+ }
807
+ )
808
+
809
+
810
+ def _validate_platform_application_name(name: str) -> None:
811
+ reason = ""
812
+ if not name:
813
+ reason = "cannot be empty"
814
+ elif not re.match(r"^.{0,256}$", name):
815
+ reason = "must be at most 256 characters long"
816
+ elif not re.match(r"^[A-Za-z0-9._-]+$", name):
817
+ reason = "must contain only characters 'a'-'z', 'A'-'Z', '0'-'9', '_', '-', and '.'"
818
+
819
+ if reason:
820
+ raise InvalidParameterException(f"Invalid parameter: {name} Reason: {reason}")
821
+
822
+
823
+ def _validate_platform_application_attributes(attributes: dict) -> None:
824
+ if not attributes:
825
+ raise CommonServiceException(
826
+ code="ValidationError",
827
+ message="1 validation error detected: Value null at 'attributes' failed to satisfy constraint: Member must not be null",
828
+ sender_fault=True,
829
+ )
830
+
831
+
832
+ def _validate_sms_attributes(attributes: dict) -> None:
833
+ for k, v in attributes.items():
834
+ if k not in SMS_ATTRIBUTE_NAMES:
835
+ raise InvalidParameterException(f"{k} is not a valid attribute")
836
+ default_send_id = attributes.get("DefaultSendID")
837
+ if default_send_id and not re.match(SMS_DEFAULT_SENDER_REGEX, default_send_id):
838
+ raise InvalidParameterException("DefaultSendID is not a valid attribute")
839
+ sms_type = attributes.get("DefaultSMSType")
840
+ if sms_type and sms_type not in SMS_TYPES:
841
+ raise InvalidParameterException("DefaultSMSType is invalid")
842
+
843
+
844
+ def _set_sms_attribute_default(store: SnsStore) -> None:
845
+ # TODO: don't call this on every sms attribute crud api call
846
+ store.sms_attributes.setdefault("MonthlySpendLimit", "1")
847
+
848
+
849
+ def _check_matching_tags(topic_arn: str, tags: TagList | None, store: SnsStore) -> bool:
850
+ """
851
+ Checks if a topic to be created doesn't already exist with different tags
852
+ :param topic_arn: Arn of the topic
853
+ :param tags: Tags to be checked
854
+ :param store: Store object that holds the topics and tags
855
+ :return: False if there is a mismatch in tags, True otherwise
856
+ """
857
+ existing_tags = store.TAGS.list_tags_for_resource(topic_arn)["Tags"]
858
+ # if this is none there is nothing to check
859
+ if topic_arn in store.topics:
860
+ if tags is None:
861
+ tags = []
862
+ for tag in tags:
863
+ # this means topic already created with empty tags and when we try to create it
864
+ # again with other tag value then it should fail according to aws documentation.
865
+ if existing_tags is not None and tag not in existing_tags:
866
+ return False
867
+ return True