arnmatch 2026.1.2__tar.gz → 2026.1.3__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arnmatch
3
- Version: 2026.1.2
3
+ Version: 2026.1.3
4
4
  Summary: Parse AWS ARNs into structured data (2000+ resource types)
5
5
  Author-email: Andrey Gubarev <andrey@andreygubarev.com>
6
6
  Requires-Python: >=3.10
@@ -9,6 +9,7 @@ from pathlib import Path
9
9
 
10
10
  from scraper import AWSScraper
11
11
  from index_sdk import SDKServiceIndexer
12
+ from index_sdk_resources import SDKResourceIndexer
12
13
 
13
14
  log = logging.getLogger(__name__)
14
15
 
@@ -264,8 +265,22 @@ class CodeGenerator:
264
265
  # Write SDK services mapping
265
266
  f.write("# Auto-generated mapping: ARN service -> AWS SDK client names\n")
266
267
  f.write("AWS_SDK_SERVICES = {\n")
267
- for arn_svc, clients in sorted(sdk_services_mapping.items()):
268
- f.write(f" {arn_svc!r}: {clients!r},\n")
268
+ for arn_service, clients in sorted(sdk_services_mapping.items()):
269
+ f.write(f" {arn_service!r}: {clients!r},\n")
270
+ f.write("}\n\n")
271
+
272
+ # Write SDK default service mapping
273
+ f.write("# Default SDK for multi-SDK services\n")
274
+ f.write("AWS_SDK_SERVICES_DEFAULT = {\n")
275
+ for arn_service, sdk in sorted(SDKResourceIndexer.DEFAULT_SERVICE.items()):
276
+ f.write(f" {arn_service!r}: {sdk!r},\n")
277
+ f.write("}\n\n")
278
+
279
+ # Write SDK service overrides (resource-level)
280
+ f.write("# Resource-level SDK overrides: resource_type -> sdk_client\n")
281
+ f.write("AWS_SDK_SERVICES_OVERRIDE = {\n")
282
+ for arn_service, overrides in sorted(SDKResourceIndexer.OVERRIDE_SERVICE.items()):
283
+ f.write(f" {arn_service!r}: {overrides!r},\n")
269
284
  f.write("}\n")
270
285
 
271
286
  log.info(f"Wrote {len(resources)} patterns for {len(by_service)} services to {output_path}")
@@ -318,6 +333,10 @@ def main():
318
333
  sdk_indexer = SDKServiceIndexer()
319
334
  sdk_mapping = sdk_indexer.process(arn_services)
320
335
 
336
+ # Validate multi-SDK services have DEFAULT_SERVICE entries
337
+ sdk_resource_indexer = SDKResourceIndexer()
338
+ sdk_resource_indexer.process(sdk_mapping)
339
+
321
340
  # Generate
322
341
  BUILD_DIR.mkdir(exist_ok=True)
323
342
  generator = CodeGenerator()
