Skip to content

RFC: Data classes for common AWS event triggers #164

Closed
@michaelbrewer

Description

@michaelbrewer

Key information

Summary

NOTE: This solution should not have any additional dependencies and be as lightweight as possible.

Create a set of data classes for all of the common AWS Event Triggers these data classes will include:

  1. Docstrings for each of the fields
  2. Type hinting and code completion
  3. Later support for validation via the Json schema PR
  4. Helper functions to handle some of the decoding (CloudwatchLogsEvent decoding)
  5. Define enums where necessary

Motivation

When defining a new lambda you start with a handler method with event Dict[str, Any] and LambdaContext context parameter. You will then have to find the corresponding documentation for the event structure and field type etc..

Proposal

Each Lambda trigger event there is a corresponding typed data class which doc strings extracted from the AWS documentation, some helper methods to updating values as well as decoding any embedded data. In the test suite there is alot a series of example trigger events.

CloudWatchLogsEvent example usage:

from aws_lambda_powertools.utilities.trigger import CloudWatchLogsEvent

def handler(event: Dict[str, Any], context: LambdaContext):
    # CloudWatchLogsEvent provides a typed data class with a method to decode gzipped log messages
    decoded_data = CloudWatchLogsEvent(event).decode_cloud_watch_logs_data()
    # CloudWatchLogsDecodedData is another typed data class with docstrings and getters
    for log_event in decoded_data.log_events:
        handle_log_message(log_event.message)

PreTokenGenerationTriggerEvent example usage:

def handler(event: Dict[str, Any], context: LambdaContext) -> Dict[str, Any]:
    # PreTokenGenerationTriggerEvent data class includes docstrings and helper methods
    pre_token_event = PreTokenGenerationTriggerEvent(event)

    claims_override_details = pre_token_event.response.claims_override_details
    if pre_token_event.user_name == "SpecialUser":
        # ClaimsOverrideDetails has convenient setters
        claims_override_details.claims_to_suppress = ["Email"]
        claims_override_details.claims_to_add_or_override = {"attribute": "value"}

    if "SpecialGroup" in pre_token_event.request.group_configuration.groups_to_override:
        # A setter method for only part of groupOverrideDetails
        claims_override_details.set_group_configuration_preferred_role("preferred_value")

    # Updated are made to the original dict
    return event

Example screenshot of the docs:

Screen Shot 2020-09-10 at 12 15 59 PM

List of initial trigger events to support:

  • API Gateway Poxy V1 and V2 events
  • CloudWatch log event with decoding of the gzipped logs
  • Cognito user pool events
  • DynamoDB stream events
  • Event bridge
  • Kinesis stream events
  • S3 event notifications
  • SES events
  • SNS events
  • SQS events

Drawbacks

Why should we not do this?

This will include alot of extra code to maintain and we will have to keep it in sync when new event triggers are created. However all new Lambda users will have to do the same.

Do we need additional dependencies? Impact performance/package size?

No extra dependencies are needed

Rationale and alternatives

  • What other designs have been considered? Why not them?
    There are alternatives that include a large set of dependencies, and will still need the similar amount of work (like tracking down example events and documentation)

Unresolved questions

Mapping for keys like type, id, from etc.. to the corresponding getter method like get_id

Optional, stash area for topics that need further development e.g. TBD
Add integration for validation code from other PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions