Skip to content

feat(parser): add support for SQS-wrapped S3 event notifications #2108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

theipster
Copy link
Contributor

Issue number: #2078

Summary

Changes

Exposes a new data model for SQS-wrapped S3 event notifications.

User experience

Please see #2078 description.

Checklist

If your change doesn't seem to apply, please leave them unchecked.

Is this a breaking change?

RFC issue number:

Checklist:

  • Migration process documented
  • Implement warnings (if it can live side by side)

Acknowledgment

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

@boring-cyborg
Copy link

boring-cyborg bot commented Apr 11, 2023

Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need.
In the meantime, check out the #python channel on our AWS Lambda Powertools Discord: Invite link

@pull-request-size pull-request-size bot added the size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. label Apr 11, 2023
@github-actions github-actions bot added the feature New feature or functionality label Apr 11, 2023
@codecov-commenter
Copy link

codecov-commenter commented Apr 11, 2023

Codecov Report

Patch coverage: 100.00% and no project coverage change.

Comparison is base (32666e4) 97.47% compared to head (9c759d7) 97.47%.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #2108   +/-   ##
========================================
  Coverage    97.47%   97.47%           
========================================
  Files          148      149    +1     
  Lines         6882     6891    +9     
  Branches       505      505           
========================================
+ Hits          6708     6717    +9     
  Misses         137      137           
  Partials        37       37           
Impacted Files Coverage Δ
...bda_powertools/utilities/parser/models/__init__.py 100.00% <100.00%> (ø)
...s/utilities/parser/models/s3_event_notification.py 100.00% <100.00%> (ø)
...s_lambda_powertools/utilities/parser/models/sqs.py 100.00% <100.00%> (ø)

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

@theipster theipster marked this pull request as ready for review April 13, 2023 23:24
@theipster theipster requested a review from a team as a code owner April 13, 2023 23:24
@theipster theipster requested review from rubenfonseca and removed request for a team April 13, 2023 23:24
The latter represents an event payload, i.e. a list of records.
@pull-request-size pull-request-size bot added size/S Denotes a PR that changes 10-29 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Apr 14, 2023
Since the SqsS3EventNotificationModel is only responsible for chaining
(rather than redefining) the existing SqsModel and S3Model models, let's
re-use both the existing stubs and their corresponding assertions.
@boring-cyborg boring-cyborg bot added the tests label Apr 14, 2023
@pull-request-size pull-request-size bot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Apr 14, 2023
@theipster
Copy link
Contributor Author

@leandrodamascena Thanks for your guidance. Do these latest changes look roughly sane to you? 🙂

I've cheekily borrowed the S3Model assertions, seeing as the primary concern of SqsS3EventNotificationModel is more about chaining SqsRecordModel and S3Model together, rather than the properties themselves.

@leandrodamascena leandrodamascena linked an issue Apr 14, 2023 that may be closed by this pull request
2 tasks
@leandrodamascena
Copy link
Contributor

@leandrodamascena Thanks for your guidance. Do these latest changes look roughly sane to you? slightly_smiling_face

I've cheekily borrowed the S3Model assertions, seeing as the primary concern of SqsS3EventNotificationModel is more about chaining SqsRecordModel and S3Model together, rather than the properties themselves.

Hi @theipster! Surely these things make sense. I didn't get a chance to see why the CI is crashing, it looks like mypy complaining about types. But don't worry, I always suffer from mypy errors also 😢 .

I will look into this today and we can work together to complete it and release in the next Powertools version. So far, very good job 👏!

@leandrodamascena
Copy link
Contributor

Hi @theipster! I'm trying to send commits to this PullRequest and I can't because it looks like you didn't allow the maintainers to do that.

There is an option in this pull request that must be checked to allow this. Can you enable it, please?
image

@leandrodamascena
Copy link
Contributor

@theipster hi!! When you have time, could you pls check my last comment?

Here's the patch I'm looking to apply to this PR to test things out: https://gist.github.com/leandrodamascena/69cad79e01fcff4697c3672f5db14f67

@theipster
Copy link
Contributor Author

@leandrodamascena Apologies for the late reply. I've now enabled this setting; thanks for your patch.

Is there any reason why mypy requires Sequence[] here, whereas it's fine with using List[] elsewhere, e.g. for S3Model? Is there some kind of exception in place for S3Model?

@boring-cyborg boring-cyborg bot added the documentation Improvements or additions to documentation label Apr 25, 2023
@leandrodamascena
Copy link
Contributor

@leandrodamascena Apologies for the late reply. I've now enabled this setting; thanks for your patch.

Is there any reason why mypy requires Sequence[] here, whereas it's fine with using List[] elsewhere, e.g. for S3Model? Is there some kind of exception in place for S3Model?

Hi @theipster no worries! Thanks for enabling this.

The problem is not in the S3Model and yes, the code works as expected. But to avoid issues with different data types, we use mypy, a library for static type checking. This helps us identify potential bugs during the development process and make the code more reliable. Because of this, the code seems to work but when mypy checks the types it encounters some issues.

The problem we're encountering is due to the Records field in the parent class, SqsModel, which is set as a List. Since List is an invariant data type, we're running into issues when trying to override this field with a str type Records field in the SqsS3EventNotificationModel class. As a result, mypy is complaining about this.

I changed the field type to Sequence in SqsModel and now the errors have stopped. Sequence is a more generic data type and List is a subclass of it.

I don't know if I made myself very clear about this, but I leave some references here:

https://blog.daftcode.pl/covariance-contravariance-and-invariance-the-ultimate-python-guide-8fabc0c24278

https://mypy.readthedocs.io/en/stable/common_issues.html#variance