@@ -0,0 +1,199 @@
1
+ """Maps ARN resource types to specific SDK clients for multi-SDK services.
2
+
3
+ Why aws_sdk_services (plural) exists alongside aws_sdk_service (singular):
4
+
5
+ Some services share identical ARN formats but are fundamentally different engines.
6
+ For example, RDS, Neptune, and DocumentDB all use the same ARN structure:
7
+ arn:aws:rds:region:account:db:instance-name
8
+
9
+ The ARN alone cannot distinguish between these engines - you need external context
10
+ (tags, API calls, or prior knowledge) to determine the correct SDK. In these cases,
11
+ aws_sdk_service returns the default (rds), while aws_sdk_services returns all
12
+ possible SDKs [rds, neptune, docdb] for the caller to disambiguate.
13
+ """
14
+
15
+
16
+ class SDKResourceIndexer:
17
+ """SDK defaults for services with multiple SDK clients.
18
+
19
+ DEFAULT_SERVICE: Service-level default where all resources use a single SDK.
20
+ Format: "arn_service" -> "sdk_client"
21
+
22
+ OVERRIDE_SERVICE: Resource-level overrides where different resources use different SDKs.
23
+ Format: "arn_service" -> {"resource_type": "sdk_client", ...}
24
+ """
25
+
26
+ # Service-level default - the SDK responsible for most resources
27
+ # (other auto-detected SDKs are typically runtime/data/query clients)
28
+ DEFAULT_SERVICE = {
29
+ "apigateway": "apigateway", # v1 REST API; v2 is HTTP/WebSocket
30
+ "appconfig": "appconfig", # appconfigdata is runtime-only
31
+ "aws-marketplace": "marketplace-catalog", # entity management
32
+ "bedrock": "bedrock", # model management
33
+ "bedrock-agentcore": "bedrock-agentcore-control", # control plane
34
+ "cassandra": "keyspaces", # keyspacesstreams is CDC
35
+ "chime": "chime", # others are specialized SDKs
36
+ "cloudhsm": "cloudhsmv2", # v1 deprecated
37
+ "cloudsearch": "cloudsearch", # domain is query-only
38
+ "connect": "connect", # contact-lens is analytics
39
+ "connect-campaigns": "connectcampaignsv2", # v1 deprecated
40
+ "dynamodb": "dynamodb", # streams is CDC
41
+ "elasticloadbalancing": "elbv2", # ALB/NLB/GLB; elb is classic
42
+ "es": "opensearch", # Elasticsearch rebranded
43
+ "execute-api": "apigatewaymanagementapi", # WebSocket management
44
+ "forecast": "forecast", # forecastquery is runtime
45
+ "greengrass": "greengrassv2", # v1 deprecated
46
+ "ivs": "ivs", # ivs-realtime is stages
47
+ "kinesisanalytics": "kinesisanalyticsv2", # v1 deprecated
48
+ "kinesisvideo": "kinesisvideo", # others are media streaming
49
+ "lex": "lexv2-models", # v1 deprecated
50
+ "mediastore": "mediastore", # data is object operations
51
+ "mgh": "mgh", # config is home region only
52
+ "partnercentral": "partnercentral-selling", # primary module
53
+ "payment-cryptography": "payment-cryptography", # data is crypto ops
54
+ "personalize": "personalize", # events/runtime are runtime
55
+ "rds": "rds", # docdb/neptune are different engines
56
+ "route53-recovery-control": "route53-recovery-control-config", # cluster is data plane
57
+ "s3": "s3", # s3control is account-level
58
+ "sagemaker": "sagemaker", # others are runtime/edge/metrics
59
+ "servicecatalog": "servicecatalog", # appregistry is separate
60
+ "ses": "sesv2", # v1 deprecated
61
+ "sms-voice": "pinpoint-sms-voice-v2", # v1 deprecated
62
+ "sso": "sso-admin", # sso is portal access
63
+ "timestream": "timestream-write", # query is queries only
64
+ "wisdom": "qconnect", # rebranded to Q Connect
65
+ }
66
+
67
+ # Resource-level overrides - only non-default SDK mappings
68
+ # Format: "arn_service" -> {"resource_type": "sdk_client", ...}
69
+ OVERRIDE_SERVICE = {
70
+ "apigateway": {
71
+ # v2 (HTTP/WebSocket API) resources
72
+ "ApiMappings": "apigatewayv2",
73
+ "ApiMapping": "apigatewayv2",
74
+ "Apis": "apigatewayv2",
75
+ "Api": "apigatewayv2",
76
+ "Cors": "apigatewayv2",
77
+ "ExportedAPI": "apigatewayv2",
78
+ "Integrations": "apigatewayv2",
79
+ "RouteRequestParameter": "apigatewayv2",
80
+ "RouteResponses": "apigatewayv2",
81
+ "RouteResponse": "apigatewayv2",
82
+ "RouteSettings": "apigatewayv2",
83
+ "Routes": "apigatewayv2",
84
+ "Route": "apigatewayv2",
85
+ },
86
+ "bedrock": {
87
+ # bedrock-agent resources
88
+ "agent-alias": "bedrock-agent",
89
+ "agent": "bedrock-agent",
90
+ "default-prompt-router": "bedrock-agent",
91
+ "flow-alias": "bedrock-agent",
92
+ "flow-execution": "bedrock-agent",
93
+ "flow": "bedrock-agent",
94
+ "knowledge-base": "bedrock-agent",
95
+ "prompt-router": "bedrock-agent",
96
+ "prompt-version": "bedrock-agent",
97
+ "prompt": "bedrock-agent",
98
+ # bedrock-agent-runtime
99
+ "session": "bedrock-agent-runtime",
100
+ # bedrock-runtime
101
+ "async-invoke": "bedrock-runtime",
102
+ },
103
+ "chime": {
104
+ "meeting": "chime-sdk-meetings",
105
+ "app-instance": "chime-sdk-identity",
106
+ "app-instance-bot": "chime-sdk-identity",
107
+ "app-instance-user": "chime-sdk-identity",
108
+ "channel": "chime-sdk-messaging",
109
+ "channel-flow": "chime-sdk-messaging",
110
+ "media-insights-pipeline-configuration": "chime-sdk-media-pipelines",
111
+ "media-pipeline": "chime-sdk-media-pipelines",
112
+ "media-pipeline-kinesis-video-stream-pool": "chime-sdk-media-pipelines",
113
+ "sip-media-application": "chime-sdk-voice",
114
+ "voice-connector-group": "chime-sdk-voice",
115
+ "voice-connector": "chime-sdk-voice",
116
+ "voice-profile": "chime-sdk-voice",
117
+ "voice-profile-domain": "chime-sdk-voice",
118
+ },
119
+ "dynamodb": {
120
+ "stream": "dynamodbstreams",
121
+ },
122
+ "elasticloadbalancing": {
123
+ "loadbalancer": "elb", # classic
124
+ },
125
+ "greengrass": {
126
+ # v1 resources (v2 is default)
127
+ "bulkDeployment": "greengrass",
128
+ "certificateAuthority": "greengrass",
129
+ "connectivityInfo": "greengrass",
130
+ "connectorDefinitionVersion": "greengrass",
131
+ "connectorDefinition": "greengrass",
132
+ "coreDefinitionVersion": "greengrass",
133
+ "coreDefinition": "greengrass",
134
+ "deviceDefinitionVersion": "greengrass",
135
+ "deviceDefinition": "greengrass",
136
+ "functionDefinitionVersion": "greengrass",
137
+ "functionDefinition": "greengrass",
138
+ "groupVersion": "greengrass",
139
+ "group": "greengrass",
140
+ "loggerDefinitionVersion": "greengrass",
141
+ "loggerDefinition": "greengrass",
142
+ "resourceDefinitionVersion": "greengrass",
143
+ "resourceDefinition": "greengrass",
144
+ "subscriptionDefinitionVersion": "greengrass",
145
+ "subscriptionDefinition": "greengrass",
146
+ "thingRuntimeConfig": "greengrass",
147
+ },
148
+ "ivs": {
149
+ # ivs-realtime resources
150
+ "Composition": "ivs-realtime",
151
+ "Encoder-Configuration": "ivs-realtime",
152
+ "Ingest-Configuration": "ivs-realtime",
153
+ "Public-Key": "ivs-realtime",
154
+ "Stage": "ivs-realtime",
155
+ "Storage-Configuration": "ivs-realtime",
156
+ },
157
+ "lex": {
158
+ # v1 resources
159
+ "channel": "lex-models",
160
+ "intent version": "lex-models",
161
+ "slottype version": "lex-models",
162
+ },
163
+ "mediastore": {
164
+ "folder": "mediastore-data",
165
+ "object": "mediastore-data",
166
+ },
167
+ "partnercentral": {
168
+ "BenefitAllocation": "partnercentral-benefits",
169
+ "BenefitApplication": "partnercentral-benefits",
170
+ "Benefit": "partnercentral-benefits",
171
+ "ChannelHandshake": "partnercentral-channel",
172
+ "ProgramManagementAccount": "partnercentral-account",
173
+ },
174
+ "s3": {
175
+ "accessgrant": "s3control",
176
+ "accessgrantslocation": "s3control",
177
+ "accessgrantsinstance": "s3control",
178
+ "accesspoint": "s3control",
179
+ "accesspointobject": "s3control",
180
+ "job": "s3control",
181
+ "multiregionaccesspoint": "s3control",
182
+ "multiregionaccesspointrequestarn": "s3control",
183
+ "storagelensconfiguration": "s3control",
184
+ "storagelensgroup": "s3control",
185
+ },
186
+ "servicecatalog": {
187
+ "Application": "servicecatalog-appregistry",
188
+ "AttributeGroup": "servicecatalog-appregistry",
189
+ },
190
+ }
191
+
192
+ def process(self, sdk_mapping):
193
+ """Validate all multi-SDK services have a DEFAULT_SERVICE entry."""
194
+ missing = {}
195
+ for arn_service, sdks in sdk_mapping.items():
196
+ if len(sdks) > 1 and arn_service not in self.DEFAULT_SERVICE:
197
+ missing[arn_service] = sdks
198
+ if missing:
199
+ raise RuntimeError(f"Missing DEFAULT_SERVICE for multi-SDK services: {missing}")
@@ -1,12 +1,17 @@
1
1
  """ARN pattern matching using regex patterns."""
