Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2b1a5bb

Browse files
ericbnleandrodamascena
andauthoredMar 18, 2025··
refactor(data_classes): simplify nested data classes (#6289)
* refactor(data_classes): simplify nested data classes Make each nested data class only know its own properties instead of having to reference from the root of the data. This hopefully simplifies the code and avoids DRY. Also simplify test cases. * refactor(data_classes): fix tests * refactor(data_classes): push another small commit to see if GH works --------- Co-authored-by: Leandro Damascena <[email protected]>
1 parent c12fd21 commit 2b1a5bb

File tree

12 files changed

+228
-301
lines changed

12 files changed

+228
-301
lines changed
 

‎aws_lambda_powertools/utilities/data_classes/alb_event.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ALBEventRequestContext(DictWrapper):
1818
@property
1919
def elb_target_group_arn(self) -> str:
2020
"""Target group arn for your Lambda function"""
21-
return self["requestContext"]["elb"]["targetGroupArn"]
21+
return self["elb"]["targetGroupArn"]
2222

2323

2424
class ALBEvent(BaseProxyEvent):
@@ -32,7 +32,7 @@ class ALBEvent(BaseProxyEvent):
3232

3333
@property
3434
def request_context(self) -> ALBEventRequestContext:
35-
return ALBEventRequestContext(self._data)
35+
return ALBEventRequestContext(self["requestContext"])
3636

3737
@property
3838
def multi_value_query_string_parameters(self) -> dict[str, list[str]]:

‎aws_lambda_powertools/utilities/data_classes/api_gateway_authorizer_event.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def stage_variables(self) -> dict[str, str]:
183183

184184
@property
185185
def request_context(self) -> BaseRequestContext:
186-
return BaseRequestContext(self._data)
186+
return BaseRequestContext(self["requestContext"])
187187

188188
@overload
189189
def get_header_value(
@@ -306,7 +306,7 @@ def query_string_parameters(self) -> dict[str, str]:
306306

307307
@property
308308
def request_context(self) -> BaseRequestContextV2:
309-
return BaseRequestContextV2(self._data)
309+
return BaseRequestContextV2(self["requestContext"])
310310

311311
@property
312312
def path_parameters(self) -> dict[str, str]:

‎aws_lambda_powertools/utilities/data_classes/api_gateway_proxy_event.py‎

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,42 +61,41 @@ class APIGatewayEventRequestContext(BaseRequestContext):
6161
@property
6262
def connected_at(self) -> int | None:
6363
"""The Epoch-formatted connection time. (WebSocket API)"""
64-
return self["requestContext"].get("connectedAt")
64+
return self.get("connectedAt")
6565

6666
@property
6767
def connection_id(self) -> str | None:
6868
"""A unique ID for the connection that can be used to make a callback to the client. (WebSocket API)"""
69-
return self["requestContext"].get("connectionId")
69+
return self.get("connectionId")
7070

7171
@property
7272
def event_type(self) -> str | None:
7373
"""The event type: `CONNECT`, `MESSAGE`, or `DISCONNECT`. (WebSocket API)"""
74-
return self["requestContext"].get("eventType")
74+
return self.get("eventType")
7575

7676
@property
7777
def message_direction(self) -> str | None:
7878
"""Message direction (WebSocket API)"""
79-
return self["requestContext"].get("messageDirection")
79+
return self.get("messageDirection")
8080

8181
@property
8282
def message_id(self) -> str | None:
8383
"""A unique server-side ID for a message. Available only when the `eventType` is `MESSAGE`."""
84-
return self["requestContext"].get("messageId")
84+
return self.get("messageId")
8585

8686
@property
8787
def operation_name(self) -> str | None:
8888
"""The name of the operation being performed"""
89-
return self["requestContext"].get("operationName")
89+
return self.get("operationName")
9090

9191
@property
9292
def route_key(self) -> str | None:
9393
"""The selected route key."""
94-
return self["requestContext"].get("routeKey")
94+
return self.get("routeKey")
9595

9696
@property
9797
def authorizer(self) -> APIGatewayEventAuthorizer:
98-
authz_data = self._data.get("requestContext", {}).get("authorizer", {})
99-
return APIGatewayEventAuthorizer(authz_data)
98+
return APIGatewayEventAuthorizer(self.get("authorizer") or {})
10099

101100

102101
class APIGatewayProxyEvent(BaseProxyEvent):
@@ -136,7 +135,7 @@ def resolved_headers_field(self) -> dict[str, Any]:
136135

137136
@property
138137
def request_context(self) -> APIGatewayEventRequestContext:
139-
return APIGatewayEventRequestContext(self._data)
138+
return APIGatewayEventRequestContext(self["requestContext"])
140139

141140
@property
142141
def path_parameters(self) -> dict[str, str]:
@@ -248,8 +247,7 @@ def iam(self) -> RequestContextV2AuthorizerIam:
248247
class RequestContextV2(BaseRequestContextV2):
249248
@property
250249
def authorizer(self) -> RequestContextV2Authorizer:
251-
ctx = self.get("requestContext") or {} # key might exist but can be `null`
252-
return RequestContextV2Authorizer(ctx.get("authorizer", {}))
250+
return RequestContextV2Authorizer(self.get("authorizer") or {})
253251

254252

255253
class APIGatewayProxyEventV2(BaseProxyEvent):
@@ -291,7 +289,7 @@ def cookies(self) -> list[str]:
291289

292290
@property
293291
def request_context(self) -> RequestContextV2:
294-
return RequestContextV2(self._data)
292+
return RequestContextV2(self["requestContext"])
295293

296294
@property
297295
def path_parameters(self) -> dict[str, str]:

‎aws_lambda_powertools/utilities/data_classes/appsync_authorizer_event.py‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,32 @@ class AppSyncAuthorizerEventRequestContext(DictWrapper):
1111
@property
1212
def api_id(self) -> str:
1313
"""AppSync API ID"""
14-
return self["requestContext"]["apiId"]
14+
return self["apiId"]
1515

1616
@property
1717
def account_id(self) -> str:
1818
"""AWS Account ID"""
19-
return self["requestContext"]["accountId"]
19+
return self["accountId"]
2020

2121
@property
2222
def request_id(self) -> str:
2323
"""Requestt ID"""
24-
return self["requestContext"]["requestId"]
24+
return self["requestId"]
2525

2626
@property
2727
def query_string(self) -> str:
2828
"""GraphQL query string"""
29-
return self["requestContext"]["queryString"]
29+
return self["queryString"]
3030

3131
@property
3232
def operation_name(self) -> str | None:
3333
"""GraphQL operation name, optional"""
34-
return self["requestContext"].get("operationName")
34+
return self.get("operationName")
3535

3636
@property
3737
def variables(self) -> dict:
3838
"""GraphQL variables"""
39-
return self["requestContext"]["variables"]
39+
return self["variables"]
4040

4141

4242
class AppSyncAuthorizerEvent(DictWrapper):
@@ -57,7 +57,7 @@ def authorization_token(self) -> str:
5757
@property
5858
def request_context(self) -> AppSyncAuthorizerEventRequestContext:
5959
"""Request context"""
60-
return AppSyncAuthorizerEventRequestContext(self._data)
60+
return AppSyncAuthorizerEventRequestContext(self["requestContext"])
6161

6262

6363
class AppSyncAuthorizerResponse:

‎aws_lambda_powertools/utilities/data_classes/cognito_user_pool_event.py‎

Lines changed: 109 additions & 115 deletions
Large diffs are not rendered by default.

‎aws_lambda_powertools/utilities/data_classes/common.py‎

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ def json_body(self) -> Any:
205205
"""Parses the submitted body as json"""
206206
if self.decoded_body:
207207
return self._json_deserializer(self.decoded_body)
208-
209208
return None
210209

211210
@cached_property
@@ -370,235 +369,235 @@ def validity_not_before(self) -> str:
370369
class APIGatewayEventIdentity(DictWrapper):
371370
@property
372371
def access_key(self) -> str | None:
373-
return self["requestContext"]["identity"].get("accessKey")
372+
return self.get("accessKey")
374373

375374
@property
376375
def account_id(self) -> str | None:
377376
"""The AWS account ID associated with the request."""
378-
return self["requestContext"]["identity"].get("accountId")
377+
return self.get("accountId")
379378

380379
@property
381380
def api_key(self) -> str | None:
382381
"""For API methods that require an API key, this variable is the API key associated with the method request.
383382
For methods that don't require an API key, this variable is null."""
384-
return self["requestContext"]["identity"].get("apiKey")
383+
return self.get("apiKey")
385384

386385
@property
387386
def api_key_id(self) -> str | None:
388387
"""The API key ID associated with an API request that requires an API key."""
389-
return self["requestContext"]["identity"].get("apiKeyId")
388+
return self.get("apiKeyId")
390389

391390
@property
392391
def caller(self) -> str | None:
393392
"""The principal identifier of the caller making the request."""
394-
return self["requestContext"]["identity"].get("caller")
393+
return self.get("caller")
395394

396395
@property
397396
def cognito_authentication_provider(self) -> str | None:
398397
"""A comma-separated list of the Amazon Cognito authentication providers used by the caller
399398
making the request. Available only if the request was signed with Amazon Cognito credentials."""
400-
return self["requestContext"]["identity"].get("cognitoAuthenticationProvider")
399+
return self.get("cognitoAuthenticationProvider")
401400

402401
@property
403402
def cognito_authentication_type(self) -> str | None:
404403
"""The Amazon Cognito authentication type of the caller making the request.
405404
Available only if the request was signed with Amazon Cognito credentials."""
406-
return self["requestContext"]["identity"].get("cognitoAuthenticationType")
405+
return self.get("cognitoAuthenticationType")
407406

408407
@property
409408
def cognito_identity_id(self) -> str | None:
410409
"""The Amazon Cognito identity ID of the caller making the request.
411410
Available only if the request was signed with Amazon Cognito credentials."""
412-
return self["requestContext"]["identity"].get("cognitoIdentityId")
411+
return self.get("cognitoIdentityId")
413412

414413
@property
415414
def cognito_identity_pool_id(self) -> str | None:
416415
"""The Amazon Cognito identity pool ID of the caller making the request.
417416
Available only if the request was signed with Amazon Cognito credentials."""
418-
return self["requestContext"]["identity"].get("cognitoIdentityPoolId")
417+
return self.get("cognitoIdentityPoolId")
419418

420419
@property
421420
def principal_org_id(self) -> str | None:
422421
"""The AWS organization ID."""
423-
return self["requestContext"]["identity"].get("principalOrgId")
422+
return self.get("principalOrgId")
424423

425424
@property
426425
def source_ip(self) -> str:
427426
"""The source IP address of the TCP connection making the request to API Gateway."""
428-
return self["requestContext"]["identity"]["sourceIp"]
427+
return self["sourceIp"]
429428

430429
@property
431430
def user(self) -> str | None:
432431
"""The principal identifier of the user making the request."""
433-
return self["requestContext"]["identity"].get("user")
432+
return self.get("user")
434433

435434
@property
436435
def user_agent(self) -> str | None:
437436
"""The User Agent of the API caller."""
438-
return self["requestContext"]["identity"].get("userAgent")
437+
return self.get("userAgent")
439438

440439
@property
441440
def user_arn(self) -> str | None:
442441
"""The Amazon Resource Name (ARN) of the effective user identified after authentication."""
443-
return self["requestContext"]["identity"].get("userArn")
442+
return self.get("userArn")
444443

445444
@property
446445
def client_cert(self) -> RequestContextClientCert | None:
447-
client_cert = self["requestContext"]["identity"].get("clientCert")
446+
client_cert = self.get("clientCert")
448447
return None if client_cert is None else RequestContextClientCert(client_cert)
449448

450449

451450
class BaseRequestContext(DictWrapper):
452451
@property
453452
def account_id(self) -> str:
454453
"""The AWS account ID associated with the request."""
455-
return self["requestContext"]["accountId"]
454+
return self["accountId"]
456455

457456
@property
458457
def api_id(self) -> str:
459458
"""The identifier API Gateway assigns to your API."""
460-
return self["requestContext"]["apiId"]
459+
return self["apiId"]
461460

462461
@property
463462
def domain_name(self) -> str | None:
464463
"""A domain name"""
465-
return self["requestContext"].get("domainName")
464+
return self.get("domainName")
466465

467466
@property
468467
def domain_prefix(self) -> str | None:
469-
return self["requestContext"].get("domainPrefix")
468+
return self.get("domainPrefix")
470469

471470
@property
472471
def extended_request_id(self) -> str | None:
473472
"""An automatically generated ID for the API call, which contains more useful information
474473
for debugging/troubleshooting."""
475-
return self["requestContext"].get("extendedRequestId")
474+
return self.get("extendedRequestId")
476475

477476
@property
478477
def protocol(self) -> str:
479478
"""The request protocol, for example, HTTP/1.1."""
480-
return self["requestContext"]["protocol"]
479+
return self["protocol"]
481480

482481
@property
483482
def http_method(self) -> str:
484483
"""The HTTP method used. Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT."""
485-
return self["requestContext"]["httpMethod"]
484+
return self["httpMethod"]
486485

487486
@property
488487
def identity(self) -> APIGatewayEventIdentity:
489-
return APIGatewayEventIdentity(self._data)
488+
return APIGatewayEventIdentity(self["identity"])
490489

491490
@property
492491
def path(self) -> str:
493-
return self["requestContext"]["path"]
492+
return self["path"]
494493

495494
@property
496495
def stage(self) -> str:
497496
"""The deployment stage of the API request"""
498-
return self["requestContext"]["stage"]
497+
return self["stage"]
499498

500499
@property
501500
def request_id(self) -> str:
502501
"""The ID that API Gateway assigns to the API request."""
503-
return self["requestContext"]["requestId"]
502+
return self["requestId"]
504503

505504
@property
506505
def request_time(self) -> str | None:
507506
"""The CLF-formatted request time (dd/MMM/yyyy:HH:mm:ss +-hhmm)"""
508-
return self["requestContext"].get("requestTime")
507+
return self.get("requestTime")
509508

510509
@property
511510
def request_time_epoch(self) -> int:
512511
"""The Epoch-formatted request time."""
513-
return self["requestContext"]["requestTimeEpoch"]
512+
return self["requestTimeEpoch"]
514513

515514
@property
516515
def resource_id(self) -> str:
517-
return self["requestContext"]["resourceId"]
516+
return self["resourceId"]
518517

519518
@property
520519
def resource_path(self) -> str:
521-
return self["requestContext"]["resourcePath"]
520+
return self["resourcePath"]
522521

523522

524523
class RequestContextV2Http(DictWrapper):
525524
@property
526525
def method(self) -> str:
527-
return self["requestContext"]["http"]["method"]
526+
return self["method"]
528527

529528
@property
530529
def path(self) -> str:
531-
return self["requestContext"]["http"]["path"]
530+
return self["path"]
532531

533532
@property
534533
def protocol(self) -> str:
535534
"""The request protocol, for example, HTTP/1.1."""
536-
return self["requestContext"]["http"]["protocol"]
535+
return self["protocol"]
537536

538537
@property
539538
def source_ip(self) -> str:
540539
"""The source IP address of the TCP connection making the request to API Gateway."""
541-
return self["requestContext"]["http"]["sourceIp"]
540+
return self["sourceIp"]
542541

543542
@property
544543
def user_agent(self) -> str:
545544
"""The User Agent of the API caller."""
546-
return self["requestContext"]["http"]["userAgent"]
545+
return self["userAgent"]
547546

548547

549548
class BaseRequestContextV2(DictWrapper):
550549
@property
551550
def account_id(self) -> str:
552551
"""The AWS account ID associated with the request."""
553-
return self["requestContext"]["accountId"]
552+
return self["accountId"]
554553

555554
@property
556555
def api_id(self) -> str:
557556
"""The identifier API Gateway assigns to your API."""
558-
return self["requestContext"]["apiId"]
557+
return self["apiId"]
559558

560559
@property
561560
def domain_name(self) -> str:
562561
"""A domain name"""
563-
return self["requestContext"]["domainName"]
562+
return self["domainName"]
564563

565564
@property
566565
def domain_prefix(self) -> str:
567-
return self["requestContext"]["domainPrefix"]
566+
return self["domainPrefix"]
568567

569568
@property
570569
def http(self) -> RequestContextV2Http:
571-
return RequestContextV2Http(self._data)
570+
return RequestContextV2Http(self["http"])
572571

573572
@property
574573
def request_id(self) -> str:
575574
"""The ID that API Gateway assigns to the API request."""
576-
return self["requestContext"]["requestId"]
575+
return self["requestId"]
577576

578577
@property
579578
def route_key(self) -> str:
580579
"""The selected route key."""
581-
return self["requestContext"]["routeKey"]
580+
return self["routeKey"]
582581

583582
@property
584583
def stage(self) -> str:
585584
"""The deployment stage of the API request"""
586-
return self["requestContext"]["stage"]
585+
return self["stage"]
587586

588587
@property
589588
def time(self) -> str:
590589
"""The CLF-formatted request time (dd/MMM/yyyy:HH:mm:ss +-hhmm)."""
591-
return self["requestContext"]["time"]
590+
return self["time"]
592591

593592
@property
594593
def time_epoch(self) -> int:
595594
"""The Epoch-formatted request time."""
596-
return self["requestContext"]["timeEpoch"]
595+
return self["timeEpoch"]
597596

598597
@property
599598
def authentication(self) -> RequestContextClientCert | None:
600599
"""Optional when using mutual TLS authentication"""
601600
# FunctionURL might have NONE as AuthZ
602-
authentication = self["requestContext"].get("authentication") or {}
601+
authentication = self.get("authentication") or {}
603602
client_cert = authentication.get("clientCert")
604603
return None if client_cert is None else RequestContextClientCert(client_cert)

‎aws_lambda_powertools/utilities/data_classes/kinesis_firehose_event.py‎

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,38 +177,33 @@ def asdict(self) -> dict:
177177

178178

179179
class KinesisFirehoseRecordMetadata(DictWrapper):
180-
@property
181-
def _metadata(self) -> dict:
182-
"""Optional: metadata associated with this record; present only when Kinesis Stream is source"""
183-
return self["kinesisRecordMetadata"] # could raise KeyError
184-
185180
@property
186181
def shard_id(self) -> str:
187182
"""Kinesis stream shard ID; present only when Kinesis Stream is source"""
188-
return self._metadata["shardId"]
183+
return self["shardId"]
189184

190185
@property
191186
def partition_key(self) -> str:
192187
"""Kinesis stream partition key; present only when Kinesis Stream is source"""
193-
return self._metadata["partitionKey"]
188+
return self["partitionKey"]
194189

195190
@property
196191
def approximate_arrival_timestamp(self) -> int:
197192
"""Kinesis stream approximate arrival ISO timestamp; present only when Kinesis Stream is source"""
198-
return self._metadata["approximateArrivalTimestamp"]
193+
return self["approximateArrivalTimestamp"]
199194

200195
@property
201196
def sequence_number(self) -> str:
202197
"""Kinesis stream sequence number; present only when Kinesis Stream is source"""
203-
return self._metadata["sequenceNumber"]
198+
return self["sequenceNumber"]
204199

205200
@property
206201
def subsequence_number(self) -> int:
207202
"""Kinesis stream sub-sequence number; present only when Kinesis Stream is source
208203
209204
Note: this will only be present for Kinesis streams using record aggregation
210205
"""
211-
return self._metadata["subsequenceNumber"]
206+
return self["subsequenceNumber"]
212207

213208

214209
class KinesisFirehoseRecord(DictWrapper):
@@ -230,7 +225,8 @@ def data(self) -> str:
230225
@property
231226
def metadata(self) -> KinesisFirehoseRecordMetadata | None:
232227
"""Optional: metadata associated with this record; present only when Kinesis Stream is source"""
233-
return KinesisFirehoseRecordMetadata(self._data) if self.get("kinesisRecordMetadata") else None
228+
metadata = self.get("kinesisRecordMetadata")
229+
return KinesisFirehoseRecordMetadata(metadata) if metadata else None
234230

235231
@property
236232
def data_as_bytes(self) -> bytes:

‎aws_lambda_powertools/utilities/data_classes/kinesis_stream_event.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,27 @@ class KinesisStreamRecordPayload(DictWrapper):
1515
@property
1616
def approximate_arrival_timestamp(self) -> float:
1717
"""The approximate time that the record was inserted into the stream"""
18-
return float(self["kinesis"]["approximateArrivalTimestamp"])
18+
return float(self["approximateArrivalTimestamp"])
1919

2020
@property
2121
def data(self) -> str:
2222
"""The data blob"""
23-
return self["kinesis"]["data"]
23+
return self["data"]
2424

2525
@property
2626
def kinesis_schema_version(self) -> str:
2727
"""Schema version for the record"""
28-
return self["kinesis"]["kinesisSchemaVersion"]
28+
return self["kinesisSchemaVersion"]
2929

3030
@property
3131
def partition_key(self) -> str:
3232
"""Identifies which shard in the stream the data record is assigned to"""
33-
return self["kinesis"]["partitionKey"]
33+
return self["partitionKey"]
3434

3535
@property
3636
def sequence_number(self) -> str:
3737
"""The unique identifier of the record within its shard"""
38-
return self["kinesis"]["sequenceNumber"]
38+
return self["sequenceNumber"]
3939

4040
def data_as_bytes(self) -> bytes:
4141
"""Decode binary encoded data as bytes"""
@@ -94,7 +94,7 @@ def invoke_identity_arn(self) -> str:
9494
@property
9595
def kinesis(self) -> KinesisStreamRecordPayload:
9696
"""Underlying Kinesis record associated with the event"""
97-
return KinesisStreamRecordPayload(self._data)
97+
return KinesisStreamRecordPayload(self["kinesis"])
9898

9999

100100
class KinesisStreamEvent(DictWrapper):

‎aws_lambda_powertools/utilities/data_classes/s3_event.py‎

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def principal_id(self) -> str:
1818
class S3RequestParameters(DictWrapper):
1919
@property
2020
def source_ip_address(self) -> str:
21-
return self["requestParameters"]["sourceIPAddress"]
21+
return self["sourceIPAddress"]
2222

2323

2424
class S3EventNotificationEventBridgeBucket(DictWrapper):
@@ -40,8 +40,8 @@ def size(self) -> int | None:
4040

4141
@property
4242
def etag(self) -> str:
43-
"""Object etag. Object deletion event doesn't contain etag; we default to empty string"""
44-
return self.get("etag", "") # type: ignore[return-value] # false positive
43+
"""Object eTag. Object deletion event doesn't contain eTag; we default to empty string"""
44+
return self.get("etag") or ""
4545

4646
@property
4747
def version_id(self) -> str:
@@ -156,77 +156,77 @@ def detail(self) -> S3EventBridgeNotificationDetail: # type: ignore[override]
156156
class S3Bucket(DictWrapper):
157157
@property
158158
def name(self) -> str:
159-
return self["s3"]["bucket"]["name"]
159+
return self["name"]
160160

161161
@property
162162
def owner_identity(self) -> S3Identity:
163-
return S3Identity(self["s3"]["bucket"]["ownerIdentity"])
163+
return S3Identity(self["ownerIdentity"])
164164

165165
@property
166166
def arn(self) -> str:
167-
return self["s3"]["bucket"]["arn"]
167+
return self["arn"]
168168

169169

170170
class S3Object(DictWrapper):
171171
@property
172172
def key(self) -> str:
173173
"""Object key"""
174-
return self["s3"]["object"]["key"]
174+
return self["key"]
175175

176176
@property
177177
def size(self) -> int:
178178
"""Object byte size"""
179-
return int(self["s3"]["object"]["size"])
179+
return int(self["size"])
180180

181181
@property
182182
def etag(self) -> str:
183183
"""Object eTag. Object deletion event doesn't contain eTag; we default to empty string"""
184-
return self["s3"]["object"].get("eTag", "")
184+
return self.get("eTag") or ""
185185

186186
@property
187187
def version_id(self) -> str | None:
188188
"""Object version if bucket is versioning-enabled, otherwise null"""
189-
return self["s3"]["object"].get("versionId")
189+
return self.get("versionId")
190190

191191
@property
192192
def sequencer(self) -> str:
193193
"""A string representation of a hexadecimal value used to determine event sequence,
194194
only used with PUTs and DELETEs
195195
"""
196-
return self["s3"]["object"]["sequencer"]
196+
return self["sequencer"]
197197

198198

199199
class S3Message(DictWrapper):
200200
@property
201201
def s3_schema_version(self) -> str:
202-
return self["s3"]["s3SchemaVersion"]
202+
return self["s3SchemaVersion"]
203203

204204
@property
205205
def configuration_id(self) -> str:
206206
"""ID found in the bucket notification configuration"""
207-
return self["s3"]["configurationId"]
207+
return self["configurationId"]
208208

209209
@property
210210
def bucket(self) -> S3Bucket:
211-
return S3Bucket(self._data)
211+
return S3Bucket(self["bucket"])
212212

213213
@property
214214
def get_object(self) -> S3Object:
215215
"""Get the `object` property as an S3Object"""
216216
# Note: this name conflicts with existing python builtins
217-
return S3Object(self._data)
217+
return S3Object(self["object"])
218218

219219

220220
class S3EventRecordGlacierRestoreEventData(DictWrapper):
221221
@property
222222
def lifecycle_restoration_expiry_time(self) -> str:
223223
"""Time when the object restoration will be expired."""
224-
return self["restoreEventData"]["lifecycleRestorationExpiryTime"]
224+
return self["lifecycleRestorationExpiryTime"]
225225

226226
@property
227227
def lifecycle_restore_storage_class(self) -> str:
228228
"""Source storage class for restore"""
229-
return self["restoreEventData"]["lifecycleRestoreStorageClass"]
229+
return self["lifecycleRestoreStorageClass"]
230230

231231

232232
class S3EventRecordGlacierEventData(DictWrapper):
@@ -236,7 +236,7 @@ def restore_event_data(self) -> S3EventRecordGlacierRestoreEventData:
236236
237237
The glacierEventData key is only visible for s3:ObjectRestore:Completed events
238238
"""
239-
return S3EventRecordGlacierRestoreEventData(self._data)
239+
return S3EventRecordGlacierRestoreEventData(self["restoreEventData"])
240240

241241

242242
class S3EventRecord(DictWrapper):
@@ -272,7 +272,7 @@ def user_identity(self) -> S3Identity:
272272

273273
@property
274274
def request_parameters(self) -> S3RequestParameters:
275-
return S3RequestParameters(self._data)
275+
return S3RequestParameters(self["requestParameters"])
276276

277277
@property
278278
def response_elements(self) -> dict[str, str]:
@@ -286,7 +286,7 @@ def response_elements(self) -> dict[str, str]:
286286

287287
@property
288288
def s3(self) -> S3Message:
289-
return S3Message(self._data)
289+
return S3Message(self["s3"])
290290

291291
@property
292292
def glacier_event_data(self) -> S3EventRecordGlacierEventData | None:

‎aws_lambda_powertools/utilities/data_classes/ses_event.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,11 @@ def action(self) -> SESReceiptAction:
212212
class SESMessage(DictWrapper):
213213
@property
214214
def mail(self) -> SESMail:
215-
return SESMail(self["ses"]["mail"])
215+
return SESMail(self["mail"])
216216

217217
@property
218218
def receipt(self) -> SESReceipt:
219-
return SESReceipt(self["ses"]["receipt"])
219+
return SESReceipt(self["receipt"])
220220

221221

222222
class SESEventRecord(DictWrapper):
@@ -232,7 +232,7 @@ def event_version(self) -> str:
232232

233233
@property
234234
def ses(self) -> SESMessage:
235-
return SESMessage(self._data)
235+
return SESMessage(self["ses"])
236236

237237

238238
class SESEvent(DictWrapper):

‎tests/unit/data_classes/required_dependencies/test_cognito_user_pool_event.py‎

Lines changed: 15 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ def test_cognito_custom_message_trigger_event():
103103
assert parsed_event.request.client_metadata == {}
104104

105105
parsed_event.response.sms_message = "sms"
106-
assert parsed_event.response.sms_message == parsed_event["response"]["smsMessage"]
106+
assert parsed_event.response.sms_message == raw_event["response"]["smsMessage"]
107107
parsed_event.response.email_message = "email"
108-
assert parsed_event.response.email_message == parsed_event["response"]["emailMessage"]
108+
assert parsed_event.response.email_message == raw_event["response"]["emailMessage"]
109109
parsed_event.response.email_subject = "subject"
110-
assert parsed_event.response.email_subject == parsed_event["response"]["emailSubject"]
110+
assert parsed_event.response.email_subject == raw_event["response"]["emailSubject"]
111111

112112

113113
def test_cognito_custom_email_sender_trigger_event():
@@ -141,7 +141,7 @@ def test_cognito_pre_authentication_trigger_event():
141141
assert parsed_event.trigger_source == raw_event["triggerSource"]
142142

143143
assert parsed_event.request.user_not_found is None
144-
parsed_event["request"]["userNotFound"] = True
144+
raw_event["request"]["userNotFound"] = True
145145
assert parsed_event.request.user_not_found is True
146146
assert parsed_event.request.user_attributes.get("email") == raw_event["request"]["userAttributes"]["email"]
147147
assert parsed_event.request.validation_data == {}
@@ -171,57 +171,42 @@ def test_cognito_pre_token_generation_trigger_event():
171171
assert parsed_event.request.user_attributes.get("email") == raw_event["request"]["userAttributes"]["email"]
172172
assert parsed_event.request.client_metadata == {}
173173

174-
parsed_event["request"]["groupConfiguration"]["preferredRole"] = "temp"
174+
raw_event["request"]["groupConfiguration"]["preferredRole"] = "temp"
175175
group_configuration = parsed_event.request.group_configuration
176176
assert group_configuration.preferred_role == "temp"
177177

178-
assert parsed_event["response"].get("claimsOverrideDetails") is None
179178
claims_override_details = parsed_event.response.claims_override_details
180-
assert parsed_event["response"]["claimsOverrideDetails"] == {}
181-
182179
assert claims_override_details.claims_to_add_or_override == {}
183180
assert claims_override_details.claims_to_suppress == []
184181
assert claims_override_details.group_configuration is None
185182

186183
claims_override_details.group_configuration = {}
187184
assert claims_override_details.group_configuration._data == {}
188-
assert parsed_event["response"]["claimsOverrideDetails"]["groupOverrideDetails"] == {}
189185

190186
expected_claims = {"test": "value"}
191187
claims_override_details.claims_to_add_or_override = expected_claims
192188
assert claims_override_details.claims_to_add_or_override["test"] == "value"
193-
assert parsed_event["response"]["claimsOverrideDetails"]["claimsToAddOrOverride"] == expected_claims
194189

195190
claims_override_details.claims_to_suppress = ["email"]
196191
assert claims_override_details.claims_to_suppress[0] == "email"
197-
assert parsed_event["response"]["claimsOverrideDetails"]["claimsToSuppress"] == ["email"]
198192

199193
expected_groups = ["group-A", "group-B"]
200194
claims_override_details.set_group_configuration_groups_to_override(expected_groups)
201195
assert claims_override_details.group_configuration.groups_to_override == expected_groups
202-
assert (
203-
parsed_event["response"]["claimsOverrideDetails"]["groupOverrideDetails"]["groupsToOverride"] == expected_groups
204-
)
205-
claims_override_details = parsed_event.response.claims_override_details
206-
assert claims_override_details["groupOverrideDetails"]["groupsToOverride"] == expected_groups
207196

208197
claims_override_details.set_group_configuration_iam_roles_to_override(["role"])
209198
assert claims_override_details.group_configuration.iam_roles_to_override == ["role"]
210-
assert parsed_event["response"]["claimsOverrideDetails"]["groupOverrideDetails"]["iamRolesToOverride"] == ["role"]
211199

212200
claims_override_details.set_group_configuration_preferred_role("role_name")
213201
assert claims_override_details.group_configuration.preferred_role == "role_name"
214-
assert parsed_event["response"]["claimsOverrideDetails"]["groupOverrideDetails"]["preferredRole"] == "role_name"
215202

216203
# Ensure that even if "claimsOverrideDetails" was explicitly set to None
217204
# accessing `event.response.claims_override_details` would set it to `{}`
218-
parsed_event["response"]["claimsOverrideDetails"] = None
205+
raw_event["response"]["claimsOverrideDetails"] = None
219206
claims_override_details = parsed_event.response.claims_override_details
220207
assert claims_override_details._data == {}
221-
assert parsed_event["response"]["claimsOverrideDetails"] == {}
222208
claims_override_details.claims_to_suppress = ["email"]
223209
assert claims_override_details.claims_to_suppress[0] == "email"
224-
assert parsed_event["response"]["claimsOverrideDetails"]["claimsToSuppress"] == ["email"]
225210

226211

227212
def test_cognito_pre_token_v2_generation_trigger_event():
@@ -236,15 +221,12 @@ def test_cognito_pre_token_v2_generation_trigger_event():
236221
assert parsed_event.request.user_attributes.get("email") == raw_event["request"]["userAttributes"]["email"]
237222
assert parsed_event.request.client_metadata == {}
238223

239-
parsed_event["request"]["groupConfiguration"]["preferredRole"] = "temp"
224+
raw_event["request"]["groupConfiguration"]["preferredRole"] = "temp"
240225
group_configuration = parsed_event.request.group_configuration
241226
assert group_configuration.preferred_role == "temp"
242227
assert parsed_event.request.scopes == raw_event["request"]["scopes"]
243228

244-
assert parsed_event["response"].get("claimsAndScopeOverrideDetails") is None
245229
claims_scope_override_details = parsed_event.response.claims_scope_override_details
246-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"] == {}
247-
248230
claims_scope_override_details.id_token_generation = claims_scope_override_details.access_token_generation = {}
249231
assert claims_scope_override_details.id_token_generation.claims_to_add_or_override == {}
250232
assert claims_scope_override_details.id_token_generation.claims_to_suppress == []
@@ -258,45 +240,24 @@ def test_cognito_pre_token_v2_generation_trigger_event():
258240

259241
claims_scope_override_details.group_configuration = {}
260242
assert claims_scope_override_details.group_configuration._data == {}
261-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["groupOverrideDetails"] == {}
262243

263244
expected_claims = {"test": "value"}
264245
claims_scope_override_details.id_token_generation.claims_to_add_or_override = expected_claims
265246
claims_scope_override_details.access_token_generation.claims_to_add_or_override = expected_claims
266247
assert claims_scope_override_details.id_token_generation.claims_to_add_or_override["test"] == "value"
267248
assert claims_scope_override_details.access_token_generation.claims_to_add_or_override["test"] == "value"
268-
assert (
269-
parsed_event["response"]["claimsAndScopeOverrideDetails"]["idTokenGeneration"]["claimsToAddOrOverride"]
270-
== expected_claims
271-
)
272-
assert (
273-
parsed_event["response"]["claimsAndScopeOverrideDetails"]["accessTokenGeneration"]["claimsToAddOrOverride"]
274-
== expected_claims
275-
)
276249

277250
claims_scope_override_details.id_token_generation.claims_to_suppress = (
278251
claims_scope_override_details.access_token_generation.claims_to_suppress
279252
) = ["email"]
280253
assert claims_scope_override_details.id_token_generation.claims_to_suppress[0] == "email"
281254
assert claims_scope_override_details.access_token_generation.claims_to_suppress[0] == "email"
282-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["idTokenGeneration"]["claimsToSuppress"] == [
283-
"email",
284-
]
285-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["accessTokenGeneration"]["claimsToSuppress"] == [
286-
"email",
287-
]
288255

289256
claims_scope_override_details.id_token_generation.scopes_to_suppress = (
290257
claims_scope_override_details.access_token_generation.scopes_to_suppress
291258
) = ["email"]
292259
assert claims_scope_override_details.id_token_generation.scopes_to_suppress[0] == "email"
293260
assert claims_scope_override_details.access_token_generation.scopes_to_suppress[0] == "email"
294-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["idTokenGeneration"]["scopesToSuppress"] == [
295-
"email",
296-
]
297-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["accessTokenGeneration"]["scopesToSuppress"] == [
298-
"email",
299-
]
300261

301262
claims_scope_override_details.id_token_generation.scopes_to_add = (
302263
claims_scope_override_details.access_token_generation.scopes_to_add
@@ -305,47 +266,27 @@ def test_cognito_pre_token_v2_generation_trigger_event():
305266
claims_scope_override_details.id_token_generation.scopes_to_add[0] == "email"
306267
and claims_scope_override_details.access_token_generation.scopes_to_add[0] == "email"
307268
)
308-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["idTokenGeneration"]["scopesToAdd"] == ["email"]
309-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["accessTokenGeneration"]["scopesToAdd"] == [
310-
"email",
311-
]
312269

313270
expected_groups = ["group-A", "group-B"]
314271
claims_scope_override_details.set_group_configuration_groups_to_override(expected_groups)
315272
assert claims_scope_override_details.group_configuration.groups_to_override == expected_groups
316-
assert (
317-
parsed_event["response"]["claimsAndScopeOverrideDetails"]["groupOverrideDetails"]["groupsToOverride"]
318-
== expected_groups
319-
)
320273
claims_scope_override_details = parsed_event.response.claims_scope_override_details
321-
assert claims_scope_override_details["groupOverrideDetails"]["groupsToOverride"] == expected_groups
322274

323275
claims_scope_override_details.set_group_configuration_iam_roles_to_override(["role"])
324276
assert claims_scope_override_details.group_configuration.iam_roles_to_override == ["role"]
325-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["groupOverrideDetails"]["iamRolesToOverride"] == [
326-
"role",
327-
]
328277

329278
claims_scope_override_details.set_group_configuration_preferred_role("role_name")
330279
assert claims_scope_override_details.group_configuration.preferred_role == "role_name"
331-
assert (
332-
parsed_event["response"]["claimsAndScopeOverrideDetails"]["groupOverrideDetails"]["preferredRole"]
333-
== "role_name"
334-
)
335280

336281
# Ensure that even if "claimsAndScopeOverrideDetails" was explicitly set to None
337282
# accessing `event.response.claims_scope_override_details` would set it to `{}`
338-
parsed_event["response"]["claimsAndScopeOverrideDetails"] = None
283+
raw_event["response"]["claimsAndScopeOverrideDetails"] = None
339284
claims_scope_override_details = parsed_event.response.claims_scope_override_details
340285
assert claims_scope_override_details._data == {}
341-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"] == {}
342286

343287
claims_scope_override_details.id_token_generation = {}
344288
claims_scope_override_details.id_token_generation.claims_to_suppress = ["email"]
345289
assert claims_scope_override_details.id_token_generation.claims_to_suppress[0] == "email"
346-
assert parsed_event["response"]["claimsAndScopeOverrideDetails"]["idTokenGeneration"]["claimsToSuppress"] == [
347-
"email",
348-
]
349290

350291

351292
def test_cognito_define_auth_challenge_trigger_event():
@@ -367,14 +308,14 @@ def test_cognito_define_auth_challenge_trigger_event():
367308

368309
# Verify setters
369310
parsed_event.response.challenge_name = "CUSTOM_CHALLENGE"
370-
assert parsed_event.response.challenge_name == parsed_event["response"]["challengeName"]
311+
assert parsed_event.response.challenge_name == raw_event["response"]["challengeName"]
371312
assert parsed_event.response.challenge_name == "CUSTOM_CHALLENGE"
372313
parsed_event.response.fail_authentication = True
373314
assert parsed_event.response.fail_authentication
374-
assert parsed_event.response.fail_authentication == parsed_event["response"]["failAuthentication"]
315+
assert parsed_event.response.fail_authentication == raw_event["response"]["failAuthentication"]
375316
parsed_event.response.issue_tokens = True
376317
assert parsed_event.response.issue_tokens
377-
assert parsed_event.response.issue_tokens == parsed_event["response"]["issueTokens"]
318+
assert parsed_event.response.issue_tokens == raw_event["response"]["issueTokens"]
378319

379320

380321
def test_create_auth_challenge_trigger_event():
@@ -395,13 +336,13 @@ def test_create_auth_challenge_trigger_event():
395336

396337
# Verify setters
397338
parsed_event.response.public_challenge_parameters = {"test": "value"}
398-
assert parsed_event.response.public_challenge_parameters == parsed_event["response"]["publicChallengeParameters"]
339+
assert parsed_event.response.public_challenge_parameters == raw_event["response"]["publicChallengeParameters"]
399340
assert parsed_event.response.public_challenge_parameters["test"] == "value"
400341
parsed_event.response.private_challenge_parameters = {"private": "value"}
401-
assert parsed_event.response.private_challenge_parameters == parsed_event["response"]["privateChallengeParameters"]
342+
assert parsed_event.response.private_challenge_parameters == raw_event["response"]["privateChallengeParameters"]
402343
assert parsed_event.response.private_challenge_parameters["private"] == "value"
403344
parsed_event.response.challenge_metadata = "meta"
404-
assert parsed_event.response.challenge_metadata == parsed_event["response"]["challengeMetadata"]
345+
assert parsed_event.response.challenge_metadata == raw_event["response"]["challengeMetadata"]
405346
assert parsed_event.response.challenge_metadata == "meta"
406347

407348

@@ -423,5 +364,5 @@ def test_verify_auth_challenge_response_trigger_event():
423364

424365
# Verify setters
425366
parsed_event.response.answer_correct = True
426-
assert parsed_event.response.answer_correct == parsed_event["response"]["answerCorrect"]
367+
assert parsed_event.response.answer_correct == raw_event["response"]["answerCorrect"]
427368
assert parsed_event.response.answer_correct

‎tests/unit/data_classes/required_dependencies/test_kinesis_stream_event.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ def test_kinesis_stream_event():
2929

3030
kinesis = record.kinesis
3131
kinesis_raw = raw_event["Records"][0]["kinesis"]
32-
assert kinesis._data["kinesis"] == kinesis_raw
3332

3433
assert kinesis.approximate_arrival_timestamp == kinesis_raw["approximateArrivalTimestamp"]
3534
assert kinesis.data == kinesis_raw["data"]

0 commit comments

Comments
 (0)
Please sign in to comment.