Comment on lines 15 to 27
def test_handle_sqs_json_body_containing_s3_notifications():
sqs_event_dict = load_event("sqsEvent.json")
s3_event_notification_dict = load_event("s3Event.json")
for record in sqs_event_dict["Records"]:
record["body"] = json_serialize(s3_event_notification_dict)

parsed_event: SqsS3EventNotificationModel = handle_sqs_json_body_containing_s3_notifications(
sqs_event_dict, LambdaContext()
)

assert len(parsed_event.Records) == 2
for parsed_sqs_record in parsed_event.Records:
assert_s3(parsed_sqs_record.body)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like an opinion here @rubenfonseca @heitorlessa. I think this test is ok, do you see another way to optimize this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good opportunity to move to the new unit tests since all we should care here is whether the new Model can be instantiated correctly (along with its ramifications, JSON deserialization etc.).

Following this, all you'd need to do here is:

  • Create a new S3->SQS JSON event file under tests/events
  • Create a new test at tests/unit/parser/test_s3.py
  • Test whether the model can be initialized without any problem (we already test both SQS and S3 Models)
  • Test whether an invalid JSON in the S3 data will lead to a model validation failure

Previously, we were shoving everything under tests/functional where we have a lot of boilerplate like @event_parser and these functions, when in fact we're merely testing the Model since we already have separate tests for the @event_parser... which in this case is a proper functional test.

Hope that makes sense ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes perfect sense @heitorlessa! I changed the files as per this feedback

@pull-request-size pull-request-size bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Apr 26, 2023
@leandrodamascena leandrodamascena changed the title feat(parser): improve support for SQS-wrapped S3 event notifications feat(parser): add support for SQS-wrapped S3 event notifications Apr 26, 2023
Comment on lines +159 to +178
| Model name | Description |
| --------------------------------------- | ------------------------------------------------------------------------------------- |
| **AlbModel** | Lambda Event Source payload for Amazon Application Load Balancer |
| **APIGatewayProxyEventModel** | Lambda Event Source payload for Amazon API Gateway |
| **APIGatewayProxyEventV2Model** | Lambda Event Source payload for Amazon API Gateway v2 payload |
| **CloudwatchLogsModel** | Lambda Event Source payload for Amazon CloudWatch Logs |
| **DynamoDBStreamModel** | Lambda Event Source payload for Amazon DynamoDB Streams |
| **EventBridgeModel** | Lambda Event Source payload for Amazon EventBridge |
| **KafkaMskEventModel** | Lambda Event Source payload for AWS MSK payload |
| **KafkaSelfManagedEventModel** | Lambda Event Source payload for self managed Kafka payload |
| **KinesisDataStreamModel** | Lambda Event Source payload for Amazon Kinesis Data Streams |
| **KinesisFirehoseModel** | Lambda Event Source payload for Amazon Kinesis Firehose |
| **LambdaFunctionUrlModel** | Lambda Event Source payload for Lambda Function URL payload |
| **S3EventNotificationEventBridgeModel** | Lambda Event Source payload for Amazon S3 Event Notification to EventBridge. |
| **S3Model** | Lambda Event Source payload for Amazon S3 |
| **S3ObjectLambdaEvent** | Lambda Event Source payload for Amazon S3 Object Lambda |
| **S3SqsEventNotificationModel** | Lambda Event Source payload for S3 event notifications wrapped in SQS event (S3->SQS) |
| **SesModel** | Lambda Event Source payload for Amazon Simple Email Service |
| **SnsModel** | Lambda Event Source payload for Amazon Simple Notification Service |
| **SqsModel** | Lambda Event Source payload for Amazon SQS |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took the opportunity to organize the names of the models in alphabetical order.

@leandrodamascena
Copy link
Contributor

Hi @theipster, congrats on this amazing job, you've done most of the work.
I just made some small changes to bring closer the developer experience we have and the project architecture.

Thank you very much for your time dedicated to this PR. Do you have any considerations before we make the last review and merge this PR?

@leandrodamascena leandrodamascena requested review from heitorlessa and removed request for rubenfonseca April 26, 2023 16:45
@theipster
Copy link
Contributor Author

@leandrodamascena Thank you for your support in making this go much more smoothly!

Nothing to add from my side, LGTM ✅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we rename this file to s3SqsEvent.json for consistency?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed



def test_s3_sqs_event_notification():
raw_event = load_event("sqsS3Event.json")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this line too

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed



def test_s3_sqs_event_notification_body_invalid_json():
raw_event = load_event("s3Event.json")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this line too

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was wrong. I was using a S3 JSON file to simulate an invalid body, but it is supposed to use an invalid json body (like a string).

Fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you compare against my original code in commit 251d8ba, I used sqsEvent.json, which is another way to solve this problem.

This is also why I originally named the classes SqsS3... (because the SQS payload literally wraps the S3 payload) rather than S3Sqs... (the direction of traffic). They're simply different ways of conceptualising the same scenario, neither is particularly right or wrong I guess!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right @theipster, I used the wrong words to explain why I changed the test. It's not a question of being right or wrong, it's just different ways of solving the same question.

Thank you very much for this feedback, it will help me to avoid mistakes like this in the future.

@rubenfonseca rubenfonseca self-requested a review May 2, 2023 10:07
@leandrodamascena leandrodamascena self-requested a review May 2, 2023 10:07
Copy link
Contributor

@rubenfonseca rubenfonseca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@leandrodamascena leandrodamascena merged commit ea91e5c into aws-powertools:develop May 2, 2023
@boring-cyborg
Copy link

boring-cyborg bot commented May 2, 2023

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation feature New feature or functionality size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request: Improve support for SQS-wrapped S3 event notifications
5 participants