2
2
 
3
- __version__ = "2026.01.2"
3
+ __version__ = "2026.01.3"
4
4
 
5
5
  import sys
6
6
  from dataclasses import dataclass
7
7
  from functools import cached_property
8
8
 
9
- from .arn_patterns import ARN_PATTERNS, AWS_SDK_SERVICES
9
+ from .arn_patterns import (
10
+ ARN_PATTERNS,
11
+ AWS_SDK_SERVICES,
12
+ AWS_SDK_SERVICES_DEFAULT,
13
+ AWS_SDK_SERVICES_OVERRIDE,
14
+ )
10
15
 
11
16
  # Standard groups that are not resource-specific
12
17
  STANDARD_GROUPS = {"Partition", "Region", "Account"}
@@ -78,7 +83,7 @@ class ARN:
78
83
  return self.resource_id
79
84
 
80
85
  @cached_property
81
- def aws_sdk_services(self):
86
+ def aws_sdk_services(self) -> list[str]:
82
87
  """Get AWS SDK (boto3) client names for this resource's service.
83
88
 
84
89
  Returns list of client names that can interact with this resource type.
@@ -88,6 +93,26 @@ class ARN:
88
93
  """
89
94
  return AWS_SDK_SERVICES.get(self.aws_service, [])
90
95
 
96
+ @cached_property
97
+ def aws_sdk_service(self) -> str | None:
98
+ """Get the AWS SDK (boto3) client name for this resource.
99
+
100
+ Returns single client name. Checks resource-level overrides first,
101
+ then falls back to service default. Returns None if no SDK exists.
102
+ """
103
+ # Check resource-level override
104
+ overrides = AWS_SDK_SERVICES_OVERRIDE.get(self.aws_service)
105
+ if overrides and self.resource_type in overrides:
106
+ return overrides[self.resource_type]
107
+
108
+ # Fall back to service-level
109
+ sdks = self.aws_sdk_services
110
+ if len(sdks) == 1:
111
+ return sdks[0]
112
+ if len(sdks) > 1:
113
+ return AWS_SDK_SERVICES_DEFAULT.get(self.aws_service)
114
+ return None
115
+
91
116
 
92
117
  def arnmatch(arn: str) -> ARN:
93
118
  """Match ARN against patterns.
@@ -132,6 +157,7 @@ def main() -> None:
132
157
  try:
133
158
  result = arnmatch(arn)
134
159
  print(f"aws_service: {result.aws_service}")
160
+ print(f"aws_sdk_service: {result.aws_sdk_service}")
135
161
  print(f"aws_sdk_services: {','.join(result.aws_sdk_services)}")
136
162
  print(f"aws_region: {result.aws_region}")
137
163
  print(f"aws_account: {result.aws_account}")
@@ -3155,3 +3155,59 @@ AWS_SDK_SERVICES = {
3155
3155
  'workspaces-web': ['workspaces-web'],
3156
3156
  'xray': ['xray'],
3157
3157
  }
3158
+
3159
+ # Default SDK for multi-SDK services
3160
+ AWS_SDK_SERVICES_DEFAULT = {
3161
+ 'apigateway': 'apigateway',
3162
+ 'appconfig': 'appconfig',
3163
+ 'aws-marketplace': 'marketplace-catalog',
3164
+ 'bedrock': 'bedrock',
3165
+ 'bedrock-agentcore': 'bedrock-agentcore-control',
3166
+ 'cassandra': 'keyspaces',
3167
+ 'chime': 'chime',
3168
+ 'cloudhsm': 'cloudhsmv2',
3169
+ 'cloudsearch': 'cloudsearch',
3170
+ 'connect': 'connect',
3171
+ 'connect-campaigns': 'connectcampaignsv2',
3172
+ 'dynamodb': 'dynamodb',
3173
+ 'elasticloadbalancing': 'elbv2',
3174
+ 'es': 'opensearch',
3175
+ 'execute-api': 'apigatewaymanagementapi',
3176
+ 'forecast': 'forecast',
3177
+ 'greengrass': 'greengrassv2',
3178
+ 'ivs': 'ivs',
3179
+ 'kinesisanalytics': 'kinesisanalyticsv2',
3180
+ 'kinesisvideo': 'kinesisvideo',
3181
+ 'lex': 'lexv2-models',
3182
+ 'mediastore': 'mediastore',
3183
+ 'mgh': 'mgh',
3184
+ 'partnercentral': 'partnercentral-selling',
3185
+ 'payment-cryptography': 'payment-cryptography',
3186
+ 'personalize': 'personalize',
3187
+ 'rds': 'rds',
3188
+ 'route53-recovery-control': 'route53-recovery-control-config',
3189
+ 's3': 's3',
3190
+ 'sagemaker': 'sagemaker',
3191
+ 'servicecatalog': 'servicecatalog',
3192
+ 'ses': 'sesv2',
3193
+ 'sms-voice': 'pinpoint-sms-voice-v2',
3194
+ 'sso': 'sso-admin',
3195
+ 'timestream': 'timestream-write',
3196
+ 'wisdom': 'qconnect',
3197
+ }
3198
+
3199
+ # Resource-level SDK overrides: resource_type -> sdk_client
3200
+ AWS_SDK_SERVICES_OVERRIDE = {
3201
+ 'apigateway': {'ApiMappings': 'apigatewayv2', 'ApiMapping': 'apigatewayv2', 'Apis': 'apigatewayv2', 'Api': 'apigatewayv2', 'Cors': 'apigatewayv2', 'ExportedAPI': 'apigatewayv2', 'Integrations': 'apigatewayv2', 'RouteRequestParameter': 'apigatewayv2', 'RouteResponses': 'apigatewayv2', 'RouteResponse': 'apigatewayv2', 'RouteSettings': 'apigatewayv2', 'Routes': 'apigatewayv2', 'Route': 'apigatewayv2'},
3202
+ 'bedrock': {'agent-alias': 'bedrock-agent', 'agent': 'bedrock-agent', 'default-prompt-router': 'bedrock-agent', 'flow-alias': 'bedrock-agent', 'flow-execution': 'bedrock-agent', 'flow': 'bedrock-agent', 'knowledge-base': 'bedrock-agent', 'prompt-router': 'bedrock-agent', 'prompt-version': 'bedrock-agent', 'prompt': 'bedrock-agent', 'session': 'bedrock-agent-runtime', 'async-invoke': 'bedrock-runtime'},
3203
+ 'chime': {'meeting': 'chime-sdk-meetings', 'app-instance': 'chime-sdk-identity', 'app-instance-bot': 'chime-sdk-identity', 'app-instance-user': 'chime-sdk-identity', 'channel': 'chime-sdk-messaging', 'channel-flow': 'chime-sdk-messaging', 'media-insights-pipeline-configuration': 'chime-sdk-media-pipelines', 'media-pipeline': 'chime-sdk-media-pipelines', 'media-pipeline-kinesis-video-stream-pool': 'chime-sdk-media-pipelines', 'sip-media-application': 'chime-sdk-voice', 'voice-connector-group': 'chime-sdk-voice', 'voice-connector': 'chime-sdk-voice', 'voice-profile': 'chime-sdk-voice', 'voice-profile-domain': 'chime-sdk-voice'},
3204
+ 'dynamodb': {'stream': 'dynamodbstreams'},
3205
+ 'elasticloadbalancing': {'loadbalancer': 'elb'},
3206
+ 'greengrass': {'bulkDeployment': 'greengrass', 'certificateAuthority': 'greengrass', 'connectivityInfo': 'greengrass', 'connectorDefinitionVersion': 'greengrass', 'connectorDefinition': 'greengrass', 'coreDefinitionVersion': 'greengrass', 'coreDefinition': 'greengrass', 'deviceDefinitionVersion': 'greengrass', 'deviceDefinition': 'greengrass', 'functionDefinitionVersion': 'greengrass', 'functionDefinition': 'greengrass', 'groupVersion': 'greengrass', 'group': 'greengrass', 'loggerDefinitionVersion': 'greengrass', 'loggerDefinition': 'greengrass', 'resourceDefinitionVersion': 'greengrass', 'resourceDefinition': 'greengrass', 'subscriptionDefinitionVersion': 'greengrass', 'subscriptionDefinition': 'greengrass', 'thingRuntimeConfig': 'greengrass'},
3207
+ 'ivs': {'Composition': 'ivs-realtime', 'Encoder-Configuration': 'ivs-realtime', 'Ingest-Configuration': 'ivs-realtime', 'Public-Key': 'ivs-realtime', 'Stage': 'ivs-realtime', 'Storage-Configuration': 'ivs-realtime'},
3208
+ 'lex': {'channel': 'lex-models', 'intent version': 'lex-models', 'slottype version': 'lex-models'},
3209
+ 'mediastore': {'folder': 'mediastore-data', 'object': 'mediastore-data'},
3210
+ 'partnercentral': {'BenefitAllocation': 'partnercentral-benefits', 'BenefitApplication': 'partnercentral-benefits', 'Benefit': 'partnercentral-benefits', 'ChannelHandshake': 'partnercentral-channel', 'ProgramManagementAccount': 'partnercentral-account'},
3211
+ 's3': {'accessgrant': 's3control', 'accessgrantslocation': 's3control', 'accessgrantsinstance': 's3control', 'accesspoint': 's3control', 'accesspointobject': 's3control', 'job': 's3control', 'multiregionaccesspoint': 's3control', 'multiregionaccesspointrequestarn': 's3control', 'storagelensconfiguration': 's3control', 'storagelensgroup': 's3control'},
3212
+ 'servicecatalog': {'Application': 'servicecatalog-appregistry', 'AttributeGroup': 'servicecatalog-appregistry'},
3213
+ }
@@ -10,12 +10,20 @@ def test_acm():
10
10
  assert result.resource_type == "certificate"
11
11
  assert result.attributes["CertificateId"] == "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
12
12
  assert result.aws_sdk_services == ["acm"]
13
+ assert result.aws_sdk_service == "acm"
13
14
 
14
15
 
15
16
  def test_apigateway():
17
+ # v1 REST API (default)
16
18
  result = arnmatch("arn:aws:apigateway:us-east-1::/restapis/abc123def4")
17
19
  assert result.resource_type == "RestApi"
18
20
  assert result.attributes["RestApiId"] == "abc123def4"
21
+ assert result.aws_sdk_service == "apigateway"
22
+
23
+ # v2 HTTP API (override)
24
+ result = arnmatch("arn:aws:apigateway:us-east-1::/apis/abc123def4")
25
+ assert result.resource_type == "Api"
26
+ assert result.aws_sdk_service == "apigatewayv2"
19
27
 
20
28
 
21
29
  def test_athena():
@@ -100,9 +108,18 @@ def test_datasync():
100
108
 
101
109
 
102
110
  def test_dynamodb():
111
+ # table (default)
103
112
  result = arnmatch("arn:aws:dynamodb:us-east-1:012345678901:table/table1")
104
113
  assert result.resource_type == "table"
105
114
  assert result.attributes["TableName"] == "table1"
115
+ assert result.aws_sdk_service == "dynamodb"
116
+
117
+ # stream (override)
118
+ result = arnmatch(
119
+ "arn:aws:dynamodb:us-east-1:012345678901:table/table1/stream/2024-01-01T00:00:00.000"
120
+ )
121
+ assert result.resource_type == "stream"
122
+ assert result.aws_sdk_service == "dynamodbstreams"
106
123
 
107
124
 
108
125
  def test_ec2():
@@ -220,30 +237,33 @@ def test_elasticfilesystem():
220
237
 
221
238
 
222
239
  def test_elasticloadbalancing():
223
- # Classic LB
240
+ # Classic LB (override)
224
241
  result = arnmatch(
225
242
  "arn:aws:elasticloadbalancing:us-east-1:012345678901:loadbalancer/a0123456789abcdef0123456789abcde"
226
243
  )
227
244
  assert result.resource_type == "loadbalancer"
228
245
  assert result.attributes["LoadBalancerName"] == "a0123456789abcdef0123456789abcde"
246
+ assert result.aws_sdk_service == "elb"
229
247
 
230
- # ALB
248
+ # ALB (default)
231
249
  result = arnmatch(
232
250
  "arn:aws:elasticloadbalancing:us-east-1:012345678901:loadbalancer/app/alb-application-lb-name/0123456789abcdef"
233
251
  )
234
252
  assert result.resource_type == "loadbalancer/app/"
235
253
  assert result.attributes["LoadBalancerName"] == "alb-application-lb-name"
236
254
  assert result.attributes["LoadBalancerId"] == "0123456789abcdef"
255
+ assert result.aws_sdk_service == "elbv2"
237
256
 
238
- # NLB
257
+ # NLB (default)
239
258
  result = arnmatch(
240
259
  "arn:aws:elasticloadbalancing:us-east-1:012345678901:loadbalancer/net/nlb-network-load-balancer/0123456789abcdef"
241
260
  )
242
261
  assert result.resource_type == "loadbalancer/net/"
243
262
  assert result.attributes["LoadBalancerName"] == "nlb-network-load-balancer"
244
263
  assert result.attributes["LoadBalancerId"] == "0123456789abcdef"
264
+ assert result.aws_sdk_service == "elbv2"
245
265
 
246
- # Target group
266
+ # Target group (default)
247
267
  result = arnmatch(
248
268
  "arn:aws:elasticloadbalancing:us-east-1:012345678901:targetgroup/target-grp-1/0123456789abcdef"
249
269
  )
@@ -252,6 +272,7 @@ def test_elasticloadbalancing():
252
272
  assert result.attributes["TargetGroupId"] == "0123456789abcdef"
253
273
  # elasticloadbalancing maps to multiple SDK clients
254
274
  assert result.aws_sdk_services == ["elb", "elbv2"]
275
+ assert result.aws_sdk_service == "elbv2"
255
276
 
256
277
 
257
278
  def test_es():
@@ -364,10 +385,17 @@ def test_route53():
364
385
 
365
386
 
366
387
  def test_s3():
388
+ # bucket (default)
367
389
  result = arnmatch("arn:aws:s3:::example-bucket-01")
368
390
  assert result.resource_type == "bucket"
369
391
  assert result.attributes["BucketName"] == "example-bucket-01"
370
392
  assert result.aws_sdk_services == ["s3", "s3control"]
393
+ assert result.aws_sdk_service == "s3"
394
+
395
+ # accesspoint (override)
396
+ result = arnmatch("arn:aws:s3:us-east-1:012345678901:accesspoint/my-access-point")
397
+ assert result.resource_type == "accesspoint"
398
+ assert result.aws_sdk_service == "s3control"
371
399
 
372
400
 
373
401
  def test_secretsmanager():
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes