diff --git a/.cfnlintrc.yaml b/.cfnlintrc.yaml new file mode 100644 index 00000000000..3909b9bb437 --- /dev/null +++ b/.cfnlintrc.yaml @@ -0,0 +1,2 @@ +ignore_templates: + - examples/event_handler_appsync_events/sam/getting_started_with_appsync_events.yaml diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md index b1fa7de1f58..beb340ad645 100755 --- a/.chglog/CHANGELOG.tpl.md +++ b/.chglog/CHANGELOG.tpl.md @@ -1,13 +1,18 @@ + + + {{ if .Versions -}} -## Unreleased +# Unreleased {{ if .Unreleased.CommitGroups -}} {{ range .Unreleased.CommitGroups -}} -### {{ .Title }} +## {{ .Title }} {{ range .Commits -}} +{{ if and (not (hasPrefix .Subject "changelog rebuild")) (not (hasPrefix .Subject "layer docs update")) (not (hasPrefix .Subject "bump version to")) -}} * {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end -}} {{ end }} {{ end -}} {{ end -}} @@ -17,22 +22,25 @@ ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} {{ range .CommitGroups -}} -### {{ .Title }} + +## {{ .Title }} {{ range .Commits -}} +{{ if and (not (hasPrefix .Subject "changelog rebuild")) (not (hasPrefix .Subject "layer docs update")) (not (hasPrefix .Subject "bump version to")) -}} * {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end -}} {{ end }} {{ end -}} {{- if .RevertCommits -}} -### Reverts +## Reverts {{ range .RevertCommits -}} * {{ .Revert.Header }} {{ end }} {{ end -}} {{- if .MergeCommits -}} -### Pull Requests +## Pull Requests {{ range .MergeCommits -}} * {{ .Header }} @@ -41,7 +49,7 @@ {{- if .NoteGroups -}} {{ range .NoteGroups -}} -### {{ .Title }} +## {{ .Title }} {{ range .Notes }} {{ .Body }} {{ end }} diff --git a/.chglog/config.yml b/.chglog/config.yml index 3392563d445..9ec1c8ef31e 100755 --- a/.chglog/config.yml +++ b/.chglog/config.yml @@ -2,7 +2,7 @@ style: github template: CHANGELOG.tpl.md info: title: CHANGELOG - repository_url: https://github.com/awslabs/aws-lambda-powertools-python + repository_url: https://github.com/aws-powertools/powertools-lambda-python options: commits: filters: @@ -32,3 +32,6 @@ options: notes: keywords: - BREAKING CHANGE + # issues: + # prefix: + # - # diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 93e6be8034d..00000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.187.0/containers/python-3/.devcontainer/base.Dockerfile - -# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6 -ARG VARIANT="3.9" -FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} -ENV POETRY_VIRTUALENVS_IN_PROJECT=true - -# [Option] Install Node.js -ARG INSTALL_NODE="true" -ARG NODE_VERSION="lts/*" -RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi - -RUN pip3 install -U pip black poetry pre-commit - -# [Optional] Uncomment this section to install additional OS packages. -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends bash-completion diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 995ac6c4fba..00000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,47 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -{ - "name": "Python 3", - "build": { - "dockerfile": "Dockerfile", - "context": "..", - "args": { - "VARIANT": "3.9", - "INSTALL_NODE": "true", - "NODE_VERSION": "lts/*" - } - }, - "settings": { - "git.enableCommitSigning": true, - "editor.formatOnSave": true, - "python.pythonPath": "/usr/local/bin/python", - "python.languageServer": "Pylance", - "python.linting.enabled": true, - "python.linting.pylintEnabled": true, - "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", - "python.formatting.blackPath": "/usr/local/py-utils/bin/black", - "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", - "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", - "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", - "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", - "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", - "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", - "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint", - "python.testing.pytestArgs": [ - "tests" - ], - "python.testing.unittestEnabled": false, - "python.testing.nosetestsEnabled": false, - "python.testing.pytestEnabled": true, - "python.formatting.provider": "black" - }, - "extensions": [ - "ms-python.python", - "ms-python.vscode-pylance", - "littlefoxteam.vscode-python-test-adapter", - "ms-azuretools.vscode-docker", - "davidanson.vscode-markdownlint", - "bungcip.better-toml" - ], - "postCreateCommand": "make dev", - "remoteUser": "vscode" -} diff --git a/.flake8 b/.flake8 index 6c0c78fa967..1db8406d9e4 100644 --- a/.flake8 +++ b/.flake8 @@ -3,6 +3,11 @@ exclude = docs, .eggs, setup.py, example, .aws-sam, .git, dist, *.md, *.yaml, ex ignore = E203, E266, W503, BLK100, W291, I004 max-line-length = 120 max-complexity = 15 +; flake8-builtins isn't honouring inline ignore (A003) +per-file-ignores = + tests/e2e/utils/data_builder/__init__.py:F401 + tests/e2e/utils/data_fetcher/__init__.py:F401 + aws_lambda_powertools/utilities/data_classes/s3_event.py:A003 [isort] multi_line_output = 3 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000..1062320f5b1 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +* @aws-powertools/lambda-python-core diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 43ec3443e8b..00000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug, triage -assignees: '' - ---- - - - - -**What were you trying to accomplish?** - -## Expected Behavior - - - -## Current Behavior - - - -## Possible Solution - - - -## Steps to Reproduce (for bugs) - - -1. -2. -3. -4. - -## Environment - -* **Powertools version used**: -* **Packaging format (Layers, PyPi)**: -* **AWS Lambda function runtime:** -* **Debugging logs** - -> [How to enable debug mode](https://awslabs.github.io/aws-lambda-powertools-python/#debug-mode)** - -```python -# paste logs here -``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..77c028f7fed --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,92 @@ +name: Bug report +description: Report a reproducible bug to help us improve +title: "Bug: TITLE" +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for submitting a bug report. Please add as much information as possible to help us reproduce, and remove any potential sensitive data. + + Please become familiar with [our definition of bug](https://github.com/aws-powertools/powertools-lambda-python/blob/develop/MAINTAINERS.md#is-that-a-bug). + - type: textarea + id: expected_behaviour + attributes: + label: Expected Behaviour + description: Please share details on the behaviour you expected + validations: + required: true + - type: textarea + id: current_behaviour + attributes: + label: Current Behaviour + description: Please share details on the current issue + validations: + required: true + - type: textarea + id: code_snippet + attributes: + label: Code snippet + description: Please share a code snippet to help us reproduce the issue + render: python + validations: + required: true + - type: textarea + id: solution + attributes: + label: Possible Solution + description: If known, please suggest a potential resolution + validations: + required: false + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: Please share how we might be able to reproduce this issue + validations: + required: true + - type: input + id: version + attributes: + label: Powertools for AWS Lambda (Python) version + placeholder: "latest, 1.25.6" + value: latest + validations: + required: true + - type: dropdown + id: runtime + attributes: + label: AWS Lambda function runtime + options: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + validations: + required: true + - type: dropdown + id: packaging + attributes: + label: Packaging format used + options: + - Lambda Layers + - Serverless Application Repository (SAR) App + - PyPi + multiple: true + validations: + required: true + - type: textarea + id: logs + attributes: + label: Debugging logs + description: If available, please share [debugging logs](https://docs.powertools.aws.dev/lambda/python/#debug-mode) + render: python + validations: + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..8acf5081f3b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/aws-powertools/powertools-lambda-python/discussions/new + about: Ask a general question about Lambda Powertools diff --git a/.github/ISSUE_TEMPLATE/documentation-improvements.md b/.github/ISSUE_TEMPLATE/documentation-improvements.md deleted file mode 100644 index 8341ae4e0c5..00000000000 --- a/.github/ISSUE_TEMPLATE/documentation-improvements.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Documentation improvements -about: Suggest a documentation update -title: '' -labels: documentation -assignees: '' - ---- - -**What were you initially searching for in the docs?** - - -**Is this related to an existing part of the documentation? Please share a link** - -**Describe how we could make it clearer** - -**If you have a proposed update, please share it here** diff --git a/.github/ISSUE_TEMPLATE/documentation_improvements.yml b/.github/ISSUE_TEMPLATE/documentation_improvements.yml new file mode 100644 index 00000000000..e750d51923f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_improvements.yml @@ -0,0 +1,50 @@ +name: Documentation improvements +description: Suggest a documentation update to improve everyone's experience +title: "Docs: TITLE" +labels: ["documentation", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for helping us improve everyone's experience. We review documentation updates on a case by case basis. + - type: textarea + id: search_area + attributes: + label: What were you searching in the docs? + description: Please help us understand how you looked for information that was either unclear or not available + validations: + required: true + - type: input + id: area + attributes: + label: Is this related to an existing documentation section? + description: Please share a link, if applicable + validations: + required: false + - type: textarea + id: idea + attributes: + label: How can we improve? + description: Please share your thoughts on how we can improve this experience + validations: + required: true + - type: textarea + id: suggestion + attributes: + label: Got a suggestion in mind? + description: Please suggest a proposed update + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: I understand the final update might be different from my proposed suggestion, or refused. + required: true + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index b837b7ad5a1..00000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: feature-request, triage -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** - - -**Describe the solution you'd like** - - -**Describe alternatives you've considered** - - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..a39fb211ac1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,48 @@ +name: Feature request +description: Suggest an idea for Powertools for AWS Lambda (Python) +title: "Feature request: TITLE" +labels: ["feature-request", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to suggest an idea to the Powertools for AWS Lambda (Python) project. + + *Future readers*: Please react with 👍 and your use case to help us understand customer demand. + - type: textarea + id: problem + attributes: + label: Use case + description: Please help us understand your use case or problem you're facing + validations: + required: true + - type: textarea + id: suggestion + attributes: + label: Solution/User Experience + description: Please share what a good solution would look like to this use case + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternative solutions + description: Please describe what alternative solutions to this use case, if any + render: markdown + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This feature request meets [Powertools for AWS Lambda (Python) Tenets](https://docs.powertools.aws.dev/lambda/python/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Java](https://github.com/aws-powertools/powertools-lambda-java/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml new file mode 100644 index 00000000000..bbc3ff3b3d8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -0,0 +1,65 @@ +name: Maintenance +description: Suggest an activity to help address governance and anything internal +title: "Maintenance: TITLE" +labels: ["internal", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to help us improve operational excellence. + + *Future readers*: Please react with 👍 and your use case to help us understand customer demand. + - type: textarea + id: importance + attributes: + label: Why is this needed? + description: Please help us understand the value so we can prioritize it accordingly + validations: + required: true + - type: dropdown + id: area + attributes: + label: Which area does this relate to? + multiple: true + options: + - Tests + - Static typing + - Tracer + - Logger + - Metrics + - Event Handler - REST API + - Event Handler - GraphQL API + - Middleware factory + - Parameters + - Batch processing + - Validation + - Event Source Data Classes + - Parser + - Idempotency + - Feature flags + - JMESPath functions + - Streaming + - Automation + - Other + - type: textarea + id: suggestion + attributes: + label: Solution + description: If available, please share what a good solution would look like + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This request meets [Powertools for AWS Lambda (Python) Tenets](https://docs.powertools.aws.dev/lambda/python/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Java](https://github.com/aws-powertools/powertools-lambda-java/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md deleted file mode 100644 index 0381a352dc5..00000000000 --- a/.github/ISSUE_TEMPLATE/rfc.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: RFC -about: Feature design and proposals -title: 'RFC: ' -labels: RFC, triage -assignees: '' -approved by: '' ---- - -## Key information - -* RFC PR: (leave this empty) -* Related issue(s), if known: -* Area: (i.e. Tracer, Metrics, Logger, etc.) -* Meet [tenets](https://awslabs.github.io/aws-lambda-powertools-python/#tenets): (Yes/no) -* Approved by: '' -* Reviewed by: '' - -## Summary -[summary]: #summary - -> One paragraph explanation of the feature. - -## Motivation -[motivation]: #motivation - -> Why are we doing this? What use cases does it support? What is the expected outcome? - -## Proposal -[proposal]: #proposal - -> This is the bulk of the RFC. - -> Explain the design in enough detail for somebody familiar with Powertools to understand it, and for somebody familiar with the implementation to implement it. - -**If this feature should be available in other runtimes (e.g. Java, Typescript), how would this look like to ensure consistency?** - -## User Experience - -**How would customers use it?** - -**Any configuration or corner cases you'd expect?** - -**Demonstration of before and after on how the experience will be better** - -## Drawbacks -[drawbacks]: #drawbacks - -> Why should we *not* do this? - -> Do we need additional dependencies? Impact performance/package size? - -## Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives - -* **What other designs have been considered? Why not them?** -* **What is the impact of not doing this?** - -## Unresolved questions -[unresolved-questions]: #unresolved-questions - -> Optional, stash area for topics that need further development e.g. TBD diff --git a/.github/ISSUE_TEMPLATE/rfc.yml b/.github/ISSUE_TEMPLATE/rfc.yml new file mode 100644 index 00000000000..31d8d7fe0c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc.yml @@ -0,0 +1,109 @@ +name: Request for Comments (RFC) +description: Feature design and detailed proposals +title: "RFC: TITLE" +labels: ["RFC", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for submitting a RFC. Please add as many details as possible to help further enrich this design. + - type: input + id: relation + attributes: + label: Is this related to an existing feature request or issue? + description: Please share a link, if applicable + - type: dropdown + id: area + attributes: + label: Which Powertools for AWS Lambda (Python) utility does this relate to? + options: + - Tracer + - Logger + - Metrics + - Event Handler - REST API + - Event Handler - GraphQL API + - Middleware factory + - Parameters + - Batch processing + - Typing + - Validation + - Event Source Data Classes + - Parser + - Idempotency + - Feature flags + - JMESPath functions + - Other + validations: + required: true + - type: textarea + id: summary + attributes: + label: Summary + description: Please provide an overview in one or two paragraphs + validations: + required: true + - type: textarea + id: problem + attributes: + label: Use case + description: Please share the use case and motivation behind this proposal + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: Please explain the design in detail, so anyone familiar with the project could implement it + placeholder: What the user experience looks like before and after this design? + validations: + required: true + - type: textarea + id: scope + attributes: + label: Out of scope + description: Please explain what should be considered out of scope in your proposal + validations: + required: true + - type: textarea + id: challenges + attributes: + label: Potential challenges + description: Nothing is perfect. Please share what common challenges, edge cases, unresolved areas, and suggestions on how to mitigate them + validations: + required: true + - type: textarea + id: integrations + attributes: + label: Dependencies and Integrations + description: If applicable, please share whether this feature has additional dependencies, and how it might integrate with other utilities available + validations: + required: false + - type: textarea + id: alternatives + attributes: + label: Alternative solutions + description: Please describe what alternative solutions to this use case, if any + render: markdown + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This feature request meets [Powertools for AWS Lambda (Python) Tenets](https://docs.powertools.aws.dev/lambda/python/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Java](https://github.com/aws-powertools/powertools-lambda-java/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. + + Metadata information for admin purposes, please leave them empty. + + * RFC PR: + * Approved by: '' + * Reviewed by: '' diff --git a/.github/ISSUE_TEMPLATE/share_your_work.yml b/.github/ISSUE_TEMPLATE/share_your_work.yml new file mode 100644 index 00000000000..e4e4ed601be --- /dev/null +++ b/.github/ISSUE_TEMPLATE/share_your_work.yml @@ -0,0 +1,56 @@ +name: I Made This (showcase your work) +description: Share what you did with Powertools for AWS Lambda (Python) 💞💞. Blog post, workshops, presentation, sample apps, etc. +title: "[I Made This]: " +labels: ["community-content"] +body: + - type: markdown + attributes: + value: Thank you for helping spread the word out on Powertools, truly! + - type: input + id: content + attributes: + label: Link to your material + description: | + Please share the original link to your material. + + *Note: Short links will be expanded when added to Powertools for AWS Lambda (Python) documentation* + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Describe in one paragraph what's in it for them (readers) + validations: + required: true + - type: input + id: author + attributes: + label: Preferred contact + description: What's your preferred contact? We'll list it next to this content + validations: + required: true + - type: input + id: author-social + attributes: + label: (Optional) Social Network + description: If different from preferred contact, what's your preferred contact for social interactions? + validations: + required: false + - type: textarea + id: notes + attributes: + label: (Optional) Additional notes + description: | + Any notes you might want to share with us related to this material. + + *Note: These notes are explicitly to Powertools for AWS Lambda (Python) maintainers. It will not be added to the community resources page.* + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: I understand this content may be removed from Powertools for AWS Lambda (Python) documentation if it doesn't conform with the [Code of Conduct](https://aws.github.io/code-of-conduct) + required: true diff --git a/.github/ISSUE_TEMPLATE/static_typing.yml b/.github/ISSUE_TEMPLATE/static_typing.yml new file mode 100644 index 00000000000..83bfd3dc361 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/static_typing.yml @@ -0,0 +1,70 @@ +name: Static typing mismatch report +description: Report a static type mismatch caught by a static type checker +title: "Static typing: TITLE" +labels: ["typing", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for submitting a static typing report. Please add as much information as possible to help us reproduce. + + Our preferred static type checker is [Mypy](https://mypy.readthedocs.io/en/stable/) using the following [configuration](https://github.com/aws-powertools/powertools-lambda-python/blob/develop/mypy.ini). + - type: dropdown + id: tool + attributes: + label: Static type checker used + options: + - mypy (project's standard) + - pyright/pylance + - pyre + - pytype + validations: + required: true + - type: dropdown + id: runtime + attributes: + label: AWS Lambda function runtime + options: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + validations: + required: true + - type: input + id: version + attributes: + label: Powertools for AWS Lambda (Python) version + placeholder: "latest, 1.25.6" + value: latest + validations: + required: true + - type: textarea + id: output + attributes: + label: Static type checker info + description: Please share your static type checker's output, its configuration, and how you typically run it + validations: + required: true + - type: textarea + id: code_snippet + attributes: + label: Code snippet + description: Please share a code snippet to help us reproduce the issue + render: python + validations: + required: true + - type: textarea + id: solution + attributes: + label: Possible Solution + description: If known, please suggest a potential resolution + validations: + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/ISSUE_TEMPLATE/support_powertools.yml b/.github/ISSUE_TEMPLATE/support_powertools.yml new file mode 100644 index 00000000000..c2c5f33676c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_powertools.yml @@ -0,0 +1,64 @@ +name: Support Powertools for AWS Lambda (Python) (become a reference) +description: Add your organization's name or logo to the Powertools for AWS Lambda (Python) documentation +title: "[Support Powertools for AWS Lambda (Python)]: <your organization name>" +labels: ["customer-reference"] +body: + - type: markdown + attributes: + value: | + Thank you for becoming a reference customer. Your support means a lot to us. It also helps new customers to know who's using it. + + If you would like us to also display your organization's logo, please share a link in the `Company logo` field. + - type: input + id: organization + attributes: + label: Organization Name + description: Please share the name of your organization + placeholder: ACME + validations: + required: true + - type: input + id: name + attributes: + label: Your Name + description: Please share your name + validations: + required: true + - type: input + id: job + attributes: + label: Your current position + description: Please share your current position at your company + validations: + required: true + - type: input + id: logo + attributes: + label: (Optional) Company logo + description: Company logo you want us to display. You also allow us to resize for optimal placement in the documentation. + validations: + required: false + - type: textarea + id: use_case + attributes: + label: (Optional) Use case + description: How are you using Powertools for AWS Lambda (Python) today? *features, etc.* + validations: + required: false + - type: checkboxes + id: other_languages + attributes: + label: Also using other Powertools for AWS Lambda languages? + options: + - label: Java + required: false + - label: TypeScript + required: false + - label: .NET + required: false + - type: markdown + attributes: + value: | + *By raising a Support Powertools for AWS Lambda (Python) issue, you are granting AWS permission to use your company's name (and/or logo) for the limited purpose described here. You are also confirming that you have authority to grant such permission.* + + *You can opt-out at any time by commenting or reopening this issue.* diff --git a/.github/ISSUE_TEMPLATE/tech_debt.yml b/.github/ISSUE_TEMPLATE/tech_debt.yml new file mode 100644 index 00000000000..064c118ba20 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tech_debt.yml @@ -0,0 +1,62 @@ +name: Technical debt +description: Suggest an activity to help address technical debt. +title: "Tech debt: TITLE" +labels: ["tech-debt", "triage"] +body: + - type: markdown + attributes: + value: Thank you for taking the time to help us proactively improve delivery velocity, safely. + - type: textarea + id: importance + attributes: + label: Why is this needed? + description: Please help us understand the value so we can prioritize it accordingly + validations: + required: true + - type: dropdown + id: area + attributes: + label: Which area does this relate to? + multiple: true + options: + - Tests + - Static typing + - Tracer + - Logger + - Metrics + - Event Handler - REST API + - Event Handler - GraphQL API + - Middleware factory + - Parameters + - Batch processing + - Validation + - Event Source Data Classes + - Parser + - Idempotency + - Feature flags + - JMESPath functions + - Streaming + - Automation + - Other + - type: textarea + id: suggestion + attributes: + label: Suggestion + description: If available, please share what a good solution would look like + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This request meets [Powertools for AWS Lambda (Python) Tenets](https://docs.powertools.aws.dev/lambda/python/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Java](https://github.com/aws-powertools/powertools-lambda-java/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5f770c58824..5265d390063 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,25 +1,40 @@ -**Issue #, if available:** +<!-- markdownlint-disable MD041 MD043 --> +**Issue number:** -## Description of changes: +## Summary -<!--- One or two sentences as a summary of what's being changed --> +### Changes -**Checklist** +> Please provide a summary of what's being changed -<!--- Leave unchecked if your change doesn't seem to apply --> +### User experience -* [ ] [Meet tenets criteria](https://awslabs.github.io/aws-lambda-powertools-python/#tenets) -* [ ] Update tests -* [ ] Update docs -* [ ] PR title follows [conventional commit semantics](https://github.com/awslabs/aws-lambda-powertools-python/blob/376ec0a2ac0d2a40e0af5717bef42ff84ca0d1b9/.github/semantic.yml#L2) +> Please share what the user experience looks like before and after this change -## Breaking change checklist +## Checklist -<!--- Ignore if it's not a breaking change --> +If your change doesn't seem to apply, please leave them unchecked. -**RFC issue #**: +* [ ] [Meet tenets criteria](https://docs.powertools.aws.dev/lambda/python/#tenets) +* [ ] I have performed a self-review of this change +* [ ] Changes have been tested +* [ ] Changes are documented +* [ ] PR title follows [conventional commit semantics](https://github.com/aws-powertools/powertools-lambda-python/blob/develop/.github/semantic.yml) + +<details> +<summary>Is this a breaking change?</summary> + +**RFC issue number**: + +Checklist: * [ ] Migration process documented * [ ] Implement warnings (if it can live side by side) +</details> + +## 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. diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 00000000000..c9597418e3f --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,3 @@ +self-hosted-runner: + labels: + - aws-powertools_ubuntu-latest_8-core diff --git a/.github/actions/create-pr/action.yml b/.github/actions/create-pr/action.yml new file mode 100644 index 00000000000..39ba6f60b1f --- /dev/null +++ b/.github/actions/create-pr/action.yml @@ -0,0 +1,88 @@ +name: "Create PR custom action" +description: "Create a PR and a temporary branch, close duplicates" + +# PROCESS +# +# 1. Setup git client using Powertools for AWS Lambda (Python) bot username +# 2. Pushes staged files to a temporary branch +# 3. Creates a PR from temporary branch against a target branch (typically trunk: develop, main, etc.) +# 4. Searches for duplicate PRs with the same title +# 5. If duplicates are found, link to the most recent one, close and delete their branches so we keep a single PR +# 6. In the event of failure, we delete the now orphaned branch (if any), and propagate the failure + +# USAGE +# +# - name: Create PR +# id: create-pr +# uses: ./.github/actions/create-pr +# with: +# files: "CHANGELOG.md" +# temp_branch_prefix: "ci-changelog" +# pull_request_title: "chore(ci): changelog rebuild" +# github_token: ${{ secrets.GITHUB_TOKEN }} +# - name: Step to demonstrate how to access outputs (no need for this) +# run: | +# echo "PR number: ${PR_ID}" +# echo "Branch: ${BRANCH}" +# env: +# PR_ID: ${{ steps.create-pr.outputs.pull_request_id}} +# BRANCH: ${{ steps.create-pr.outputs.temp_branch}} + +inputs: + files: + description: "Files to add separated by space" + required: true + temp_branch_prefix: + description: "Prefix for temporary git branch to be created, e.g, ci-docs" + required: true + pull_request_title: + description: "Pull Request title to use" + required: true + github_token: + description: "GitHub token for GitHub CLI" + required: true + target_branch: + description: "Branch to target when creating a PR against (develop, by default)" + required: false + default: develop + +outputs: + pull_request_id: + description: "Pull request ID created" + value: ${{ steps.create-pr.outputs.pull_request_id }} + temp_branch: + description: "Temporary branch created with staged changed" + value: ${{ steps.create-pr.outputs.temp_branch }} + +runs: + using: "composite" + steps: + - id: adjust-path + run: echo "${{ github.action_path }}" >> $GITHUB_PATH + shell: bash + - id: setup-git + name: Git client setup and refresh tip + run: | + git config user.name "Powertools for AWS Lambda (Python) bot" + git config user.email "151832416+aws-powertools-bot@users.noreply.github.com" + git config pull.rebase true + git config remote.origin.url >&- + shell: bash + - id: create-pr + working-directory: ${{ env.GITHUB_WORKSPACE }} + run: create_pr_for_staged_changes.sh "${FILES}" + env: + FILES: ${{ inputs.files }} + TEMP_BRANCH_PREFIX: ${{ inputs.temp_branch_prefix }} + PR_TITLE: ${{ inputs.pull_request_title }} + BASE_BRANCH: ${{ inputs.target_branch }} + GH_TOKEN: ${{ inputs.github_token }} + shell: bash + - id: cleanup + name: Cleanup orphaned branch + if: failure() + run: git push origin --delete "${TEMP_BRANCH_PREFIX}-${GITHUB_RUN_ID}" || echo "Must have failed before creating temporary branch; no cleanup needed." + env: + TEMP_BRANCH_PREFIX: ${{ inputs.temp_branch_prefix }} + GITHUB_RUN_ID: ${{ github.run_id }} + shell: bash diff --git a/.github/actions/create-pr/create_pr_for_staged_changes.sh b/.github/actions/create-pr/create_pr_for_staged_changes.sh new file mode 100755 index 00000000000..f31217467ee --- /dev/null +++ b/.github/actions/create-pr/create_pr_for_staged_changes.sh @@ -0,0 +1,148 @@ +#!/bin/bash +set -uo pipefail # prevent accessing unset env vars, prevent masking pipeline errors to the next command + +#docs +#title :create_pr_for_staged_changes.sh +#description :This script will create a PR for staged changes, detect and close duplicate PRs. All PRs will be omitted from Release Notes and Changelogs +#author :@heitorlessa +#date :May 8th 2023 +#version :0.1 +#usage :bash create_pr_for_staged_changes.sh {git_staged_files_or_directories_separated_by_space} +#notes :Meant to use in GitHub Actions only. Temporary branch will be named $TEMP_BRANCH_PREFIX-$GITHUB_RUN_ID +#os_version :Ubuntu 22.04.2 LTS +#required_env_vars :PR_TITLE, TEMP_BRANCH_PREFIX, GH_TOKEN +#============================================================================== + +# Sets GitHub Action with error message to ease troubleshooting +function error() { + echo "::error file=${FILENAME}::$1" + exit 1 +} + +function debug() { + TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z + echo ""${TIMESTAMP}" - $1" +} + +function notice() { + echo "::notice file=${FILENAME}::$1" +} + +function start_span() { + echo "::group::$1" +} + +function end_span() { + echo "::endgroup::" +} + +function has_required_config() { + start_span "Validating required config" + test -z "${TEMP_BRANCH_PREFIX}" && error "TEMP_BRANCH_PREFIX env must be set to create a PR" + test -z "${PR_TITLE}" && error "PR_TITLE env must be set" + test -z "${GH_TOKEN}" && error "GH_TOKEN env must be set for GitHub CLI" + + # Default GitHub Actions Env Vars: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables + debug "Are we running in GitHub Action environment?" + test -z "${GITHUB_RUN_ID}" && error "GITHUB_RUN_ID env must be set to trace Workflow Run ID back to PR" + test -z "${GITHUB_SERVER_URL}" && error "GITHUB_SERVER_URL env must be set to trace Workflow Run ID back to PR" + test -z "${GITHUB_REPOSITORY}" && error "GITHUB_REPOSITORY env must be set to trace Workflow Run ID back to PR" + + debug "Config validated successfully!" + set_environment_variables + end_span +} + +function set_environment_variables() { + start_span "Setting environment variables" + export readonly WORKFLOW_URL="${GITHUB_SERVER_URL}"/"${GITHUB_REPOSITORY}"/actions/runs/"${GITHUB_RUN_ID}" # e.g., heitorlessa/aws-lambda-powertools-test/actions/runs/4913570678 + export readonly TEMP_BRANCH="${TEMP_BRANCH_PREFIX}"-"${GITHUB_RUN_ID}" # e.g., ci-changelog-4894658712 + export readonly BASE_BRANCH="${BASE_BRANCH:-develop}" # e.g., main, defaults to develop if missing + export readonly PR_BODY="This is an automated PR created from the following workflow" + export readonly FILENAME=".github/scripts/$(basename "$0")" + export readonly NO_DUPLICATES_MESSAGE="No duplicated PRs found" + export readonly SKIP_LABEL="skip-changelog" + + end_span +} + +function has_anything_changed() { + start_span "Validating git staged files" + HAS_ANY_SOURCE_CODE_CHANGED="$(git status --porcelain)" + + test -z "${HAS_ANY_SOURCE_CODE_CHANGED}" && debug "Nothing to update; exitting early" && exit 0 + end_span +} + +function create_temporary_branch_with_changes() { + start_span "Creating temporary branch: "${TEMP_BRANCH}"" + git checkout -b "${TEMP_BRANCH}" + + debug "Committing staged files: $*" + echo "$@" | xargs -n1 git add || error "Failed to add staged changes: "$@"" + git commit -m "${PR_TITLE}" + + git push origin "${TEMP_BRANCH}" || error "Failed to create new temporary branch" + end_span +} + +function create_pr() { + start_span "Creating PR against ${TEMP_BRANCH} branch" + # TODO: create label + NEW_PR_URL=$(gh pr create --title "${PR_TITLE}" --body "${PR_BODY}: ${WORKFLOW_URL}" --base "${BASE_BRANCH}" --label "${SKIP_LABEL}" || error "Failed to create PR") # e.g, https://github.com/aws-powertools/powertools-lambda-python/pull/13 + + # greedy remove any string until the last URL path, including the last '/'. https://opensource.com/article/17/6/bash-parameter-expansion + debug "Extracing PR Number from PR URL: "${NEW_PR_URL}"" + NEW_PR_ID="${NEW_PR_URL##*/}" # 13 + export NEW_PR_URL + export NEW_PR_ID + end_span +} + +function close_duplicate_prs() { + start_span "Searching for duplicate PRs" + DUPLICATE_PRS=$(gh pr list --search "${PR_TITLE}" --json number --jq ".[] | select(.number != ${NEW_PR_ID}) | .number") # e.g, 13\n14 + + if [ -z "${DUPLICATE_PRS}" ]; then + debug "No duplicate PRs found" + DUPLICATE_PRS="${NO_DUPLICATES_MESSAGE}" + else + debug "Closing duplicated PRs: "${DUPLICATE_PRS}"" + echo "${DUPLICATE_PRS}" | xargs -L1 gh pr close --delete-branch --comment "Superseded by #${NEW_PR_ID}" + fi + + export readonly DUPLICATE_PRS + end_span +} + +function report_job_output() { + start_span "Updating job outputs" + echo pull_request_id="${NEW_PR_ID}" >>"$GITHUB_OUTPUT" + echo temp_branch="${TEMP_BRANCH}" >>"$GITHUB_OUTPUT" + end_span +} + +function report_summary() { + start_span "Creating job summary" + echo "### Pull request created successfully :rocket: ${NEW_PR_URL} <br/><br/> Closed duplicated PRs: ${DUPLICATE_PRS}" >>"$GITHUB_STEP_SUMMARY" + + notice "PR_URL is: ${NEW_PR_URL}" + notice "PR_BRANCH is: ${TEMP_BRANCH}" + notice "PR_DUPLICATES are: ${DUPLICATE_PRS}" + end_span +} + +function main() { + # Sanity check + has_anything_changed + has_required_config + + create_temporary_branch_with_changes "$@" + create_pr + close_duplicate_prs + + report_job_output + report_summary +} + +main "$@" diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml new file mode 100644 index 00000000000..1f1347e4220 --- /dev/null +++ b/.github/actions/download-artifact/action.yml @@ -0,0 +1,58 @@ +name: Download artifact +description: Wrapper around GitHub's official action, with additional extraction before download + +# PROCESS +# +# 1. Downloads artifact using actions/download-artifact action +# 2. Extracts and overwrites tarball previously uploaded +# 3. Remove archive after extraction + +# NOTES +# +# Upload-artifact and download-artifact takes ~2m40s to upload 8MB +# so this is custom action cuts down the entire operation to 1s +# by uploading/extracting a tarball while relying on the official upload-artifact/download-artifact actions +# + +# USAGE +# +# NOTE: Meant to be used with ./.github/actions/upload-artifact +# +# - name: Restore sealed source code +# uses: ./.github/actions/download-artifact +# with: +# name: ${{ needs.seal.outputs.INTEGRITY_HASH }} +# path: . + +# https://github.com/actions/download-artifact/blob/main/action.yml +inputs: + name: + description: Artifact name + required: true + path: + description: Destination path. By default, it will download to the current working directory. + required: false + default: . + +runs: + using: composite + steps: + - name: Download artifacts + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} + + - name: Extract artifacts + run: tar -xvf "${ARCHIVE}" + env: + ARCHIVE: ${{ inputs.name }}.tar + shell: bash + working-directory: ${{ inputs.path }} + + - name: Remove archive + run: rm -f "${ARCHIVE}" + env: + ARCHIVE: ${{ inputs.name }}.tar + shell: bash + working-directory: ${{ inputs.path }} diff --git a/.github/actions/seal-restore/action.yml b/.github/actions/seal-restore/action.yml new file mode 100644 index 00000000000..1107414b640 --- /dev/null +++ b/.github/actions/seal-restore/action.yml @@ -0,0 +1,82 @@ +name: "Restore sealed source code" +description: "Restore sealed source code and confirm integrity hash" + +# PROCESS +# +# 1. Exports artifact name using Prefix + GitHub Run ID (unique for each release trigger) +# 2. Compress entire source code as tarball OR given files +# 3. Create and export integrity hash for tarball +# 4. Upload artifact +# 5. Remove archive + +# USAGE +# +# - name: Seal and upload +# id: seal_source_code +# uses: ./.github/actions/seal +# with: +# artifact_name_prefix: "source" +# +# - name: Restore sealed source code +# uses: ./.github/actions/seal-restore +# with: +# integrity_hash: ${{ needs.seal_source_code.outputs.integrity_hash }} +# artifact_name: ${{ needs.seal_source_code.outputs.artifact_name }} + +# NOTES +# +# To be used together with .github/actions/seal + +inputs: + integrity_hash: + description: "Integrity hash to verify" + required: true + artifact_name: + description: "Sealed artifact name to restore" + required: true + +runs: + using: "composite" + steps: + - id: adjust-path + run: echo "${{ github.action_path }}" >> $GITHUB_PATH + shell: bash + + - name: Download artifacts + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 + with: + name: ${{ inputs.artifact_name }} + path: . + + - id: integrity_hash + name: Create integrity hash for downloaded artifact + run: | + HASH=$(sha256sum "${ARTIFACT_NAME}.tar" | awk '{print $1}') + + echo "current_hash=${HASH}" >> "$GITHUB_OUTPUT" + env: + ARTIFACT_NAME: ${{ inputs.artifact_name }} + shell: bash + + - id: verify_hash + name: Verify sealed artifact integrity hash + run: test "${CURRENT_HASH}" = "${PROVIDED_HASH}" || exit 1 + env: + ARTIFACT_NAME: ${{ inputs.artifact_name }} + PROVIDED_HASH: ${{ inputs.integrity_hash }} + CURRENT_HASH: ${{ steps.integrity_hash.outputs.current_hash }} + shell: bash + + # Restore and overwrite tarball in current directory + - id: overwrite + name: Extract tarball + run: tar -xvf "${ARTIFACT_NAME}".tar + env: + ARTIFACT_NAME: ${{ inputs.artifact_name }} + shell: bash + + - name: Remove archive + run: rm -f "${ARTIFACT_NAME}.tar" + env: + ARTIFACT_NAME: ${{ inputs.artifact_name }} + shell: bash diff --git a/.github/actions/seal/action.yml b/.github/actions/seal/action.yml new file mode 100644 index 00000000000..c3e75a13e92 --- /dev/null +++ b/.github/actions/seal/action.yml @@ -0,0 +1,93 @@ +name: "Seal and hash source code" +description: "Seal and export source code as a tarball artifact along with its integrity hash" + +# PROCESS +# +# 1. Exports artifact name using Prefix + GitHub Run ID (unique for each release trigger) +# 2. Compress entire source code as tarball OR given files +# 3. Create and export integrity hash for tarball +# 4. Upload artifact +# 5. Remove archive + +# USAGE +# +# - name: Seal and upload +# id: seal_source_code +# uses: ./.github/actions/seal +# with: +# artifact_name_prefix: "source" + +inputs: + files: + description: "Files to seal separated by space" + required: false + artifact_name_prefix: + description: "Prefix to use when exporting artifact" + required: true + +outputs: + integrity_hash: + description: "Source code integrity hash" + value: ${{ steps.integrity_hash.outputs.integrity_hash }} + artifact_name: + description: "Artifact name containTemporary branch created with staged changed" + value: ${{ steps.export_artifact_name.outputs.artifact_name }} + +runs: + using: "composite" + steps: + - id: adjust-path + run: echo "${{ github.action_path }}" >> $GITHUB_PATH + shell: bash + + - id: export_artifact_name + name: Export final artifact name + run: echo "artifact_name=${ARTIFACT_PREFIX}-${GITHUB_RUN_ID}" >> "$GITHUB_OUTPUT" + env: + GITHUB_RUN_ID: ${{ github.run_id }} + ARTIFACT_PREFIX: ${{ inputs.artifact_name_prefix }} + shell: bash + + # By default, create a tarball of the current directory minus .git + # otherwise it breaks GH Actions when restoring it + - id: compress_all + if: ${{ !inputs.files }} + name: Create tarball for entire source + run: tar --exclude-vcs -cvf "${ARTIFACT_NAME}".tar * + env: + ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }} + shell: bash + + # If a list of files are given, then create a tarball for those only + - id: compress_selected_files + if: ${{ inputs.files }} + name: Create tarball for selected files + run: tar --exclude-vcs -cvf "${ARTIFACT_NAME}".tar "${FILES}" + env: + FILES: ${{ inputs.files }} + ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }} + shell: bash + + - id: integrity_hash + name: Create and export integrity hash for tarball + run: | + HASH=$(sha256sum "${ARTIFACT_NAME}.tar" | awk '{print $1}') + + echo "integrity_hash=${HASH}" >> "$GITHUB_OUTPUT" + env: + ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }} + shell: bash + + - name: Upload artifacts + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + with: + if-no-files-found: error + name: ${{ steps.export_artifact_name.outputs.artifact_name }} + path: ${{ steps.export_artifact_name.outputs.artifact_name }}.tar + retention-days: 1 + + - name: Remove archive + run: rm -f "${ARTEFACT_NAME}.tar" + env: + ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }} + shell: bash diff --git a/.github/actions/upload-artifact/action.yml b/.github/actions/upload-artifact/action.yml new file mode 100644 index 00000000000..f88ea2475b9 --- /dev/null +++ b/.github/actions/upload-artifact/action.yml @@ -0,0 +1,82 @@ +name: Upload artifact +description: Wrapper around GitHub's official action, with additional archiving before upload + +# PROCESS +# +# 1. Creates tarball excluding .git files +# 2. Uploads tarball using actions/upload-artifact action, fail CI job if no file is found +# 3. Remove archive after uploading it. + +# NOTES +# +# Upload-artifact and download-artifact takes ~2m40s to upload 8MB +# so this is custom action cuts down the entire operation to 1s +# by uploading/extracting a tarball while relying on the official upload-artifact/download-artifact actions +# + +# USAGE +# +# NOTE: Meant to be used with ./.github/actions/download-artifact +# +# - name: Upload sealed source code +# uses: ./.github/actions/upload-artifact +# with: +# name: ${{ steps.integrity.outputs.INTEGRITY_HASH }} +# path: . + +# https://github.com/actions/upload-artifact/blob/main/action.yml +inputs: + name: + description: Artifact name + required: true + path: + description: > + A file, directory or wildcard pattern that describes what to upload. + + You can pass multiple paths separated by space (e.g., dir1 dir2 file.txt). + + Paths and wildcard patterns must be tar command compatible. + required: true + retention-days: + description: > + Artifact retention in days. By default 1 day, max of 90 days, and 0 honours default repo retention. + + You can change max days in the repository settings. + required: false + default: "1" + if-no-files-found: + description: > + Action to perform if no files are found: warn, error, ignore. By default, it fails fast with 'error'. + + Options: + warn: Output a warning but do not fail the action + error: Fail the action with an error message + ignore: Do not output any warnings or errors, the action does not fail + required: false + default: error + +runs: + using: composite + steps: + - name: Archive artifacts + run: | + tar --exclude-vcs \ + -cvf "${ARCHIVE}" "${PATH_TO_ARCHIVE}" + env: + ARCHIVE: ${{ inputs.name }}.tar + PATH_TO_ARCHIVE: ${{ inputs.path }} + shell: bash + + - name: Upload artifacts + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + with: + if-no-files-found: ${{ inputs.if-no-files-found }} + name: ${{ inputs.name }} + path: ${{ inputs.name }}.tar + retention-days: ${{ inputs.retention-days }} + + - name: Remove archive + run: rm -f "${ARCHIVE}" + env: + ARCHIVE: ${{ inputs.name }}.tar + shell: bash diff --git a/.github/actions/upload-release-provenance/action.yml b/.github/actions/upload-release-provenance/action.yml new file mode 100644 index 00000000000..d0829efd4f4 --- /dev/null +++ b/.github/actions/upload-release-provenance/action.yml @@ -0,0 +1,67 @@ +name: "Upload provenance attestation to release" +description: "Download and upload newly generated provenance attestation to latest release." + +# PROCESS +# +# 1. Downloads provenance attestation artifact generated earlier in the release pipeline +# 2. Updates latest GitHub draft release pointing to newly git release tag +# 3. Uploads provenance attestation file to latest GitHub draft release + +# USAGE +# +# - name: Upload provenance +# id: upload-provenance +# uses: ./.github/actions/upload-release-provenance +# with: +# release_version: ${{ needs.seal.outputs.RELEASE_VERSION }} +# provenance_name: ${{needs.provenance.outputs.provenance-name}} +# github_token: ${{ secrets.GITHUB_TOKEN }} + +# NOTES +# +# There are no outputs. +# + +inputs: + provenance_name: + description: "Provenance artifact name to download" + required: true + release_version: + description: "Release version (e.g., 2.20.0)" + required: true + github_token: + description: "GitHub token for GitHub CLI" + required: true + +runs: + using: "composite" + steps: + - id: adjust-path + run: echo "${{ github.action_path }}" >> $GITHUB_PATH + shell: bash + + - id: download-provenance + name: Download newly generated provenance + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 + with: + name: ${{ inputs.provenance_name }} + + - id: sync-release-tag + name: Update draft release tag to release commit tag + run: | + CURRENT_DRAFT_RELEASE=$(gh release list | awk '{ if ($2 == "Draft") print $1}') + gh release edit "${CURRENT_DRAFT_RELEASE}" --tag v"${RELEASE_VERSION}" + env: + RELEASE_VERSION: ${{ inputs.release_version }} + GH_TOKEN: ${{ inputs.github_token }} + shell: bash + + - id: upload-provenance + name: Upload provenance to release tag + # clobber flag means overwrite release asset if available (eventual consistency, retried failed steps) + run: gh release upload --clobber v"${RELEASE_VERSION}" "${PROVENANCE_FILE}" + env: + RELEASE_VERSION: ${{ inputs.release_version }} + PROVENANCE_FILE: ${{ inputs.provenance_name }} + GH_TOKEN: ${{ inputs.github_token }} + shell: bash diff --git a/.github/actions/verify-provenance/verify_provenance.sh b/.github/actions/verify-provenance/verify_provenance.sh new file mode 100755 index 00000000000..dbfe280473d --- /dev/null +++ b/.github/actions/verify-provenance/verify_provenance.sh @@ -0,0 +1,134 @@ +#!/bin/bash +set -uo pipefail # prevent accessing unset env vars, prevent masking pipeline errors to the next command + +#docs +#title :verify_provenance.sh +#description :This script will download and verify a signed Powertools for AWS Lambda (Python) release build with SLSA Verifier +#author :@heitorlessa +#date :July 1st 2023 +#version :0.1 +#usage :bash verify_provenance.sh {release version} +#notes :Meant to use in GitHub Actions or locally (MacOS, Linux, WSL). +#os_version :Ubuntu 22.04.2 LTS +#============================================================================== + +# Check if RELEASE_VERSION is provided as a command line argument +if [[ $# -eq 1 ]]; then + export readonly RELEASE_VERSION="$1" +else + echo "ERROR: Please provider Powertools release version as a command line argument." + echo "Example: bash verify_provenance.sh 2.20.0" + exit 1 +fi + +export readonly ARCHITECTURE=$(uname -m | sed 's/x86_64/amd64/g') # arm64, x86_64 ->amd64 +export readonly OS_NAME=$(uname -s | tr '[:upper:]' '[:lower:]') # darwin, linux +export readonly SLSA_VERIFIER_VERSION="2.3.0" +export readonly SLSA_VERIFIER_CHECKSUM_FILE="SHA256SUM.md" +export readonly SLSA_VERIFIER_BINARY="./slsa-verifier-${OS_NAME}-${ARCHITECTURE}" + +export readonly RELEASE_BINARY="aws_lambda_powertools-${RELEASE_VERSION}-py3-none-any.whl" +export readonly ORG="aws-powertools" +export readonly REPO="powertools-lambda-python" +export readonly PROVENANCE_FILE="multiple.intoto.jsonl" + +export readonly FILES=("${SLSA_VERIFIER_BINARY}" "${SLSA_VERIFIER_CHECKSUM_FILE}" "${PROVENANCE_FILE}" "${RELEASE_BINARY}") + +function debug() { + TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z + echo ""${TIMESTAMP}" DEBUG - [*] $1" +} + +function error() { + cleanup + TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z + echo ""${TIMESTAMP}" ERROR - [!] $1" + echo ""${TIMESTAMP}" ERROR - [!] exiting" + exit 1 +} + +function download_slsa_verifier() { + readonly SLSA_URL="https://github.com/slsa-framework/slsa-verifier/releases/download/v${SLSA_VERIFIER_VERSION}/slsa-verifier-${OS_NAME}-${ARCHITECTURE}" + # debug "Downloading SLSA Verifier for - Binary: slsa-verifier-${OS_NAME}-${ARCHITECTURE}" + debug "Downloading SLSA Verifier binary: ${SLSA_URL}" + curl \ + --location \ + --fail \ + --silent \ + -O "${SLSA_URL}" || error "Failed to download SLSA Verifier binary" + + readonly SLSA_CHECKSUM_URL="https://raw.githubusercontent.com/slsa-framework/slsa-verifier/f59b55ef2190581d40fc1a5f3b7a51cab2f4a652/${SLSA_VERIFIER_CHECKSUM_FILE}" + debug "Downloading SLSA Verifier checksums" + curl \ + --location \ + --fail \ + --silent \ + -O "${SLSA_CHECKSUM_URL}" || error "Failed to download SLSA Verifier binary checksum file" + + debug "Verifying SLSA Verifier binary integrity" + CURRENT_HASH=$(sha256sum "${SLSA_VERIFIER_BINARY}" | awk '{print $1}') + if [[ $(grep "${CURRENT_HASH}" "${SLSA_VERIFIER_CHECKSUM_FILE}") ]]; then + debug "SLSA Verifier binary integrity confirmed" + chmod +x "${SLSA_VERIFIER_BINARY}" + else + error "Failed integrity check for SLSA Verifier binary: ${SLSA_VERIFIER_BINARY}" + fi +} + +function download_provenance() { + readonly PROVENANCE_URL="https://github.com/${ORG}/${REPO}/releases/download/v${RELEASE_VERSION}/${PROVENANCE_FILE}" + debug "Downloading attestation: ${PROVENANCE_URL}" + + curl \ + --location \ + --fail \ + --silent \ + -O ${PROVENANCE_URL} || error "Failed to download provenance. Does the release already exist?" +} + +function download_release_artifact() { + debug "Downloading ${RELEASE_VERSION} release from PyPi" + python -m pip download \ + --only-binary=:all: \ + --no-deps \ + --quiet \ + aws-lambda-powertools=="${RELEASE_VERSION}" +} + +function verify_provenance() { + debug "Verifying attestation with slsa-verifier" + "${SLSA_VERIFIER_BINARY}" verify-artifact \ + --provenance-path "${PROVENANCE_FILE}" \ + --source-uri github.com/${ORG}/${REPO} \ + ${RELEASE_BINARY} +} + +function cleanup() { + debug "Cleaning up previously downloaded files" + rm -f "${SLSA_VERIFIER_BINARY}" + rm -f "${SLSA_VERIFIER_CHECKSUM_FILE}" + rm -f "${PROVENANCE_FILE}" + rm -f "${RELEASE_BINARY}" + echo "${FILES[@]}" | xargs -n1 echo "Removed file: " +} + +function main() { + download_slsa_verifier + download_provenance + download_release_artifact + verify_provenance + cleanup +} + +main + +# Lessons learned +# +# 1. If source doesn't match provenance +# +# FAILED: SLSA verification failed: source used to generate the binary does not match provenance: expected source 'awslabs/aws-lambda-powertools-python', got 'heitorlessa/aws-lambda-powertools-test' +# +# 2. Avoid building deps during download in Test registry endpoints +# +# FAILED: Could not find a version that satisfies the requirement poetry-core>=1.3.2 (from versions: 1.2.0) +# diff --git a/.github/auto_assign-issues.yml b/.github/auto_assign-issues.yml deleted file mode 100644 index 1b7aa13da77..00000000000 --- a/.github/auto_assign-issues.yml +++ /dev/null @@ -1,8 +0,0 @@ -addAssignees: true - -# The list of users to assign to new issues. -# If empty or not provided, the repository owner is assigned -assignees: - - heitorlessa - - nmoutschen - - cakepietoast diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml index 31aa24bc94f..006db19c585 100644 --- a/.github/boring-cyborg.yml +++ b/.github/boring-cyborg.yml @@ -1,66 +1,85 @@ ##### Labeler ########################################################################################################## labelPRBasedOnFilePath: - area/logger: + logger: - aws_lambda_powertools/logging/* - aws_lambda_powertools/logging/**/* - aws_lambda_powertools/package_logger.py - area/tracer: + tracer: - aws_lambda_powertools/tracing/* - aws_lambda_powertools/tracing/**/* - area/metrics: + metrics: - aws_lambda_powertools/metrics/* - aws_lambda_powertools/metrics/**/* - area/event_handlers: + event_handlers: - aws_lambda_powertools/event_handler/* - aws_lambda_powertools/event_handler/**/* - area/middleware_factory: + middleware_factory: - aws_lambda_powertools/middleware_factory/* - aws_lambda_powertools/middleware_factory/**/* - area/parameters: + parameters: - aws_lambda_powertools/parameters/* - aws_lambda_powertools/parameters/**/* - area/batch: + batch: - aws_lambda_powertools/batch/* - aws_lambda_powertools/batch/**/* - area/validator: + validator: - aws_lambda_powertools/validation/* - aws_lambda_powertools/validation/**/* - area/event_sources: + event_sources: - aws_lambda_powertools/data_classes/* - aws_lambda_powertools/data_classes/**/* - area/parser: + parser: - aws_lambda_powertools/parser/* - aws_lambda_powertools/parser/**/* - area/idempotency: + idempotency: - aws_lambda_powertools/idempotency/* - aws_lambda_powertools/idempotency/**/* - area/feature_flags: + feature_flags: - aws_lambda_powertools/feature_flags/* - aws_lambda_powertools/feature_flags/**/* - area/jmespath_util: + jmespath: - aws_lambda_powertools/utilities/jmespath_utils/* - area/utilities: - - aws_lambda_powertools/utilities/* - - aws_lambda_powertools/utilities/**/* - - aws_lambda_powertools/middleware_factory/* - - aws_lambda_powertools/middleware_factory/**/* + typing: + - aws_lambda_powertools/utilities/typing/* + - mypy.ini + streaming: + - aws_lambda_powertools/utilities/streaming/* + commons: + - aws_lambda_powertools/shared/* documentation: - docs/* - docs/**/* - mkdocs.yml + github-actions: + - .github/workflows/* + - .github/workflows/**/* + - .github/dependabot.yml + - .github/boring-cyborg.yml + - .github/release-drafter.yml + - .github/semantic.yml + - .github/stale.yml + - .github/mergify.yml + + github-templates: + - .github/ISSUE_TEMPLATE/* + - .github/PULL_REQUEST_TEMPLATE.md + - .github/.chglog/* + - .github/.chglog/**/* + internal: - - .github/* - - .github/**/* - - .chglog/* - .flake8 + - .bandit.baseline - .gitignore - .pre-commit-config.yaml + - MANIFEST.in - Makefile - CONTRIBUTING.md + - MAINTAINERS.md - CODE_OF_CONDUCT.md - LICENSE + - THIRD-PARTY-LICENSES - aws_lambda_powertools_python/shared/* - aws_lambda_powertools_python/shared/** @@ -78,6 +97,8 @@ labelPRBasedOnFilePath: firstPRWelcomeComment: > 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 Powertools for AWS Lambda Discord: [Invite link](https://discord.gg/B8zZKbbyET) + # Comment to be posted to congratulate user on their first merged PR firstPRMergeComment: > Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience! @@ -86,6 +107,7 @@ firstPRMergeComment: > firstIssueWelcomeComment: > Thanks for opening your first issue here! We'll come back to you as soon as we can. + In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: [Invite link](https://discord.gg/B8zZKbbyET) ###### IssueLink Adder ################################################################################################# # Insert Issue (Jira/Github etc) link in PR description based on the Issue ID in PR title. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e95edbee4e6..fed13e3e577 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,5 @@ version: 2 updates: - - package-ecosystem: "github-actions" directory: "/" schedule: @@ -17,17 +16,74 @@ updates: commit-message: prefix: chore include: scope + ignore: + # 2022-04-23: Ignoring boto3 changes until we need to care about them. + - dependency-name: "boto3" + groups: + boto-typing: + patterns: + - "mypy-boto3-*" + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + target-branch: "develop" + commit-message: + prefix: chore + include: scope + allow: + # Allow updates for AWS CDK + - dependency-name: "aws-cdk" + + - package-ecosystem: pip + directory: /benchmark/src/instrumented + commit-message: + prefix: chore + include: scope + schedule: + interval: daily + + - package-ecosystem: pip + directory: /benchmark/src/reference + commit-message: + prefix: chore + include: scope + schedule: + interval: daily + + - package-ecosystem: docker + directory: /docs + commit-message: + prefix: chore + include: scope + schedule: + interval: daily + + - package-ecosystem: pip + directory: /docs + commit-message: + prefix: chore + include: scope + schedule: + interval: daily -# - package-ecosystem: "pip" -# directory: "/" -# schedule: -# interval: "daily" -# target-branch: "develop" -# update_types: -# - "semver:major" -# labels: -# - "do-not-merge" -# - "dependencies" -# commit-message: -# prefix: chore -# include: scope + - package-ecosystem: pip + directory: /examples/event_handler_graphql/src + commit-message: + prefix: chore + include: scope + schedule: + interval: daily + + - package-ecosystem: gomod + directory: /layer/scripts/layer-balancer + commit-message: + prefix: chore + include: scope + schedule: + interval: daily + groups: + layer-balancer: + patterns: + - "*" diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml new file mode 100644 index 00000000000..208fd73c9e1 --- /dev/null +++ b/.github/dependency-review-config.yml @@ -0,0 +1,35 @@ +allow-licenses: + - 'Apache-1.1' + - 'Apache-2.0' + - 'MIT' + - 'MIT-0' + - 'MIT-CMU' + - 'MIT-enna' + - 'MIT-feh' + - 'MIT-Festival' + - 'MIT-Modern-Variant' + - 'MIT-open-group' + - 'MIT-testregex' + - 'MIT-Wu' + - 'BSD-1-Clause' + - 'BSD-2-Clause' + - 'BSD-2-Clause-Views' + - 'BSD-3-Clause' + - 'BSD-3-Clause-Attribution' + - 'BSD-3-Clause-Clear' + - 'BSD-3-Clause-flex' + - 'BSD-3-Clause-HP' + - 'BSD-3-Clause-LBNL' + - 'BSD-3-Clause-Modification' + - 'BSD-3-Clause-No-Military-License' + - 'BSD-3-Clause-No-Nuclear-License' + - 'BSD-3-Clause-No-Nuclear-License-2014' + - 'BSD-3-Clause-No-Nuclear-Warranty' + - 'BSD-3-Clause-Open-MPI' + - 'Python-2.0' + - 'Python-2.0.1' + - 'ISC' + - 'MPL-1.1' + - 'MPL-2.0' +comment-summary-in-pr: on-failure +fail-on-scopes: runtime diff --git a/.github/mergify.yml b/.github/mergify.yml index 8e010eeca29..dc3f1953586 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -1,29 +1,30 @@ -queue_rules: - - name: default - conditions: - # Conditions to get out of the queue (= merged) - - check-success=Semantic Pull Request - - "#approved-reviews-by>=1" - - -title~=(WIP|wip) - - -label~="do-not-merge" - - "#changes-requested-reviews-by=0" +## Temporarily disabled after Mergify breaking changes +## might move to custom GitHub Actions altogether +# queue_rules: +# - name: default +# conditions: +# # Conditions to get out of the queue (= merged) +# - check-success=Semantic Pull Request +# - "#approved-reviews-by>=1" +# - -title~=(WIP|wip) +# - -label~="do-not-merge" +# - "#changes-requested-reviews-by=0" -pull_request_rules: - - name: automatic merge for Dependabot pull requests - conditions: - - author~=^dependabot(|-preview)\[bot\]$ - actions: - queue: - name: default - method: squash - commit_message: title+body +# pull_request_rules: +# - name: automatic merge for Dependabot pull requests +# conditions: +# - author~=^dependabot(|-preview)\[bot\]$ +# actions: +# queue: +# name: default +# method: squash +# commit_message: title+body - - name: Automatic merge ⬇️ on approval ✔ - conditions: - - base!=master - - "#approved-reviews-by>=2" - actions: - queue: - name: default - method: squash - commit_message: title+body +# - name: Automatic merge ⬇️ on approval ✔ +# conditions: +# - "#approved-reviews-by>=2" +# actions: +# queue: +# name: default +# method: squash +# commit_message: title+body diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 44ad5a61779..b590898b517 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,32 +1,28 @@ change-template: "* $TITLE (#$NUMBER) by @$AUTHOR" categories: - - title: '⚡ Breaking Changes' + - title: "⚡ Breaking Changes" labels: - - 'breaking-change' - - title: '🌟New features and non-breaking changes' + - "breaking-change" + - title: "🌟New features and non-breaking changes" labels: - - 'major' - - 'feature' - - title: '🌟 Minor Changes' + - "feature" + - title: "📜 Documentation updates" labels: - - 'enhancement' - - title: '📜 Documentation updates' + - "documentation" + - title: "🐛 Bug and hot fixes" labels: - - 'documentation' - - title: '🐛 Bug and hot fixes' + - "bug" + - "fix" + - title: "🚒 Deprecations" labels: - - 'bug' - - 'fix' - - title: '🚒 Deprecations' + - "deprecated" + - title: "🔧 Maintenance" labels: - - 'deprecated' - - title: '🔧 Maintenance' - labels: - - 'internal' - - 'dependencies' + - "internal" + - "dependencies" exclude-labels: - - 'skip-changelog' -tag-template: 'v$NEXT_PATCH_VERSION' + - "skip-changelog" +tag-template: "v$NEXT_PATCH_VERSION" template: | ## Summary diff --git a/.github/scripts/comment_on_large_pr.js b/.github/scripts/comment_on_large_pr.js new file mode 100644 index 00000000000..c17199faf76 --- /dev/null +++ b/.github/scripts/comment_on_large_pr.js @@ -0,0 +1,73 @@ +const { + PR_NUMBER, + PR_ACTION, + PR_AUTHOR, + IGNORE_AUTHORS, +} = require("./constants") + + +/** + * Notify PR author to split XXL PR in smaller chunks + * + * @param {object} core - core functions instance from @actions/core + * @param {object} gh_client - Pre-authenticated REST client (Octokit) + * @param {string} owner - GitHub Organization + * @param {string} repository - GitHub repository + */ +const notifyAuthor = async ({ + core, + gh_client, + owner, + repository, +}) => { + core.info(`Commenting on PR ${PR_NUMBER}`) + + let msg = `### ⚠️Large PR detected⚠️ + +Please consider breaking into smaller PRs to avoid significant review delays. Ignore if this PR has naturally grown to this size after reviews. + `; + + try { + await gh_client.rest.issues.createComment({ + owner: owner, + repo: repository, + body: msg, + issue_number: PR_NUMBER, + }); + } catch (error) { + core.setFailed("Failed to notify PR author to split large PR"); + console.error(err); + } +} + +module.exports = async ({github, context, core}) => { + if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { + return core.notice("Author in IGNORE_AUTHORS list; skipping...") + } + + if (PR_ACTION != "labeled") { + return core.notice("Only run on PRs labeling actions; skipping") + } + + + /** @type {string[]} */ + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: PR_NUMBER, + }) + + // Schema: https://docs.github.com/en/rest/issues/labels#list-labels-for-an-issue + for (const label of labels) { + core.info(`Label: ${label}`) + if (label.name == "size/XXL") { + await notifyAuthor({ + core: core, + gh_client: github, + owner: context.repo.owner, + repository: context.repo.repo, + }) + break; + } + } +} diff --git a/.github/scripts/constants.js b/.github/scripts/constants.js new file mode 100644 index 00000000000..8bfe5571974 --- /dev/null +++ b/.github/scripts/constants.js @@ -0,0 +1,42 @@ +module.exports = Object.freeze({ + /** @type {string} */ + // Values: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request + "PR_ACTION": process.env.PR_ACTION?.replace(/"/g, '') || "", + + /** @type {string} */ + "PR_AUTHOR": process.env.PR_AUTHOR?.replace(/"/g, '') || "", + + /** @type {string} */ + "PR_BODY": process.env.PR_BODY || "", + + /** @type {string} */ + "PR_TITLE": process.env.PR_TITLE || "", + + /** @type {number} */ + "PR_NUMBER": process.env.PR_NUMBER || 0, + + /** @type {string} */ + "PR_IS_MERGED": process.env.PR_IS_MERGED || "false", + + /** @type {string} */ + "PR_LABELS": process.env.PR_LABELS || "", + + /** @type {string} */ + "LABEL_BLOCK": "do-not-merge", + + /** @type {string} */ + "LABEL_BLOCK_REASON": "need-issue", + + /** @type {string} */ + "LABEL_BLOCK_MISSING_LICENSE_AGREEMENT": "need-license-agreement-acknowledge", + + /** @type {string} */ + "LABEL_PENDING_RELEASE": "pending-release", + + /** @type {string} */ + "HANDLE_MAINTAINERS_TEAM": "@aws-powertools/powertools-lambda-python", + + /** @type {string[]} */ + "IGNORE_AUTHORS": ["dependabot[bot]", "markdownify[bot]"], + +}); diff --git a/.github/scripts/download_pr_artifact.js b/.github/scripts/download_pr_artifact.js new file mode 100644 index 00000000000..274467c1f1c --- /dev/null +++ b/.github/scripts/download_pr_artifact.js @@ -0,0 +1,26 @@ +module.exports = async ({github, context, core}) => { + const fs = require('fs'); + + const workflowRunId = process.env.WORKFLOW_ID; + core.info(`Listing artifacts for workflow run ${workflowRunId}`); + + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: workflowRunId, + }); + + const matchArtifact = artifacts.data.artifacts.filter(artifact => artifact.name == "pr")[0]; + + core.info(`Downloading artifacts for workflow run ${workflowRunId}`); + const artifact = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + + core.info("Saving artifact found", artifact); + + fs.writeFileSync('pr.zip', Buffer.from(artifact.data)); +} diff --git a/.github/scripts/enforce_acknowledgment.js b/.github/scripts/enforce_acknowledgment.js new file mode 100644 index 00000000000..3e3be636ede --- /dev/null +++ b/.github/scripts/enforce_acknowledgment.js @@ -0,0 +1,40 @@ +const { +PR_ACTION, +PR_AUTHOR, +PR_BODY, +PR_NUMBER, +IGNORE_AUTHORS, +LABEL_BLOCK, +LABEL_BLOCK_REASON +} = require("./constants") + +module.exports = async ({github, context, core}) => { + if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { + return core.notice("Author in IGNORE_AUTHORS list; skipping...") + } + + if (PR_ACTION != "opened") { + return core.notice("Only newly open PRs are labelled to avoid spam; skipping") + } + + const RELATED_ISSUE_REGEX = /Issue number:[^\d\r\n]+(?<issue>\d+)/; + const isMatch = RELATED_ISSUE_REGEX.exec(PR_BODY); + if (isMatch == null) { + core.info(`No related issue found, maybe the author didn't use the template but there is one.`) + + let msg = "No related issues found. Please ensure there is an open issue related to this change to avoid significant delays or closure."; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + body: msg, + issue_number: PR_NUMBER, + }); + + return await github.rest.issues.addLabels({ + issue_number: PR_NUMBER, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [LABEL_BLOCK, LABEL_BLOCK_REASON] + }) + } +} diff --git a/.github/scripts/label_missing_acknowledgement_section.js b/.github/scripts/label_missing_acknowledgement_section.js new file mode 100644 index 00000000000..12b85241d1d --- /dev/null +++ b/.github/scripts/label_missing_acknowledgement_section.js @@ -0,0 +1,41 @@ +const { + PR_ACTION, + PR_AUTHOR, + PR_BODY, + PR_NUMBER, + IGNORE_AUTHORS, + LABEL_BLOCK, + LABEL_BLOCK_MISSING_LICENSE_AGREEMENT +} = require("./constants") + +module.exports = async ({github, context, core}) => { + if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { + return core.notice("Author in IGNORE_AUTHORS list; skipping...") + } + + if (PR_ACTION != "opened") { + return core.notice("Only newly open PRs are labelled to avoid spam; skipping") + } + + const RELATED_ACK_SECTION_REGEX = /By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice./; + + const isMatch = RELATED_ACK_SECTION_REGEX.exec(PR_BODY); + if (isMatch == null) { + core.info(`No acknowledgement section found, maybe the author didn't use the template but there is one.`) + + let msg = "No acknowledgement section found. Please make sure you used the template to open a PR and didn't remove the acknowledgment section. Check the template here: https://github.com/aws-powertools/powertools-lambda-python/blob/develop/.github/PULL_REQUEST_TEMPLATE.md#acknowledgment"; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + body: msg, + issue_number: PR_NUMBER, + }); + + return await github.rest.issues.addLabels({ + issue_number: PR_NUMBER, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [LABEL_BLOCK, LABEL_BLOCK_MISSING_LICENSE_AGREEMENT] + }) + } +} diff --git a/.github/scripts/label_missing_related_issue.js b/.github/scripts/label_missing_related_issue.js new file mode 100644 index 00000000000..705e414c47f --- /dev/null +++ b/.github/scripts/label_missing_related_issue.js @@ -0,0 +1,40 @@ +const { + PR_ACTION, + PR_AUTHOR, + PR_BODY, + PR_NUMBER, + IGNORE_AUTHORS, + LABEL_BLOCK, + LABEL_BLOCK_REASON +} = require("./constants") + +module.exports = async ({github, context, core}) => { + if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { + return core.notice("Author in IGNORE_AUTHORS list; skipping...") + } + + if (PR_ACTION != "opened") { + return core.notice("Only newly open PRs are labelled to avoid spam; skipping") + } + + const RELATED_ISSUE_REGEX = /Issue number:[^\d\r\n]+(?<issue>\d+)/; + const isMatch = RELATED_ISSUE_REGEX.exec(PR_BODY); + if (isMatch == null) { + core.info(`No related issue found, maybe the author didn't use the template but there is one.`) + + let msg = "No related issues found. Please ensure there is an open issue related to this change to avoid significant delays or closure."; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + body: msg, + issue_number: PR_NUMBER, + }); + + return await github.rest.issues.addLabels({ + issue_number: PR_NUMBER, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [LABEL_BLOCK, LABEL_BLOCK_REASON] + }) + } +} diff --git a/.github/scripts/label_pr_based_on_title.js b/.github/scripts/label_pr_based_on_title.js new file mode 100644 index 00000000000..02f77f448b8 --- /dev/null +++ b/.github/scripts/label_pr_based_on_title.js @@ -0,0 +1,62 @@ +const { PR_NUMBER, PR_TITLE, PR_LABELS } = require("./constants") + +module.exports = async ({github, context, core}) => { + const FEAT_REGEX = /feat(\((.+)\))?(:.+)/ + const BUG_REGEX = /(fix|bug)(\((.+)\))?(:.+)/ + const DOCS_REGEX = /(docs|doc)(\((.+)\))?(:.+)/ + const CHORE_REGEX = /(chore)(\((.+)\))?(:.+)/ + const DEPRECATED_REGEX = /(deprecated)(\((.+)\))?(:.+)/ + const REFACTOR_REGEX = /(refactor)(\((.+)\))?(:.+)/ + + const labels = { + "feature": FEAT_REGEX, + "bug": BUG_REGEX, + "documentation": DOCS_REGEX, + "internal": CHORE_REGEX, + "enhancement": REFACTOR_REGEX, + "deprecated": DEPRECATED_REGEX, + } + + // get PR labels from env + const prLabels = PR_LABELS.replaceAll("\"", "").split(","); + const labelKeys = Object.keys(labels); + + let miss = 0; + try { + for (const label in labels) { + const matcher = new RegExp(labels[label]) + const matches = matcher.exec(PR_TITLE) + if (matches != null) { + core.info(`Auto-labeling PR ${PR_NUMBER} with ${label}`) + + for (const prLabel of prLabels) { + if (labelKeys.includes(prLabel) && prLabel !== label) { + core.info(`PR previously tagged with: ${prLabel}, removing.`); + await github.rest.issues.removeLabel({ + issue_number: PR_NUMBER, + owner: context.repo.owner, + repo: context.repo.repo, + name: prLabel + }) + } + } + + await github.rest.issues.addLabels({ + issue_number: PR_NUMBER, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [label] + }) + + return; + } else { + core.debug(`'${PR_TITLE}' didn't match '${label}' semantic.`) + miss += 1 + } + } + } finally { + if (miss == Object.keys(labels).length) { + core.notice(`PR ${PR_NUMBER} title '${PR_TITLE}' doesn't follow semantic titles; skipping...`) + } + } +} diff --git a/.github/scripts/label_related_issue.js b/.github/scripts/label_related_issue.js new file mode 100644 index 00000000000..790aac1ced5 --- /dev/null +++ b/.github/scripts/label_related_issue.js @@ -0,0 +1,53 @@ +const { + PR_AUTHOR, + PR_BODY, + PR_NUMBER, + IGNORE_AUTHORS, + LABEL_PENDING_RELEASE, + HANDLE_MAINTAINERS_TEAM, + PR_IS_MERGED, +} = require("./constants") + +module.exports = async ({github, context, core}) => { + if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { + return core.notice("Author in IGNORE_AUTHORS list; skipping...") + } + + if (PR_IS_MERGED == "false") { + return core.notice("Only merged PRs to avoid spam; skipping") + } + + const RELATED_ISSUE_REGEX = /Issue number:[^\d\r\n]+(?<issue>\d+)/; + + const isMatch = RELATED_ISSUE_REGEX.exec(PR_BODY); + + try { + if (!isMatch) { + core.setFailed(`Unable to find related issue for PR number ${PR_NUMBER}.\n\n Body details: ${PR_BODY}`); + return await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + body: `${HANDLE_MAINTAINERS_TEAM} No related issues found. Please ensure '${LABEL_PENDING_RELEASE}' label is applied before releasing.`, + issue_number: PR_NUMBER, + }); + } + } catch (error) { + core.setFailed(`Unable to create comment on PR number ${PR_NUMBER}.\n\n Error details: ${error}`); + throw new Error(error); + } + + const { groups: {issue} } = isMatch + + try { + core.info(`Auto-labeling related issue ${issue} for release`) + return await github.rest.issues.addLabels({ + issue_number: issue, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [LABEL_PENDING_RELEASE] + }) + } catch (error) { + core.setFailed(`Is this issue number (${issue}) valid? Perhaps a discussion?`); + throw new Error(error); + } +} diff --git a/.github/workflows/post_release.js b/.github/scripts/post_release.js similarity index 92% rename from .github/workflows/post_release.js rename to .github/scripts/post_release.js index a174263db28..d6a598f1960 100644 --- a/.github/workflows/post_release.js +++ b/.github/scripts/post_release.js @@ -1,4 +1,4 @@ -const STAGED_LABEL = "status/staged-next-release"; +const STAGED_LABEL = "pending-release"; /** * Fetch issues using GitHub REST API @@ -15,7 +15,7 @@ const fetchIssues = async ({ gh_client, org, repository, - state = "open", + state = "all", label = STAGED_LABEL, }) => { @@ -100,13 +100,13 @@ const notifyRelease = async ({ // context: https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts module.exports = async ({ github, context }) => { - const { RELEASE_TAG_VERSION } = process.env; - console.log(`Running post-release script for ${RELEASE_TAG_VERSION} version`); + const { RELEASE_VERSION } = process.env; + console.log(`Running post-release script for ${RELEASE_VERSION} version`); await notifyRelease({ gh_client: github, owner: context.repo.owner, repository: context.repo.repo, - release_version: RELEASE_TAG_VERSION, + release_version: RELEASE_VERSION, }); }; diff --git a/.github/scripts/save_pr_details.js b/.github/scripts/save_pr_details.js new file mode 100644 index 00000000000..ba2de975b3c --- /dev/null +++ b/.github/scripts/save_pr_details.js @@ -0,0 +1,23 @@ +module.exports = async ({github, context, core}) => { + const fs = require('fs'); + const filename = "pr.txt"; + + const labelsData = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: (context.payload.issue || context.payload.pull_request || context.payload).number, + }); + + const labels = labelsData.data.map((label) => { + return label['name']; + }); + + try { + fs.writeFileSync(`./${filename}`, JSON.stringify({...context.payload, ...{labels:labels.join(",")}})); + + return `PR successfully saved ${filename}` + } catch (err) { + core.setFailed("Failed to save PR details"); + console.error(err); + } +} diff --git a/.github/workflows/bootstrap_region.yml b/.github/workflows/bootstrap_region.yml new file mode 100644 index 00000000000..d93f266a411 --- /dev/null +++ b/.github/workflows/bootstrap_region.yml @@ -0,0 +1,115 @@ +name: Region Bootstrap + +# bootstraps new regions +# +# PURPOSE +# Ensures new regions are deployable in future releases +# +# JOB 1 PROCESS +# +# 1. Installs CDK +# 2. Bootstraps region +# +# JOB 2 PROCESS +# 1. Sets up Go +# 2. Installs the balance script +# 3. Runs balance script to copy layers between aws regions + +on: + workflow_dispatch: + inputs: + environment: + type: choice + options: + - beta + - prod + description: Deployment environment + region: + type: string + required: true + description: AWS region to bootstrap (i.e. eu-west-1) + +run-name: Region Bootstrap ${{ inputs.region }} + +permissions: + contents: read + +jobs: + cdk: + name: Install CDK + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: layer-${{ inputs.environment }} + steps: + - name: checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.sha }} + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "22" + - name: Setup dependencies + uses: aws-powertools/actions/.github/actions/cached-node-modules@29979bc5339bf54f76a11ac36ff67701986bb0f0 + - id: credentials + name: AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df + with: + aws-region: ${{ inputs.region }} + role-to-assume: ${{ secrets.REGION_IAM_ROLE }} + mask-aws-account-id: true + - id: workdir + name: Create Workdir + run: | + mkdir -p build/project + - id: cdk-project + name: CDK Project + working-directory: build/project + run: | + npx cdk init app --language=typescript + AWS_REGION="${{ inputs.region }}" npx cdk bootstrap + + copy_layers: + name: Copy Layers + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39-arm64 + - AWSLambdaPowertoolsPythonV3-python310-arm64 + - AWSLambdaPowertoolsPythonV3-python311-arm64 + - AWSLambdaPowertoolsPythonV3-python312-arm64 + - AWSLambdaPowertoolsPythonV3-python313-arm64 + - AWSLambdaPowertoolsPythonV3-python39-x86_64 + - AWSLambdaPowertoolsPythonV3-python310-x86_64 + - AWSLambdaPowertoolsPythonV3-python311-x86_64 + - AWSLambdaPowertoolsPythonV3-python312-x86_64 + - AWSLambdaPowertoolsPythonV3-python313-x86_64 + environment: layer-${{ inputs.environment }} + steps: + - id: credentials + name: AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.REGION_IAM_ROLE }} + mask-aws-account-id: true + - id: go-setup + name: Setup Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: '>=1.23.0' + - id: go-env + name: Go Env + run: go env + - id: go-install-pkg + name: Install + run: go install github.com/aws-powertools/actions/layer-balancer/cmd/balance@29979bc5339bf54f76a11ac36ff67701986bb0f0 + - id: run-balance + name: Run Balance + run: balance -read-region us-east-1 -write-region ${{ inputs.region }} -write-role ${{ secrets.BALANCE_ROLE_ARN }} -layer-name ${{ matrix.layer }} -dry-run=false diff --git a/.github/workflows/build_changelog.yml b/.github/workflows/build_changelog.yml new file mode 100644 index 00000000000..ffa6163ca03 --- /dev/null +++ b/.github/workflows/build_changelog.yml @@ -0,0 +1,31 @@ +# Standalone workflow to update changelog if necessary +name: Build changelog + +# PROCESS +# +# 1. Fetch latest changes compared to the latest tag +# 2. Rebuild CHANGELOG.md using Keep A Changelog format +# 3. Create a PR with the latest changelog (close and reference any it supersedes) + +# USAGE +# +# Always triggered on PR merge or manually from GitHub UI if we must. + +on: + workflow_dispatch: +# push: +# branches: +# - develop + schedule: + # Note: run daily at 10am UTC time until upstream git-chlog uses stable sorting + - cron: "0 10 * * *" + +permissions: + contents: read + +jobs: + changelog: + permissions: + contents: write # create temporary branch to store changelog changes + pull-requests: write # create PR with changelog changes + uses: ./.github/workflows/reusable_publish_changelog.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 52d7ba29fa9..196bc498976 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,36 +1,38 @@ name: "CodeQL" +# PROCESS +# +# 1. Static code analysis with CodeQL + +# USAGE +# +# NOTE: This is our slowest workflow hence it only runs on code merged. +# +# Always triggered on PR merge when source code changes. + on: push: branches: [develop] +permissions: + contents: read + jobs: analyze: name: Analyze + if: github.repository == 'aws-powertools/powertools-lambda-python' runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['python'] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + permissions: + security-events: write + actions: read steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@f31a31c052207cc13b328d6295c5b728bb49568c # v2.13.1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@f31a31c052207cc13b328d6295c5b728bb49568c # v2.13.1 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000000..6f3ec5fb143 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,25 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: 'Checkout Repository' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: 'Dependency Review' + uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 diff --git a/.github/workflows/dispatch_analytics.yml b/.github/workflows/dispatch_analytics.yml new file mode 100644 index 00000000000..368e902ea1d --- /dev/null +++ b/.github/workflows/dispatch_analytics.yml @@ -0,0 +1,67 @@ +name: Dispatch analytics + +# PROCESS +# +# 1. Trade GitHub JWT token with AWS credentials for the analytics account +# 2. Invoke a Lambda function dispatcher synchronously with the read-only scoped JWT token +# 3. The dispatcher function will call GitHub APIs to read data from the last hour and aggregate for operational analytics + +# USAGE +# +# NOTE: meant to use as a scheduled task only (or manually for debugging purposes). + +on: + workflow_dispatch: + + schedule: + - cron: "0 * * * *" + +permissions: + contents: read + + +jobs: + dispatch_token: + if: github.repository == 'aws-powertools/powertools-lambda-python' + concurrency: + group: analytics + runs-on: ubuntu-latest + environment: analytics + permissions: + id-token: write + actions: read + checks: read + contents: read # previously we needed `write` to use GH_TOKEN in our dispatcher (Lambda) + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + security-events: read + statuses: read + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + aws-region: eu-central-1 + role-to-assume: ${{ secrets.AWS_LAYERS_ROLE_ARN }} + mask-aws-account-id: true + + - name: Invoke Lambda function + run: | + payload=$(echo -n '{"githubToken": "${{ secrets.GITHUB_TOKEN }}"}' | base64) + response=$(aws lambda invoke \ + --function-name "${{ secrets.AWS_ANALYTICS_DISPATCHER_ARN }}" \ + --payload "$payload" \ + response.json \ + --query 'FunctionError' \ + --output text) + + cat response.json ; echo # add newline at the end + + if [ "$response" != "None" ]; then + echo "Error invoking lambda function: $response. Aborting." + exit 1 + fi diff --git a/.github/workflows/label_pr_on_title.yml b/.github/workflows/label_pr_on_title.yml index 8d7871c899f..65b649b2080 100644 --- a/.github/workflows/label_pr_on_title.yml +++ b/.github/workflows/label_pr_on_title.yml @@ -1,87 +1,67 @@ name: Label PR based on title +# PROCESS +# +# 1. Fetch PR details previously saved from untrusted location +# 2. Parse details for safety +# 3. Label PR based on semantic title (e.g., area, change type) + +# USAGE +# +# NOTE: meant to be used with ./.github/workflows/record_pr.yml +# +# Security Note: +# +# This workflow depends on "Record PR" workflow that runs in an untrusted location (forks) instead of `pull_request_target`. +# This enforces zero trust where "Record PR" workflow always runs on fork with zero permissions on GH_TOKEN. +# When "Record PR" completes, this workflow runs in our repository with the appropriate permissions and sanitize inputs. +# +# Coupled with "Approve GitHub Action to run on forks", we have confidence no privilege can be escalated, +# since any malicious change would need to be approved, and upon social engineering, it'll have zero permissions. + + on: workflow_run: - workflows: ["Record PR number"] + workflows: ["Record PR details"] types: - completed +permissions: + contents: read + jobs: - upload: - runs-on: ubuntu-latest + get_pr_details: + permissions: + actions: read # download PR artifact + contents: read # checkout code # Guardrails to only ever run if PR recording workflow was indeed # run in a PR event and ran successfully - if: > - ${{ github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion == 'success' }} + if: ${{ github.event.workflow_run.conclusion == 'success' }} + uses: ./.github/workflows/reusable_export_pr_details.yml + with: + record_pr_workflow_id: ${{ github.event.workflow_run.id }} + workflow_origin: ${{ github.event.repository.full_name }} + secrets: + token: ${{ secrets.GITHUB_TOKEN }} + label_pr: + needs: get_pr_details + runs-on: ubuntu-latest + permissions: + pull-requests: write # label respective PR steps: - - name: 'Download artifact' - uses: actions/github-script@v5 - # For security, we only download artifacts tied to the successful PR recording workflow - with: - script: | - const fs = require('fs'); - - const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{github.event.workflow_run.id }}, - }); - - const matchArtifact = artifacts.data.artifacts.filter(artifact => artifact.name == "pr")[0]; - - const artifact = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - - fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(artifact.data)); - # NodeJS standard library doesn't provide ZIP capabilities; use system `unzip` command instead - - run: unzip pr.zip - - - name: 'Label PR based on title' - uses: actions/github-script@v5 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: "Label PR based on title" + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + PR_NUMBER: ${{ needs.get_pr_details.outputs.prNumber }} + PR_TITLE: ${{ needs.get_pr_details.outputs.prTitle }} + PR_LABELS: ${{ needs.get_pr_details.outputs.prLabels }} with: github-token: ${{ secrets.GITHUB_TOKEN }} # This safely runs in our base repo, not on fork # thus allowing us to provide a write access token to label based on PR title # and label PR based on semantic title accordingly script: | - const fs = require('fs'); - const pr_number = Number(fs.readFileSync('./number')); - const pr_title = fs.readFileSync('./title', 'utf-8').trim(); - - const FEAT_REGEX = /feat(\((\w+)\))?(\:.+)/ - const BUG_REGEX = /(fix|bug)(\((\w+)\))?(\:.+)/ - const DOCS_REGEX = /(docs|doc)(\((\w+)\))?(\:.+)/ - const CHORE_REGEX = /(chore)(\((\w+)\))?(\:.+)/ - const DEPRECATED_REGEX = /(deprecated)(\((\w+)\))?(\:.+)/ - const REFACTOR_REGEX = /(refactor)(\((\w+)\))?(\:.+)/ - - const labels = { - "feature": FEAT_REGEX, - "bug": BUG_REGEX, - "documentation": DOCS_REGEX, - "internal": CHORE_REGEX, - "enhancement": REFACTOR_REGEX, - "deprecated": DEPRECATED_REGEX, - } - - for (const label in labels) { - const matcher = new RegExp(labels[label]) - const isMatch = matcher.exec(pr_title) - if (isMatch != null) { - console.info(`Auto-labeling PR ${pr_number} with ${label}`) - - await github.rest.issues.addLabels({ - issue_number: pr_number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: [label] - }) - - break - } - } + const script = require('.github/scripts/label_pr_based_on_title.js') + await script({github, context, core}) diff --git a/.github/workflows/layer_govcloud.yml b/.github/workflows/layer_govcloud.yml new file mode 100644 index 00000000000..a136d1fa670 --- /dev/null +++ b/.github/workflows/layer_govcloud.yml @@ -0,0 +1,221 @@ +name: Layer Deployment (GovCloud) + +# GovCloud Layer Publish +# --- +# This workflow publishes a specific layer version in an AWS account based on the environment input. +# +# Using a matrix, we pull each architecture and python version of the layer and store them as artifacts +# we upload them to each of the GovCloud AWS accounts. +# +# A number of safety checks are performed to ensure safety. + +on: + workflow_dispatch: + inputs: + environment: + description: Deployment environment + type: choice + options: + - Gamma + - Prod + required: true + version: + description: Layer version to duplicate + type: string + required: true + workflow_call: + inputs: + environment: + description: Deployment environment + type: string + required: true + version: + description: Layer version to duplicate + type: string + required: true + +run-name: Layer Deployment (GovCloud) - ${{ inputs.environment }} + +permissions: + contents: read + +jobs: + download: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: Prod (Readonly) + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + mask-aws-account-id: true + - name: Grab Zip + run: | + aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} --query 'Content.Location' | xargs curl -L -o ${{ matrix.layer }}_${{ matrix.arch }}.zip + aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} > ${{ matrix.layer }}_${{ matrix.arch }}.json + - name: Store Zip + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.zip + path: ${{ matrix.layer }}_${{ matrix.arch }}.zip + retention-days: 1 + if-no-files-found: error + - name: Store Metadata + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.json + path: ${{ matrix.layer }}_${{ matrix.arch }}.json + retention-days: 1 + if-no-files-found: error + + copy_east: + name: Copy (East) + needs: download + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: GovCloud ${{ inputs.environment }} (East) + steps: + - name: Download Zip + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.zip + - name: Download Metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.json + - name: Verify Layer Signature + run: | + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$(openssl dgst -sha256 -binary ${{ matrix.layer }}_${{ matrix.arch }}.zip | openssl enc -base64)" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-gov-east-1 + mask-aws-account-id: true + - name: Create Layer + id: create-layer + run: | + LAYER_VERSION=$(aws --region us-gov-east-1 lambda publish-layer-version \ + --layer-name ${{ matrix.layer }}-${{ matrix.arch }} \ + --zip-file fileb://./${{ matrix.layer }}_${{ matrix.arch }}.zip \ + --compatible-runtimes "$(jq -r '.CompatibleRuntimes[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --compatible-architectures "$(jq -r '.CompatibleArchitectures[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --license-info "MIT-0" \ + --description "$(jq -r '.Description' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --query 'Version' \ + --output text) + + echo "LAYER_VERSION=$LAYER_VERSION" >> "$GITHUB_OUTPUT" + + aws --region us-gov-east-1 lambda add-layer-version-permission \ + --layer-name '${{ matrix.layer }}-${{ matrix.arch }}' \ + --statement-id 'PublicLayer' \ + --action lambda:GetLayerVersion \ + --principal '*' \ + --version-number "$LAYER_VERSION" + - name: Verify Layer + env: + LAYER_VERSION: ${{ steps.create-layer.outputs.LAYER_VERSION }} + run: | + REMOTE_SHA=$(aws --region us-gov-east-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-east-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --query 'Content.CodeSha256' --output text) + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$REMOTE_SHA" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + aws --region us-gov-east-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-east-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --output table + + copy_west: + name: Copy (West) + needs: download + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: + name: GovCloud ${{ inputs.environment }} (West) + steps: + - name: Download Zip + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.zip + - name: Download Metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.json + - name: Verify Layer Signature + run: | + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$(openssl dgst -sha256 -binary ${{ matrix.layer }}_${{ matrix.arch }}.zip | openssl enc -base64)" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-gov-west-1 + mask-aws-account-id: true + - name: Create Layer + id: create-layer + run: | + LAYER_VERSION=$(aws --region us-gov-west-1 lambda publish-layer-version \ + --layer-name ${{ matrix.layer }}-${{ matrix.arch }} \ + --zip-file fileb://./${{ matrix.layer }}_${{ matrix.arch }}.zip \ + --compatible-runtimes "$(jq -r '.CompatibleRuntimes[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --compatible-architectures "$(jq -r '.CompatibleArchitectures[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --license-info "MIT-0" \ + --description "$(jq -r '.Description' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --query 'Version' \ + --output text) + + echo "LAYER_VERSION=$LAYER_VERSION" >> "$GITHUB_OUTPUT" + + aws --region us-gov-west-1 lambda add-layer-version-permission \ + --layer-name '${{ matrix.layer }}-${{ matrix.arch }}' \ + --statement-id 'PublicLayer' \ + --action lambda:GetLayerVersion \ + --principal '*' \ + --version-number "$LAYER_VERSION" + - name: Verify Layer + env: + LAYER_VERSION: ${{ steps.create-layer.outputs.LAYER_VERSION }} + run: | + REMOTE_SHA=$(aws --region us-gov-west-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-west-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --query 'Content.CodeSha256' --output text) + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$REMOTE_SHA" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + aws --region us-gov-west-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-west-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --output table diff --git a/.github/workflows/layer_govcloud_python313.yml b/.github/workflows/layer_govcloud_python313.yml new file mode 100644 index 00000000000..b889e258ca4 --- /dev/null +++ b/.github/workflows/layer_govcloud_python313.yml @@ -0,0 +1,209 @@ +name: Layer Deployment (GovCloud) - Temporary for Python 3.13 + +# GovCloud Layer Publish +# --- +# This workflow publishes a specific layer version in an AWS account based on the environment input. +# +# Using a matrix, we pull each architecture and python version of the layer and store them as artifacts +# we upload them to each of the GovCloud AWS accounts. +# +# A number of safety checks are performed to ensure safety. + +on: + workflow_dispatch: + inputs: + environment: + description: Deployment environment + type: choice + options: + - Gamma + - Prod + required: true + version: + description: Layer version to duplicate + type: string + required: true + workflow_call: + inputs: + environment: + description: Deployment environment + type: string + required: true + version: + description: Layer version to duplicate + type: string + required: true + +run-name: Layer Deployment (GovCloud) - ${{ inputs.environment }} + +permissions: + contents: read + +jobs: + download: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: Prod (Readonly) + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + mask-aws-account-id: true + - name: Grab Zip + run: | + aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} --query 'Content.Location' | xargs curl -L -o ${{ matrix.layer }}_${{ matrix.arch }}.zip + aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} > ${{ matrix.layer }}_${{ matrix.arch }}.json + - name: Store Zip + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.zip + path: ${{ matrix.layer }}_${{ matrix.arch }}.zip + retention-days: 1 + if-no-files-found: error + - name: Store Metadata + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.json + path: ${{ matrix.layer }}_${{ matrix.arch }}.json + retention-days: 1 + if-no-files-found: error + + copy_east: + name: Copy (East) + needs: download + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: GovCloud ${{ inputs.environment }} (East) + steps: + - name: Download Zip + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.zip + - name: Download Metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.json + - name: Verify Layer Signature + run: | + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$(openssl dgst -sha256 -binary ${{ matrix.layer }}_${{ matrix.arch }}.zip | openssl enc -base64)" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-gov-east-1 + mask-aws-account-id: true + - name: Create Layer + id: create-layer + run: | + LAYER_VERSION=$(aws --region us-gov-east-1 lambda publish-layer-version \ + --layer-name ${{ matrix.layer }}-${{ matrix.arch }} \ + --zip-file fileb://./${{ matrix.layer }}_${{ matrix.arch }}.zip \ + --compatible-runtimes "$(jq -r '.CompatibleRuntimes[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --compatible-architectures "$(jq -r '.CompatibleArchitectures[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --license-info "MIT-0" \ + --description "$(jq -r '.Description' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --query 'Version' \ + --output text) + + echo "LAYER_VERSION=$LAYER_VERSION" >> "$GITHUB_OUTPUT" + + aws --region us-gov-east-1 lambda add-layer-version-permission \ + --layer-name '${{ matrix.layer }}-${{ matrix.arch }}' \ + --statement-id 'PublicLayer' \ + --action lambda:GetLayerVersion \ + --principal '*' \ + --version-number "$LAYER_VERSION" + - name: Verify Layer + env: + LAYER_VERSION: ${{ steps.create-layer.outputs.LAYER_VERSION }} + run: | + REMOTE_SHA=$(aws --region us-gov-east-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-east-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --query 'Content.CodeSha256' --output text) + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$REMOTE_SHA" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + aws --region us-gov-east-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-east-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --output table + + copy_west: + name: Copy (West) + needs: download + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: + name: GovCloud ${{ inputs.environment }} (West) + steps: + - name: Download Zip + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.zip + - name: Download Metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}_${{ matrix.arch }}.json + - name: Verify Layer Signature + run: | + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$(openssl dgst -sha256 -binary ${{ matrix.layer }}_${{ matrix.arch }}.zip | openssl enc -base64)" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-gov-west-1 + mask-aws-account-id: true + - name: Create Layer + id: create-layer + run: | + LAYER_VERSION=$(aws --region us-gov-west-1 lambda publish-layer-version \ + --layer-name ${{ matrix.layer }}-${{ matrix.arch }} \ + --zip-file fileb://./${{ matrix.layer }}_${{ matrix.arch }}.zip \ + --compatible-runtimes "$(jq -r '.CompatibleRuntimes[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --compatible-architectures "$(jq -r '.CompatibleArchitectures[0]' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --license-info "MIT-0" \ + --description "$(jq -r '.Description' '${{ matrix.layer }}_${{ matrix.arch }}.json')" \ + --query 'Version' \ + --output text) + + echo "LAYER_VERSION=$LAYER_VERSION" >> "$GITHUB_OUTPUT" + + aws --region us-gov-west-1 lambda add-layer-version-permission \ + --layer-name '${{ matrix.layer }}-${{ matrix.arch }}' \ + --statement-id 'PublicLayer' \ + --action lambda:GetLayerVersion \ + --principal '*' \ + --version-number "$LAYER_VERSION" + - name: Verify Layer + env: + LAYER_VERSION: ${{ steps.create-layer.outputs.LAYER_VERSION }} + run: | + REMOTE_SHA=$(aws --region us-gov-west-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-west-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --query 'Content.CodeSha256' --output text) + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json') + test "$REMOTE_SHA" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + aws --region us-gov-west-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-west-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' --output table diff --git a/.github/workflows/layer_govcloud_verify.yml b/.github/workflows/layer_govcloud_verify.yml new file mode 100644 index 00000000000..fe12fc1c111 --- /dev/null +++ b/.github/workflows/layer_govcloud_verify.yml @@ -0,0 +1,111 @@ +# GovCloud Layer Verification +# --- +# This workflow queries the GovCloud layer info in production only + +on: + workflow_dispatch: + inputs: + version: + description: Layer version to verify information + type: string + required: true + workflow_call: + inputs: + version: + description: Layer version to verify information + type: string + required: true + +name: Layer Verification (GovCloud) +run-name: Layer Verification (GovCloud) + +jobs: + commercial: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: Prod (Readonly) + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + mask-aws-account-id: true + - name: Output ${{ matrix.layer }}-${{ matrix.arch }} + run: | + aws --region us-east-1 lambda get-layer-version-by-arn --arn 'arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }}' | jq -r '{"Layer Version Arn": .LayerVersionArn, "Version": .Version, "Description": .Description, "Compatible Runtimes": .CompatibleRuntimes[0], "Compatible Architectures": .CompatibleArchitectures[0], "SHA": .Content.CodeSha256} | keys[] as $k | [$k, .[$k]] | @tsv' | column -t -s $'\t' + + gov_east: + name: Verify (East) + needs: commercial + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: GovCloud Prod (East) + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-gov-east-1 + mask-aws-account-id: true + - name: Verify Layer ${{ matrix.layer }}-${{ matrix.arch }} + id: verify-layer + run: | + aws --region us-gov-east-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-east-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }}' | jq -r '{"Layer Version Arn": .LayerVersionArn, "Version": .Version, "Description": .Description, "Compatible Runtimes": .CompatibleRuntimes[0], "Compatible Architectures": .CompatibleArchitectures[0], "SHA": .Content.CodeSha256} | keys[] as $k | [$k, .[$k]] | @tsv' | column -t -s $'\t' + + gov_west: + name: Verify (West) + needs: commercial + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + environment: GovCloud Prod (West) + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-gov-east-1 + mask-aws-account-id: true + - name: Verify Layer ${{ matrix.layer }}-${{ matrix.arch }} + id: verify-layer + run: | + aws --region us-gov-west-1 lambda get-layer-version-by-arn --arn 'arn:aws-us-gov:lambda:us-gov-west-1:${{ secrets.AWS_ACCOUNT_ID }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }}' | jq -r '{"Layer Version Arn": .LayerVersionArn, "Version": .Version, "Description": .Description, "Compatible Runtimes": .CompatibleRuntimes[0], "Compatible Architectures": .CompatibleArchitectures[0], "SHA": .Content.CodeSha256} | keys[] as $k | [$k, .[$k]] | @tsv' | column -t -s $'\t' diff --git a/.github/workflows/layers_partition_verify.yml b/.github/workflows/layers_partition_verify.yml new file mode 100644 index 00000000000..53b613c89f3 --- /dev/null +++ b/.github/workflows/layers_partition_verify.yml @@ -0,0 +1,156 @@ +# Partition Layer Verification +# --- +# This workflow queries the Partition layer info in production only + +on: + workflow_dispatch: + inputs: + environment: + description: Deployment environment + type: choice + options: + - Gamma + - Prod + required: true + version: + description: Layer version to verify + type: string + required: true + partition_version: + description: Layer version to verify, this is mostly used in Gamma where a version mismatch might exist + type: string + required: false + partition: + description: Partition to deploy to + type: choice + options: + - China + - GovCloud + workflow_call: + inputs: + environment: + description: Deployment environment + type: string + required: true + version: + description: Layer version to verify + type: string + required: true + partition_version: + description: Partition Layer version to verify, this is mostly used in Gamma where a version mismatch might exist + type: string + required: false + +name: Layer Verification (Partition) +run-name: Layer Verification (${{ inputs.partition }}) - ${{ inputs.environment }} / Version - ${{ inputs.version }} + +permissions: {} + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + regions: ${{ format('{0}{1}', steps.regions_china.outputs.regions, steps.regions_govcloud.outputs.regions) }} + partition: ${{ format('{0}{1}', steps.regions_china.outputs.partition, steps.regions_govcloud.outputs.partition) }} + aud: ${{ format('{0}{1}', steps.regions_china.outputs.aud, steps.regions_govcloud.outputs.aud) }} + steps: + - id: regions_china + name: Partition (China) + if: ${{ inputs.partition == 'China' }} + run: | + echo regions='["cn-north-1"]'>> "$GITHUB_OUTPUT" + echo partition='aws-cn'>> "$GITHUB_OUTPUT" + echo aud='sts.amazonaws.com.cn'>> "$GITHUB_OUTPUT" + - id: regions_govcloud + name: Partition (GovCloud) + if: ${{ inputs.partition == 'GovCloud' }} + run: | + echo regions='["us-gov-east-1", "us-gov-west-1"]'>> "$GITHUB_OUTPUT" + echo partition='aws-us-gov'>> "$GITHUB_OUTPUT" + echo aud='sts.amazonaws.com'>> "$GITHUB_OUTPUT" + commercial: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + environment: Prod (Readonly) + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + mask-aws-account-id: true + - name: Output ${{ matrix.layer }}-${{ matrix.arch }} + # fetch the specific layer version information from the us-east-1 commercial region + run: | + aws --region us-east-1 lambda get-layer-version-by-arn --arn 'arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }}' > '${{ matrix.layer }}-${{ matrix.arch }}.json' + - name: Store Metadata + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}-${{ matrix.arch }}.json + path: ${{ matrix.layer }}-${{ matrix.arch }}.json + retention-days: 1 + if-no-files-found: error + + verify: + name: Verify + needs: + - setup + - commercial + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + # Environment should interperlate as "GovCloud Prod" or "China Beta" + environment: ${{ inputs.partition }} ${{ inputs.environment }} + strategy: + matrix: + region: ${{ fromJson(needs.setup.outputs.regions) }} + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + steps: + - name: Download Metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}-${{ matrix.arch }}.json + - id: transform + run: | + echo 'CONVERTED_REGION=${{ matrix.region }}' | tr 'a-z\-' 'A-Z_' >> "$GITHUB_OUTPUT" + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets[format('IAM_ROLE_{0}', steps.transform.outputs.CONVERTED_REGION)] }} + aws-region: ${{ matrix.region}} + mask-aws-account-id: true + audience: ${{ needs.setup.outputs.aud }} + - id: partition_version + name: Partition Layer Version + run: | + echo 'partition_version=$([[ -n "${{ inputs.partition_version}}" ]] && echo ${{ inputs.partition_version}} || echo ${{ inputs.version }} )' >> "$GITHUB_OUTPUT" + - name: Verify Layer + run: | + export layer_output='${{ matrix.layer }}-${{ matrix.arch }}-${{matrix.region}}.json' + aws --region ${{ matrix.region}} lambda get-layer-version-by-arn --arn "arn:${{ needs.setup.outputs.partition }}:lambda:${{ matrix.region}}:${{ secrets[format('AWS_ACCOUNT_{0}', steps.transform.outputs.CONVERTED_REGION)] }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ steps.partition_version.outputs.partition_version }}" > $layer_output + REMOTE_SHA=$(jq -r '.Content.CodeSha256' $layer_output) + LOCAL_SHA=$(jq -r '.Content.CodeSha256' ${{ matrix.layer }}-${{ matrix.arch }}.json) + test "$REMOTE_SHA" == "$LOCAL_SHA" && echo "SHA OK: ${LOCAL_SHA}" || exit 1 + jq -s -r '["Layer Arn", "Runtimes", "Version", "Description", "SHA256"], ([.[0], .[1]] | .[] | [.LayerArn, (.CompatibleRuntimes | join("/")), .Version, .Description, .Content.CodeSha256]) |@tsv' ${{ matrix.layer }}-${{ matrix.arch }}.json $layer_output | column -t -s $'\t' \ No newline at end of file diff --git a/.github/workflows/layers_partitions.yml b/.github/workflows/layers_partitions.yml new file mode 100644 index 00000000000..4c239c478f7 --- /dev/null +++ b/.github/workflows/layers_partitions.yml @@ -0,0 +1,193 @@ +# Partitioned Layer Publish +# --- +# This workflow publishes a specific layer version in an AWS account based on the environment input. +# +# We pull each the version of the layer and store them as artifacts, the we upload them to each of the Partitioned AWS accounts. +# +# A number of safety checks are performed to ensure safety. + +on: + workflow_dispatch: + inputs: + environment: + description: Deployment environment + type: choice + options: + - Gamma + - Prod + required: true + version: + description: Layer version to duplicate + type: string + required: true + partition: + description: Partition to deploy to + type: choice + options: + - China + - GovCloud + workflow_call: + inputs: + environment: + description: Deployment environment + type: string + required: true + version: + description: Layer version to duplicate + type: string + required: true + +name: Layer Deployment (Partitions) +run-name: Layer Deployment (${{ inputs.partition }}) - ${{ inputs.environment }} / Version - ${{ inputs.version }} + +permissions: + contents: read + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + regions: ${{ format('{0}{1}', steps.regions_china.outputs.regions, steps.regions_govcloud.outputs.regions) }} + partition: ${{ format('{0}{1}', steps.regions_china.outputs.partition, steps.regions_govcloud.outputs.partition) }} + aud: ${{ format('{0}{1}', steps.regions_china.outputs.aud, steps.regions_govcloud.outputs.aud) }} + steps: + - id: regions_china + name: Partition (China) + if: ${{ inputs.partition == 'China' }} + run: | + echo regions='["cn-north-1"]'>> "$GITHUB_OUTPUT" + echo partition='aws-cn'>> "$GITHUB_OUTPUT" + echo aud='sts.amazonaws.com.cn'>> "$GITHUB_OUTPUT" + - id: regions_govcloud + name: Partition (GovCloud) + if: ${{ inputs.partition == 'GovCloud' }} + run: | + echo regions='["us-gov-east-1", "us-gov-west-1"]'>> "$GITHUB_OUTPUT" + echo partition='aws-us-gov'>> "$GITHUB_OUTPUT" + echo aud='sts.amazonaws.com'>> "$GITHUB_OUTPUT" + download: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + environment: Prod (Readonly) + strategy: + matrix: + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + mask-aws-account-id: true + - name: Grab Zip + run: | + aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} --query 'Content.Location' | xargs curl -L -o ${{ matrix.layer }}-${{ matrix.arch }}.zip + aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} > ${{ matrix.layer }}-${{ matrix.arch }}.json + - name: Store Zip + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}-${{ matrix.arch }}.zip + path: ${{ matrix.layer }}-${{ matrix.arch }}.zip + retention-days: 1 + if-no-files-found: error + - name: Store Metadata + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}-${{ matrix.arch }}.json + path: ${{ matrix.layer }}-${{ matrix.arch }}.json + retention-days: 1 + if-no-files-found: error + + copy: + name: Copy + needs: + - setup + - download + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + # Environment should interperlate as "GovCloud Prod" or "China Beta" + environment: ${{ inputs.partition }} ${{ inputs.environment }} + strategy: + matrix: + region: ${{ fromJson(needs.setup.outputs.regions) }} + layer: + - AWSLambdaPowertoolsPythonV3-python39 + - AWSLambdaPowertoolsPythonV3-python310 + - AWSLambdaPowertoolsPythonV3-python311 + - AWSLambdaPowertoolsPythonV3-python312 + - AWSLambdaPowertoolsPythonV3-python313 + arch: + - arm64 + - x86_64 + steps: + - name: Download Zip + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}-${{ matrix.arch }}.zip + - name: Download Metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ matrix.layer }}-${{ matrix.arch }}.json + - name: Verify Layer Signature + run: | + SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}-${{ matrix.arch }}.json') + test "$(openssl dgst -sha256 -binary ${{ matrix.layer }}-${{ matrix.arch }}.zip | openssl enc -base64)" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1 + - id: transform + run: | + echo 'CONVERTED_REGION=${{ matrix.region }}' | tr 'a-z\-' 'A-Z_' >> "$GITHUB_OUTPUT" + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets[format('IAM_ROLE_{0}', steps.transform.outputs.CONVERTED_REGION)] }} + aws-region: ${{ matrix.region}} + mask-aws-account-id: true + audience: ${{ needs.setup.outputs.aud }} + - name: Create Layer + id: create-layer + run: | + cat '${{ matrix.layer }}-${{ matrix.arch }}.json' | jq '{"LayerName": "${{ matrix.layer }}-${{ matrix.arch }}", "Description": .Description, "CompatibleRuntimes": .CompatibleRuntimes, "CompatibleArchitectures": .CompatibleArchitectures, "LicenseInfo": .LicenseInfo}' > input.json + + LAYER_VERSION=$(aws --region ${{ matrix.region}} lambda publish-layer-version \ + --zip-file 'fileb://./${{ matrix.layer }}-${{ matrix.arch }}.zip' \ + --cli-input-json file://./input.json \ + --query 'Version' \ + --output text) + + echo "LAYER_VERSION=$LAYER_VERSION" >> "$GITHUB_OUTPUT" + + aws --region ${{ matrix.region}} lambda add-layer-version-permission \ + --layer-name ${{ matrix.layer }}-${{ matrix.arch }} \ + --statement-id 'PublicLayer' \ + --action lambda:GetLayerVersion \ + --principal '*' \ + --version-number "$LAYER_VERSION" + - name: Verify Layer + env: + LAYER_VERSION: ${{ steps.create-layer.outputs.LAYER_VERSION }} + run: | + export layer_output='${{ matrix.layer }}-${{ matrix.arch }}-${{matrix.region}}.json' + aws --region ${{ matrix.region}} lambda get-layer-version-by-arn --arn 'arn:${{ needs.setup.outputs.partition }}:lambda:${{ matrix.region}}:${{ secrets[format('AWS_ACCOUNT_{0}', steps.transform.outputs.CONVERTED_REGION)] }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' > $layer_output + REMOTE_SHA=$(jq -r '.Content.CodeSha256' $layer_output) + LOCAL_SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}-${{ matrix.arch }}.json') + test "$REMOTE_SHA" == "$LOCAL_SHA" && echo "SHA OK: ${LOCAL_SHA}" || exit 1 + jq -s -r '["Layer Arn", "Runtimes", "Version", "Description", "SHA256"], ([.[0], .[1]] | .[] | [.LayerArn, (.CompatibleRuntimes | join("/")), .Version, .Description, .Content.CodeSha256]) |@tsv' '${{ matrix.layer }}-${{ matrix.arch }}.json' $layer_output | column -t -s $'\t' + + - name: Store Metadata - ${{ matrix.region }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ matrix.layer }}-${{ matrix.arch }}-${{ matrix.region }}.json + path: ${{ matrix.layer }}-${{ matrix.arch }}-${{ matrix.region }}.json + retention-days: 1 + if-no-files-found: error \ No newline at end of file diff --git a/.github/workflows/on_closed_issues.yml b/.github/workflows/on_closed_issues.yml new file mode 100644 index 00000000000..78c2c84033e --- /dev/null +++ b/.github/workflows/on_closed_issues.yml @@ -0,0 +1,33 @@ +name: Closed Issue Message + +# PROCESS +# +# 1. Comment on recently closed issues to warn future responses may not be looked after + +# USAGE +# +# Always triggered upon issue closure +# + +on: + issues: + types: [closed] +permissions: + contents: read + +jobs: + auto_comment: + runs-on: ubuntu-latest + permissions: + issues: write # comment on issues + steps: + - uses: aws-powertools/actions/.github/actions/close-issue-message@428c1934f4b22c0984ff4a39b66c2f70765bbed6 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + message: | + ### ⚠️COMMENT VISIBILITY WARNING⚠️ + This issue is now closed. Please be mindful that future comments are hard for our team to see. + + If you need more assistance, please either tag a [team member](https://github.com/aws-powertools/powertools-lambda-python/blob/develop/MAINTAINERS.md#current-maintainers) or open a new issue that references this one. + + If you wish to keep having a conversation with other community members under this issue feel free to do so. diff --git a/.github/workflows/on_label_added.yml b/.github/workflows/on_label_added.yml new file mode 100644 index 00000000000..50ba0992188 --- /dev/null +++ b/.github/workflows/on_label_added.yml @@ -0,0 +1,62 @@ +name: On Label added + +# PROCESS +# +# 1. Fetch PR details previously saved from untrusted location +# 2. Parse details for safety +# 3. Comment on PR labels `size/XXL` and suggest splitting into smaller PRs if possible + +# USAGE +# +# NOTE: meant to be used with ./.github/workflows/record_pr.yml +# +# Security Note: +# +# This workflow depends on "Record PR" workflow that runs in an untrusted location (forks) instead of `pull_request_target`. +# This enforces zero trust where "Record PR" workflow always runs on fork with zero permissions on GH_TOKEN. +# When "Record PR" completes, this workflow runs in our repository with the appropriate permissions and sanitize inputs. +# +# Coupled with "Approve GitHub Action to run on forks", we have confidence no privilege can be escalated, +# since any malicious change would need to be approved, and upon social engineering, it'll have zero permissions. + +on: + workflow_run: + workflows: ["Record PR details"] + types: + - completed + +permissions: + contents: read + +jobs: + get_pr_details: + permissions: + actions: read # download PR artifact + contents: read # checkout code + if: ${{ github.event.workflow_run.conclusion == 'success' }} + uses: ./.github/workflows/reusable_export_pr_details.yml + with: + record_pr_workflow_id: ${{ github.event.workflow_run.id }} + workflow_origin: ${{ github.event.repository.full_name }} + secrets: + token: ${{ secrets.GITHUB_TOKEN }} + + split_large_pr: + needs: get_pr_details + runs-on: ubuntu-latest + permissions: + pull-requests: write # comment on PR + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + # Maintenance: Persist state per PR as an artifact to avoid spam on label add + - name: "Suggest split large Pull Request" + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + PR_NUMBER: ${{ needs.get_pr_details.outputs.prNumber }} + PR_ACTION: ${{ needs.get_pr_details.outputs.prAction }} + PR_AUTHOR: ${{ needs.get_pr_details.outputs.prAuthor }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const script = require('.github/scripts/comment_on_large_pr.js'); + await script({github, context, core}); diff --git a/.github/workflows/on_merged_pr.yml b/.github/workflows/on_merged_pr.yml new file mode 100644 index 00000000000..68eaf552e16 --- /dev/null +++ b/.github/workflows/on_merged_pr.yml @@ -0,0 +1,64 @@ +name: On PR merge + +# PROCESS +# +# 1. Fetch PR details previously saved from untrusted location +# 2. Parse details for safety +# 3. Add `pending-release` label for related issue +# 4. Make a comment in PR if related issue is invalid or can't be labeled + +# USAGE +# +# NOTE: meant to be used with ./.github/workflows/record_pr.yml +# +# Security Note: +# +# This workflow depends on "Record PR" workflow that runs in an untrusted location (forks) instead of `pull_request_target`. +# This enforces zero trust where "Record PR" workflow always runs on fork with zero permissions on GH_TOKEN. +# When "Record PR" completes, this workflow runs in our repository with the appropriate permissions and sanitize inputs. +# +# Coupled with "Approve GitHub Action to run on forks", we have confidence no privilege can be escalated, +# since any malicious change would need to be approved, and upon social engineering, it'll have zero permissions. + +on: + workflow_run: + workflows: ["Record PR details"] + types: + - completed + +permissions: + contents: read + +jobs: + get_pr_details: + permissions: + actions: read # download PR artifact + contents: read # checkout code + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' + uses: ./.github/workflows/reusable_export_pr_details.yml + with: + record_pr_workflow_id: ${{ github.event.workflow_run.id }} + workflow_origin: ${{ github.event.repository.full_name }} + secrets: + token: ${{ secrets.GITHUB_TOKEN }} + release_label_on_merge: + needs: get_pr_details + runs-on: ubuntu-latest + permissions: + pull-requests: write # make a comment in PR if unable to find related issue + issues: write # label issue with pending-release + if: needs.get_pr_details.outputs.prIsMerged == 'true' + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: "Label PR related issue for release" + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + PR_NUMBER: ${{ needs.get_pr_details.outputs.prNumber }} + PR_BODY: ${{ needs.get_pr_details.outputs.prBody }} + PR_IS_MERGED: ${{ needs.get_pr_details.outputs.prIsMerged }} + PR_AUTHOR: ${{ needs.get_pr_details.outputs.prAuthor }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const script = require('.github/scripts/label_related_issue.js') + await script({github, context, core}) diff --git a/.github/workflows/on_opened_pr.yml b/.github/workflows/on_opened_pr.yml new file mode 100644 index 00000000000..0db1bcaa026 --- /dev/null +++ b/.github/workflows/on_opened_pr.yml @@ -0,0 +1,81 @@ +name: On new PR + +# PROCESS +# +# 1. Fetch PR details previously saved from untrusted location +# 2. Parse details for safety +# 3. Confirm there is a related issue for newly opened PR +# 4. Verify if PR template is used and legal acknowledgement hasn't been removed + +# USAGE +# +# NOTE: meant to be used with ./.github/workflows/record_pr.yml +# +# Security Note: +# +# This workflow depends on "Record PR" workflow that runs in an untrusted location (forks) instead of `pull_request_target`. +# This enforces zero trust where "Record PR" workflow always runs on fork with zero permissions on GH_TOKEN. +# When "Record PR" completes, this workflow runs in our repository with the appropriate permissions and sanitize inputs. +# +# Coupled with "Approve GitHub Action to run on forks", we have confidence no privilege can be escalated, +# since any malicious change would need to be approved, and upon social engineering, it'll have zero permissions. + +on: + workflow_run: + workflows: ["Record PR details"] + types: + - completed + +permissions: + contents: read + +jobs: + get_pr_details: + permissions: + actions: read # download PR artifact + contents: read # checkout code + if: ${{ github.event.workflow_run.conclusion == 'success' }} + uses: ./.github/workflows/reusable_export_pr_details.yml + with: + record_pr_workflow_id: ${{ github.event.workflow_run.id }} + workflow_origin: ${{ github.event.repository.full_name }} + secrets: + token: ${{ secrets.GITHUB_TOKEN }} + check_related_issue: + permissions: + pull-requests: write # label and comment on PR if missing related issue (requirement) + needs: get_pr_details + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: "Ensure related issue is present" + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + PR_BODY: ${{ needs.get_pr_details.outputs.prBody }} + PR_NUMBER: ${{ needs.get_pr_details.outputs.prNumber }} + PR_ACTION: ${{ needs.get_pr_details.outputs.prAction }} + PR_AUTHOR: ${{ needs.get_pr_details.outputs.prAuthor }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const script = require('.github/scripts/label_missing_related_issue.js') + await script({github, context, core}) + check_acknowledge_section: + needs: get_pr_details + runs-on: ubuntu-latest + permissions: + pull-requests: write # label and comment on PR if missing acknowledge section (requirement) + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: "Ensure acknowledgement section is present" + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + PR_BODY: ${{ needs.get_pr_details.outputs.prBody }} + PR_NUMBER: ${{ needs.get_pr_details.outputs.prNumber }} + PR_ACTION: ${{ needs.get_pr_details.outputs.prAction }} + PR_AUTHOR: ${{ needs.get_pr_details.outputs.prAuthor }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const script = require('.github/scripts/label_missing_acknowledgement_section.js') + await script({github, context, core}) diff --git a/.github/workflows/on_pr_updates.yml b/.github/workflows/on_pr_updates.yml new file mode 100644 index 00000000000..2663d707399 --- /dev/null +++ b/.github/workflows/on_pr_updates.yml @@ -0,0 +1,36 @@ +# Fail PR check if do-not-merge label is present +name: PR requirements + +# PROCESS +# +# 1. Verify whether 'do-not-merge' label is present +# 2. Fail PR to prevent merging until resolved +# 3. Pass PR if do-not-merge label is removed by a maintainer + +# USAGE +# +# Always triggered on PR labeling changes. + +# NOTES +# +# PR requirements are checked async in on_opened_pr.yml and enforced here synchronously +# due to limitations in GH API. + +on: + pull_request: + types: + - opened + - labeled + - unlabeled + +permissions: {} # no permission required + +jobs: + check-requirements: + runs-on: ubuntu-latest + steps: + - name: Block if it doesn't minimum requirements + if: contains(github.event.pull_request.labels.*.name, 'do-not-merge') + run: | + echo "This PR does not meet minimum requirements (check PR comments)." + exit 1 diff --git a/.github/workflows/on_push_docs.yml b/.github/workflows/on_push_docs.yml new file mode 100644 index 00000000000..fc1aa3786e6 --- /dev/null +++ b/.github/workflows/on_push_docs.yml @@ -0,0 +1,36 @@ +name: Docs + +# PROCESS +# +# 1. Build User Guide and API docs +# 2. Publish to GitHub Pages +# 3. Publish to S3 (new home) + +# USAGE +# +# Always triggered on PR merge when changes in documentation changes occur. + +on: + push: + branches: + - develop + paths: + - "docs/**" + - "mkdocs.yml" + - "examples/**" + - "CHANGELOG.md" + +permissions: + contents: read + +jobs: + release-docs: + permissions: + contents: write # push to gh-pages + pages: write # deploy gh-pages website + id-token: write # trade JWT token for AWS credentials in AWS Docs account + secrets: inherit + uses: ./.github/workflows/reusable_publish_docs.yml + with: + version: develop + alias: stage \ No newline at end of file diff --git a/.github/workflows/on_schedule_monthly_roadmap_reminder.yml b/.github/workflows/on_schedule_monthly_roadmap_reminder.yml new file mode 100644 index 00000000000..a274e2dea08 --- /dev/null +++ b/.github/workflows/on_schedule_monthly_roadmap_reminder.yml @@ -0,0 +1,22 @@ +name: Monthly roadmap reminder + +on: + workflow_dispatch: {} + schedule: + - cron: '0 0 1 * *' # runs first day of the month + +permissions: + contents: read + + +jobs: + call-workflow-passing-data: + permissions: + contents: read + pull-requests: read + issues: write # create monthly roadmap report + + # setting to `@main` until we have releases and governance installed + uses: aws-powertools/actions/.github/workflows/monthly_roadmap_reminder.yml@main + secrets: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ossf_scorecard.yml b/.github/workflows/ossf_scorecard.yml new file mode 100644 index 00000000000..660d32dcc74 --- /dev/null +++ b/.github/workflows/ossf_scorecard.yml @@ -0,0 +1,48 @@ +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + schedule: + - cron: "0 9 * * *" + push: + branches: [develop] + workflow_dispatch: + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + environment: scorecard + permissions: + security-events: write # update code-scanning dashboard + id-token: write # confirm org+repo identity before publish results + + steps: + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + with: + results_file: results.sarif + results_format: sarif + publish_results: true # publish to OSSF Scorecard REST API + repo_token: ${{ secrets.SCORECARD_TOKEN }} # read-only fine-grained token to read branch protection settings + + - name: "Upload results" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 00000000000..1acb898fbfb --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,275 @@ +name: Pre-Release + +# PRE-RELEASE PROCESS +# +# === Automated activities === +# +# 1. [Seal] Bump to release version and export source code with integrity hash +# 2. [Quality check] Restore sealed source code, run tests, linting, security and complexity base line +# 3. [Build] Restore sealed source code, create and export hashed build artifact for PyPi release (wheel, tarball) +# 4. [Provenance] Generates provenance for build, signs attestation with GitHub OIDC claims to confirm it came from this release pipeline, commit, org, repo, branch, hash, etc. +# 5. [Release] Restore built artifact, and publish package to PyPi prod repository +# 6. [PR to bump version] Restore sealed source code, and create a PR to update trunk with latest released project metadata + +# NOTE +# +# See MAINTAINERS.md "Releasing a new version" for release mechanisms +# +# Every job is isolated and starts a new fresh container. + +env: + RELEASE_COMMIT: ${{ github.sha }} + +on: + workflow_dispatch: + inputs: + skip_code_quality: + description: "Skip tests, linting, and baseline. Only use if release fail for reasons beyond our control and you need a quick release." + default: false + type: boolean + required: false + skip_pypi: + description: "Skip publishing to PyPi. Used for testing release steps." + default: false + type: boolean + required: false + schedule: + # Note: run daily on weekdays at 8am UTC time + - cron: "0 8 * * 1-5" + +permissions: + contents: read + +jobs: + + # This job bumps the package version to the pre-release version + # creates an integrity hash from the source code + # uploads the artifact with the integrity hash as the key name + # so subsequent jobs can restore from a trusted point in time to prevent tampering + seal: + # ignore forks + if: github.repository == 'aws-powertools/powertools-lambda-python' + + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + integrity_hash: ${{ steps.seal_source_code.outputs.integrity_hash }} + artifact_name: ${{ steps.seal_source_code.outputs.artifact_name }} + RELEASE_VERSION: ${{ steps.release_version.outputs.RELEASE_VERSION }} + steps: + # NOTE: Different from prod release, we need both poetry and source code available in earlier steps to bump and verify. + + # We use a pinned version of Poetry to be certain it won't modify source code before we create a hash + - name: Install poetry + run: | + pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + pipx inject poetry git+https://github.com/monim67/poetry-bumpversion@348de6f247222e2953d649932426e63492e0a6bf # v0.3.3 + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Bump and export release version + id: release_version + run: | + RELEASE_VERSION="$(poetry version prerelease --short | head -n1 | tr -d '\n')" + + echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Verifies pre-release version semantics + # verify pre-release semantics before proceeding to avoid versioning pollution + # e.g., 2.40.0a1 and 2.40.0b2 are valid while 2.40.0 is not + # NOTE. we do it in a separate step to handle edge cases like + # `poetry` CLI uses immutable install, versioning behaviour could change even in a minor version (we had breaking changes before) + # a separate step allows us to pinpoint what happened (before/after) + run: | + if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+[a-b].*$ ]]; then + echo "Version $VERSION doesn't look like a pre-release version; aborting" + exit 1 + fi + env: + RELEASE_VERSION: ${{ steps.release_version.outputs.RELEASE_VERSION}} + + - name: Seal and upload + id: seal_source_code + uses: ./.github/actions/seal + with: + artifact_name_prefix: "source" + + # This job runs our automated test suite, complexity and security baselines + # it ensures previously merged have been tested as part of the pull request process + # + # NOTE + # + # we don't upload the artifact after testing to prevent any tampering of our source code dependencies + quality_check: + needs: seal + runs-on: ubuntu-latest + permissions: + contents: read + steps: + # NOTE: we need actions/checkout to configure git first (pre-commit hooks in make dev) + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + + - name: Debug cache restore + run: cat pyproject.toml + + - name: Install poetry + run: pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + cache: "poetry" + - name: Install dependencies + run: make dev + - name: Run all tests, linting and baselines + run: make pr + + # This job creates a release artifact (tar.gz, wheel) + # it checks out code from release commit for custom actions to work + # then restores the sealed source code (overwrites any potential tampering) + # it's done separately from release job to enforce least privilege. + # We export just the final build artifact for release + build: + runs-on: ubuntu-latest + needs: [quality_check, seal] + permissions: + contents: read + outputs: + integrity_hash: ${{ steps.seal_build.outputs.integrity_hash }} + artifact_name: ${{ steps.seal_build.outputs.artifact_name }} + attestation_hashes: ${{ steps.encoded_hash.outputs.attestation_hashes }} + steps: + # NOTE: we need actions/checkout to configure git first (pre-commit hooks in make dev) + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + + - name: Install poetry + run: pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + cache: "poetry" + + - name: Build python package and wheel + run: poetry build + + - name: Seal and upload + id: seal_build + uses: ./.github/actions/seal + with: + artifact_name_prefix: "build" + files: "dist/" + + # NOTE: SLSA retraces our build to its artifact to ensure it wasn't tampered + # coupled with GitHub OIDC, SLSA can then confidently sign it came from this release pipeline+commit+branch+org+repo+actor+integrity hash + - name: Create attestation encoded hash for provenance + id: encoded_hash + working-directory: dist + run: echo "attestation_hashes=$(sha256sum ./* | base64 -w0)" >> "$GITHUB_OUTPUT" + + # This job creates a provenance file that describes how our release was built (all steps) + # after it verifies our build is reproducible within the same pipeline + # it confirms that its own software and the CI build haven't been tampered with (Trust but verify) + # lastly, it creates and sign an attestation (multiple.intoto.jsonl) that confirms + # this build artifact came from this GitHub org, branch, actor, commit ID, inputs that triggered this pipeline, and matches its integrity hash + # NOTE: supply chain threats review (we protect against all of them now): https://slsa.dev/spec/v1.0/threats-overview + provenance: + needs: [seal, build] + permissions: + contents: write # nested job explicitly require despite upload assets being set to false + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + # NOTE: provenance fails if we use action pinning... it's a Github limitation + # because SLSA needs to trace & attest it came from a given branch; pinning doesn't expose that information + # https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/generic/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 + with: + base64-subjects: ${{ needs.build.outputs.attestation_hashes }} + upload-assets: false # we upload its attestation in create_tag job, otherwise it creates a new release + + # This job uses release artifact to publish to PyPi + # it exchanges JWT tokens with GitHub to obtain PyPi credentials + # since it's already registered as a Trusted Publisher. + # It uses the sealed build artifact (.whl, .tar.gz) to release it + release: + needs: [build, seal, provenance] + environment: pre-release + runs-on: ubuntu-latest + permissions: + id-token: write # OIDC for PyPi Trusted Publisher feature + env: + RELEASE_VERSION: ${{ needs.seal.outputs.RELEASE_VERSION }} + steps: + # NOTE: we need actions/checkout in order to use our local actions (e.g., ./.github/actions) + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.build.outputs.integrity_hash }} + artifact_name: ${{ needs.build.outputs.artifact_name }} + + - name: Upload to PyPi prod + if: ${{ !inputs.skip_pypi }} + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + + # Creates a PR with the latest version we've just released + # since our trunk is protected against any direct pushes from automation + bump_version: + needs: [release, seal, provenance] + permissions: + contents: write # create-pr action creates a temporary branch + pull-requests: write # create-pr action creates a PR using the temporary branch + runs-on: ubuntu-latest + steps: + # NOTE: we need actions/checkout to authenticate and configure git first + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + + - name: Download provenance + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{needs.provenance.outputs.provenance-name}} + + - name: Update provenance + run: mkdir -p "${PROVENANCE_DIR}" && mv "${PROVENANCE_FILE}" "${PROVENANCE_DIR}/" + env: + PROVENANCE_FILE: ${{ needs.provenance.outputs.provenance-name }} + PROVENANCE_DIR: provenance/${{ needs.seal.outputs.RELEASE_VERSION}} + + - name: Create PR + id: create-pr + uses: ./.github/actions/create-pr + with: + files: "pyproject.toml aws_lambda_powertools/shared/version.py provenance/" + temp_branch_prefix: "ci-bump" + pull_request_title: "chore(ci): new pre-release ${{ needs.seal.outputs.RELEASE_VERSION }}" + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index f1f589ca270..00000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,128 +0,0 @@ -name: Publish to PyPi - -# RELEASE PROCESS -# -# === Manual activities === -# -# 1. Document human readable changes in CHANGELOG (pre-generate unreleased changes with `make changelog`) -# 2. Bump package version using poetry version <major|minor|patch|specific version> -# 3. Merge version changes to develop branch -# 4. Edit the current draft release notes -# 5. If not already set, use `v<new version>` as a tag, and select develop as target branch -# -# === Automated activities === -# -# 1. Extract release notes tag that was published -# 2. Ensure release notes tag match what's in CHANGELOG and pyproject -# 3. Run tests, linting, security and complexity base line -# 4. Publish package to PyPi test repository -# 5. Publish package to PyPi prod repository -# 6. Kick off Lambda Layer pipeline to publish latest version with minimal dependencies as a SAR App -# 7. Kick off Lambda Layer pipeline to publish latest version with extra dependencies as a SAR App -# 8. Builds a fresh version of docs including Changelog updates -# 9. Push latest release source code to master using release title as the commit message -# 10. Builds latest documentation for new release, and update latest alias pointing to the new release tag -# 11. Close and notify all issues labeled "status/staged-next-release" about the release details - -# -# === Fallback mechanism due to external failures === -# -# 1. Trigger "Publish to PyPi" workflow manually: https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow -# 2. Use the version released under Releases e.g. v1.13.0 -# - -# -# === Documentation hotfix === -# -# Look for rebuild latest docs workflow - - -on: - release: - types: [published] - -jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v2.3.1 - with: - python-version: "3.8" - - name: Set release notes tag - run: | - RELEASE_TAG_VERSION=${{ github.event.release.tag_name }} - echo "RELEASE_TAG_VERSION=${RELEASE_TAG_VERSION:1}" >> $GITHUB_ENV - - name: Ensure new version is also set in pyproject and CHANGELOG - run: | - grep --regexp "${RELEASE_TAG_VERSION}" CHANGELOG.md - grep --regexp "version \= \"${RELEASE_TAG_VERSION}\"" pyproject.toml - - name: Install dependencies - run: make dev - - name: Run all tests, linting and baselines - run: make pr - - name: Build python package and wheel - run: poetry build - - name: Upload to PyPi test - run: make release-test - env: - PYPI_USERNAME: __token__ - PYPI_TEST_TOKEN: ${{ secrets.PYPI_TEST_TOKEN }} - - name: Upload to PyPi prod - run: make release-prod - env: - PYPI_USERNAME: __token__ - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - - name: publish lambda layer in SAR by triggering the internal codepipeline - run: | - aws ssm put-parameter --name "powertools-python-release-version" --value $RELEASE_TAG_VERSION --overwrite - aws codepipeline start-pipeline-execution --name ${{ secrets.CODEPIPELINE_NAME }} - env: - # Maintenance: Migrate to new OAuth mechanism - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: eu-west-1 - AWS_DEFAULT_OUTPUT: json - - name: Setup doc deploy - run: | - git config --global user.name Docs deploy - git config --global user.email aws-devax-open-source@amazon.com - - name: Build docs website and API reference - run: | - make release-docs VERSION=${RELEASE_TAG_VERSION} ALIAS="latest" - poetry run mike set-default --push latest - - name: Release API docs to release version - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./api - keep_files: true - destination_dir: ${{ env.RELEASE_TAG_VERSION }}/api - - name: Release API docs to latest - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./api - keep_files: true - destination_dir: latest/api - - name: Close issues related to this release - uses: actions/github-script@v5 - with: - script: | - const post_release = require('.github/workflows/post_release.js') - await post_release({github, context, core}) - - sync_master: - needs: release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Sync master from detached head - # If version matches CHANGELOG and pyproject.toml - # If it passes all checks, successfully releases to test and prod - # Then sync up master with latest source code release - # where commit message will be Release notes title - run: git push origin HEAD:refs/heads/master --force diff --git a/.github/workflows/publish_v3_layer.yml b/.github/workflows/publish_v3_layer.yml new file mode 100644 index 00000000000..6ea77ebb41b --- /dev/null +++ b/.github/workflows/publish_v3_layer.yml @@ -0,0 +1,324 @@ +name: Deploy v3 layer to all regions + +# PROCESS +# +# 1. Compile Layer using cdk-aws-lambda-powertools-layer CDK construct for Python3.9-3.13 and x86_64/ARM architectures (uses custom runner as it's CPU heavy) +# 2. Kick off pipeline for beta, prod, and canary releases +# 3. Create PR to update trunk so staged docs also point to the latest Layer ARN, when merged +# 4. Builds and publishes docs with latest Layer ARN using given version (generally coming from release) + +# USAGE +# +# NOTE: meant to be used with ./.github/workflows/release-v3.yml +# +# publish_layer: +# needs: [seal, release, create_tag] +# secrets: inherit +# permissions: +# id-token: write +# contents: write +# pages: write +# pull-requests: write +# uses: ./.github/workflows/publish_v2_layer.yml +# with: +# latest_published_version: ${{ needs.seal.outputs.RELEASE_VERSION }} +# pre_release: ${{ inputs.pre_release }} +# source_code_artifact_name: ${{ needs.seal.outputs.artifact_name }} +# source_code_integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + + +on: + workflow_dispatch: + inputs: + latest_published_version: + description: "Latest PyPi published version to rebuild latest docs for, e.g. 3.0.0, 3.0.0a1 (pre-release)" + required: true + layer_documentation_version: + description: "Version to be updated in our documentation. e.g. if the current layer number is 3, this value must be 4." + required: true + source_code_artifact_name: + description: "Artifact name to restore sealed source code" + type: string + required: true + source_code_integrity_hash: + description: "Sealed source code integrity hash" + type: string + required: true + pre_release: + description: "Publishes documentation using a pre-release tag (3.0.0a1)." + default: false + type: boolean + required: false + skip_lambda_layer: + description: "Skip publishing Lambda Layers as it can publish duplicated versions of the same layer. Useful for semi-failed releases" + type: boolean + required: false + + workflow_call: + inputs: + latest_published_version: + type: string + description: "Latest PyPi published version to rebuild latest docs for, e.g. 3.0.0, 3.0.0a1 (pre-release)" + required: true + layer_documentation_version: + type: string + description: "Version to be updated in our documentation. e.g. if the current layer number is 3, this value must be 4." + required: true + pre_release: + description: "Publishes documentation using a pre-release tag (3.0.0a1)." + default: false + type: boolean + required: false + source_code_artifact_name: + description: "Artifact name to restore sealed source code" + type: string + required: true + source_code_integrity_hash: + description: "Sealed source code integrity hash" + type: string + required: true + skip_lambda_layer: + description: "Skip publishing Lambda Layers as it can publish duplicated versions of the same layer. Useful for semi-failed releases" + default: false + type: boolean + required: false + +permissions: + contents: read + + +env: + RELEASE_COMMIT: ${{ github.sha }} + +jobs: + build-layer: + permissions: + # lower privilege propagated from parent workflow (release-v3.yml) + contents: read + id-token: write + pages: none + pull-requests: none + runs-on: aws-powertools_ubuntu-latest_8-core + strategy: + max-parallel: 5 + matrix: + python-version: ["3.9","3.10","3.11","3.12","3.13"] + defaults: + run: + working-directory: ./layer_v3 + steps: + - name: checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ inputs.source_code_integrity_hash }} + artifact_name: ${{ inputs.source_code_artifact_name }} + + - name: Install poetry + run: | + pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + pipx inject poetry git+https://github.com/python-poetry/poetry-plugin-export@8c83d26603ca94f2e203bfded7b6d7f530960e06 # v1.8.0 + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "18.20.4" + - name: Setup python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + - name: Resolve and install project dependencies + # CDK spawns system python when compiling stack + # therefore it ignores both activated virtual env and cached interpreter by GH + run: | + poetry export --format requirements.txt --output requirements.txt + pip install --require-hashes -r requirements.txt + + - name: Set up QEMU + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v2.0.0 + with: + platforms: arm64 + # NOTE: we need QEMU to build Layer against a different architecture (e.g., ARM) + + - name: Set up Docker Buildx + id: builder + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + with: + install: true + driver: docker + platforms: linux/amd64,linux/arm64 + + - name: Install CDK + working-directory: ./ + run: | + npm ci + npx cdk --version + + # Baking time for PyPi eventual consistency; 60s seemed more than enough + # https://github.com/aws-powertools/powertools-lambda-python/issues/2491 + - name: Baking time (PyPi) + run: sleep 60 + + - name: CDK build + run: npx cdk synth --verbose --context version="${{ inputs.latest_published_version }}" --context pythonVersion="python${{ matrix.python-version }}" -o cdk.out + - name: zip output + run: zip -r cdk.py${{ matrix.python-version }}.out.zip cdk.out + - name: Archive CDK artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: cdk-layer-artifact-py${{ matrix.python-version }} + path: layer_v3/cdk.py${{ matrix.python-version }}.out.zip + + beta: + needs: build-layer + # lower privilege propagated from parent workflow (release-v3.yml) + permissions: + id-token: write + contents: read + pages: write # docs will be updated with latest Layer ARNs + pull-requests: write # creation-action will create a PR with Layer ARN updates + uses: ./.github/workflows/reusable_deploy_v3_layer_stack.yml + secrets: inherit + with: + stage: "BETA" + environment: "layer-beta" + source_code_artifact_name: ${{ inputs.source_code_artifact_name }} + source_code_integrity_hash: ${{ inputs.source_code_integrity_hash }} + + prod: + if: ${{ !inputs.skip_lambda_layer }} + needs: beta + # lower privilege propagated from parent workflow (release-v3.yml) + permissions: + id-token: write + contents: read + pages: write # docs will be updated with latest Layer ARNs + pull-requests: write # creation-action will create a PR with Layer ARN updates + uses: ./.github/workflows/reusable_deploy_v3_layer_stack.yml + secrets: inherit + with: + stage: "PROD" + environment: "layer-prod" + source_code_artifact_name: ${{ inputs.source_code_artifact_name }} + source_code_integrity_hash: ${{ inputs.source_code_integrity_hash }} + + sar-beta: + needs: beta # canaries run on Layer Beta env + permissions: + # lower privilege propagated from parent workflow (release.yml) + id-token: write + contents: read + pull-requests: none + pages: none + uses: ./.github/workflows/reusable_deploy_v3_sar.yml + secrets: inherit + with: + stage: "BETA" + environment: "layer-beta" + package-version: ${{ inputs.latest_published_version }} + source_code_artifact_name: ${{ inputs.source_code_artifact_name }} + source_code_integrity_hash: ${{ inputs.source_code_integrity_hash }} + + sar-prod: + needs: sar-beta + permissions: + # lower privilege propagated from parent workflow (release.yml) + id-token: write + contents: read + pull-requests: none + pages: none + uses: ./.github/workflows/reusable_deploy_v3_sar.yml + secrets: inherit + with: + stage: "PROD" + environment: "layer-prod" + package-version: ${{ inputs.latest_published_version }} + source_code_artifact_name: ${{ inputs.source_code_artifact_name }} + source_code_integrity_hash: ${{ inputs.source_code_integrity_hash }} + + + # Updating the documentation with the latest Layer ARNs is a two-phase process + # + # 1. Update layer ARNs with latest deployed locally and create a PR with these changes + # 2. Pull from temporary branch with these changes and update the docs we're releasing + # + # This keeps our permissions tight and we don't run into a conflict, + # where a new release creates a new doc (2.16.0) while layers are still pointing to 2.15 + # because the PR has to be merged while release process is running + + update_v3_layer_arn_docs: + needs: prod + outputs: + temp_branch: ${{ steps.create-pr.outputs.temp_branch }} + runs-on: ubuntu-latest + permissions: + # lower privilege propagated from parent workflow (release.yml) + contents: write + pull-requests: write + id-token: none + pages: none + steps: + - name: Checkout repository # reusable workflows start clean, so we need to checkout again + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ inputs.source_code_integrity_hash }} + artifact_name: ${{ inputs.source_code_artifact_name }} + + - name: Replace layer versions in documentation + run: ./layer_v3/scripts/update_layer_arn_v3.sh ${{ inputs.layer_documentation_version }} + # NOTE: It felt unnecessary creating yet another PR to update changelog w/ latest tag + # since this is the only step in the release where we update docs from a temp branch + - name: Update changelog with latest tag + run: make changelog + - name: Create PR + id: create-pr + uses: ./.github/actions/create-pr + with: + files: "docs/index.md docs/includes/_layer_homepage_arm64.md docs/includes/_layer_homepage_x86.md examples CHANGELOG.md" + temp_branch_prefix: "ci-layer-docs" + pull_request_title: "chore(ci): layer docs update" + github_token: ${{ secrets.GITHUB_TOKEN }} + + prepare_docs_alias: + runs-on: ubuntu-latest + permissions: + # lower privilege propagated from parent workflow (release.yml) + contents: read + pages: none + id-token: none + pull-requests: none + outputs: + DOCS_ALIAS: ${{ steps.set-alias.outputs.DOCS_ALIAS }} + steps: + - name: Set docs alias + id: set-alias + run: | + DOCS_ALIAS=latest + if [[ "${{ inputs.pre_release }}" == true ]] ; then + DOCS_ALIAS=alpha + fi + echo DOCS_ALIAS="$DOCS_ALIAS" >> "$GITHUB_OUTPUT" + + release_docs: + needs: [update_v3_layer_arn_docs, prepare_docs_alias] + permissions: + # lower privilege propagated from parent workflow (release.yml) + contents: write + pages: write + pull-requests: none + id-token: write + secrets: inherit + uses: ./.github/workflows/reusable_publish_docs.yml + with: + version: ${{ inputs.latest_published_version }} + alias: ${{ needs.prepare_docs_alias.outputs.DOCS_ALIAS }} + git_ref: ${{ needs.update_v3_layer_arn_docs.outputs.temp_branch }} diff --git a/.github/workflows/python_build.yml b/.github/workflows/python_build.yml deleted file mode 100644 index 17aa08ead81..00000000000 --- a/.github/workflows/python_build.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Code quality - -on: - pull_request: - branches: - - develop - - master - push: - branches: - - develop - - master - -jobs: - build: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [3.6, 3.7, 3.8, 3.9] - env: - OS: ${{ matrix.os }} - PYTHON: ${{ matrix.python-version }} - steps: - - uses: actions/checkout@v1 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: make dev - - name: Formatting and Linting - run: make lint - - name: Test with pytest - run: make test - - name: Security baseline - run: make security-baseline - - name: Complexity baseline - run: make complexity-baseline - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2.1.0 - with: - file: ./coverage.xml - # flags: unittests - env_vars: OS,PYTHON - name: aws-lambda-powertools-python-codecov - # fail_ci_if_error: true # failing more consistently making CI unreliable despite all tests above passing - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/python_docs.yml b/.github/workflows/python_docs.yml deleted file mode 100644 index bb1426534fb..00000000000 --- a/.github/workflows/python_docs.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Docs - -on: - push: - branches: - - develop - paths: - - 'docs/**' - - 'CHANGELOG.md' - - 'mkdocs.yml' - -jobs: - docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v2.3.1 - with: - python-version: "3.8" - - name: Install dependencies - run: make dev - - name: Setup doc deploy - run: | - git config --global user.name Docs deploy - git config --global user.email docs@dummy.bot.com - - name: Build docs website and API reference - run: make release-docs VERSION="develop" ALIAS="stage" - - name: Deploy all docs - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./api - keep_files: true - destination_dir: develop/api - - name: Create redirect from old docs - run: | - git checkout gh-pages - test -f 404.html && echo "Redirect already set" && exit 0 - git checkout develop -- 404.html - git add 404.html - git commit -m "chore: set docs redirect" --no-verify - git push origin gh-pages -f diff --git a/.github/workflows/quality_check.yml b/.github/workflows/quality_check.yml new file mode 100644 index 00000000000..0cfeac0e6e5 --- /dev/null +++ b/.github/workflows/quality_check.yml @@ -0,0 +1,84 @@ +name: Code quality + +# PROCESS +# +# 1. Install all dependencies and spin off containers for all supported Python versions +# 2. Run code formatters and linters (various checks) for code standard +# 3. Run static typing checker for potential bugs +# 4. Run entire test suite for regressions except end-to-end (unit, functional, performance) +# 5. Run static analysis (in addition to CodeQL) for common insecure code practices +# 6. Run complexity baseline to avoid error-prone bugs and keep maintenance lower +# 7. Collect and report on test coverage + +# USAGE +# +# Always triggered on new PRs, PR changes and PR merge. + + +on: + pull_request: + paths: + - "aws_lambda_powertools/**" + - "tests/**" + - "examples/**" + - "pyproject.toml" + - "poetry.lock" + - "mypy.ini" + branches: + - develop + push: + paths: + - "aws_lambda_powertools/**" + - "tests/**" + - "examples/**" + - "pyproject.toml" + - "poetry.lock" + - "mypy.ini" + branches: + - develop + +permissions: + contents: read + +jobs: + quality_check: + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + matrix: + python-version: ["3.9","3.10","3.11","3.12","3.13"] + env: + PYTHON: "${{ matrix.python-version }}" + permissions: + contents: read # checkout code only + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Install poetry + run: pipx install poetry + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: make dev-quality-code + - name: Checking and enforcing format + run: make format-check + - name: Formatting and Linting + run: make lint + - name: Static type checking + run: make mypy + - name: Test with pytest + run: make test + - name: Test dependencies with Nox + run: make test-dependencies + - name: Security baseline + run: make security-baseline + - name: Complexity baseline + run: make complexity-baseline + - name: Upload coverage to Codecov + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # 5.4.3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.xml + env_vars: PYTHON + name: aws-lambda-powertools-python-codecov diff --git a/.github/workflows/quality_code_cdk_constructor.yml b/.github/workflows/quality_code_cdk_constructor.yml new file mode 100644 index 00000000000..d65ba0dcceb --- /dev/null +++ b/.github/workflows/quality_code_cdk_constructor.yml @@ -0,0 +1,70 @@ +name: Code quality - CDK constructor + +# PROCESS +# +# 1. Install all dependencies and spin off containers for all supported Python versions +# 2. Run code formatters and linters (various checks) for code standard +# 3. Run static typing checker for potential bugs +# 4. Run tests + +# USAGE +# +# Always triggered on new PRs, PR changes and PR merge. + + +on: + pull_request: + paths: + - "layer_v3/layer_constructors/**" + branches: + - develop + push: + paths: + - "layer_v3/layer_constructors/**" + branches: + - develop + +permissions: + contents: read + +jobs: + quality_check_cdk: + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python-version: ["3.12"] + env: + PYTHON: "${{ matrix.python-version }}" + permissions: + contents: read # checkout code only + defaults: + run: + working-directory: ./layer_v3/layer_constructors + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Install poetry + run: pipx install poetry + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ matrix.python-version }} + cache: "poetry" + - name: Set up QEMU + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v2.0.0 + with: + platforms: arm64 + # NOTE: we need QEMU to build Layer against a different architecture (e.g., ARM) + - name: Set up Docker Buildx + id: builder + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + with: + install: true + driver: docker + platforms: linux/amd64,linux/arm64 + - name: Install dependencies + run: | + pip install --upgrade pip pre-commit poetry + poetry install + - name: Test with pytest + run: poetry run pytest tests diff --git a/.github/workflows/rebuild_latest_docs.yml b/.github/workflows/rebuild_latest_docs.yml index 606d1c55696..665cad81dd1 100644 --- a/.github/workflows/rebuild_latest_docs.yml +++ b/.github/workflows/rebuild_latest_docs.yml @@ -1,62 +1,39 @@ name: Rebuild latest docs +# PROCESS # -# === Documentation hotfix === +# 1. Build User Guide and API docs +# 2. Publish to GitHub Pages +# 3. Publish to S3 (new home) + +# USAGE +# +# Only used for deploying a documentation hotfix to /latest and its associated version w/o a full release. +# +# Steps: # # 1. Trigger "Rebuild latest docs" workflow manually: https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow -# 2. Use the latest version released under Releases e.g. v1.22.0 -# 3. Set `Build and publish docs only` field to `true` - +# 2. Use the latest version released under Releases e.g. 2.0.0 on: workflow_dispatch: inputs: latest_published_version: - description: 'Latest PyPi published version to rebuild latest docs for, e.g. v1.22.0' - default: 'v1.22.0' + description: "Latest PyPi published version to rebuild latest docs for, e.g. 2.16.3" + default: "2.16.3" required: true +permissions: + contents: read jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v2.3.1 - with: - python-version: "3.8" - - name: Set release notes tag - run: | - RELEASE_TAG_VERSION=${{ github.event.inputs.latest_published_version }} - echo "RELEASE_TAG_VERSION=${RELEASE_TAG_VERSION:1}" >> $GITHUB_ENV - - name: Ensure new version is also set in pyproject and CHANGELOG - run: | - grep --regexp "${RELEASE_TAG_VERSION}" CHANGELOG.md - grep --regexp "version \= \"${RELEASE_TAG_VERSION}\"" pyproject.toml - - name: Install dependencies - run: make dev - - name: Setup doc deploy - run: | - git config --global user.name Docs deploy - git config --global user.email aws-devax-open-source@amazon.com - - name: Build docs website and API reference - run: | - make release-docs VERSION=${RELEASE_TAG_VERSION} ALIAS="latest" - poetry run mike set-default --push latest - - name: Release API docs to release version - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./api - keep_files: true - destination_dir: ${{ env.RELEASE_TAG_VERSION }}/api - - name: Release API docs to latest - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./api - keep_files: true - destination_dir: latest/api + release-docs: + permissions: + contents: write # push to gh-pages + pages: write # deploy gh-pages website + id-token: write # trade JWT token for AWS credentials in AWS Docs account + secrets: inherit + uses: ./.github/workflows/reusable_publish_docs.yml + with: + version: ${{ inputs.latest_published_version }} + alias: latest diff --git a/.github/workflows/record_pr.yml b/.github/workflows/record_pr.yml index 7ec726443cf..c43b4a403c2 100644 --- a/.github/workflows/record_pr.yml +++ b/.github/workflows/record_pr.yml @@ -1,21 +1,60 @@ -name: Record PR number +name: Record PR details + +# PROCESS +# +# 1. Runs in fork location upon PR creation or changes +# 2. Saves GitHub Pull Request Webhook payload +# 3. Uploads as a temporary GitHub Action Artifact with shortest retention + +# USAGE +# +# see .github/workflows/on_merged_pr.yml and related for full example. +# +# on: +# workflow_run: +# workflows: ["Record PR details"] +# types: +# - completed +# +# Security Note: +# +# For security, this is intended to be a 2-step process: (1) collect PR, (2) act on PR. +# Do not ever use `pull_request_target` to "simplify", as it sends a write-token to the fork. Our linter should catch it. +# +# The first step runs in untrusted location (fork), therefore we limit permissions to only check out code. +# +# The second step will be workflows that want to act on a given PR, this time with intended permissions, and +# it runs on its base location (this repo!). +# +# This enforces zero trust where this workflow always runs on fork with zero permissions on GH_TOKEN. +# When this workflow completes, X workflows run in our repository with the appropriate permissions and sanitize inputs. +# +# Coupled with "Approve GitHub Action to run on forks", we have confidence no privilege can be escalated, +# since any malicious change would need to be approved, and upon social engineering, it'll have zero permissions. + on: pull_request: - types: [opened, edited] + types: [opened, edited, closed, labeled] + +permissions: + contents: read jobs: - build: + record_pr: runs-on: ubuntu-latest - + permissions: + contents: read # NOTE: treat as untrusted location steps: - - uses: actions/checkout@v2 - - name: Save PR number - run: | - mkdir -p ./pr - echo ${{ github.event.number }} > ./pr/number - echo "${{ github.event.pull_request.title }}" > ./pr/title - - uses: actions/upload-artifact@v2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: "Extract PR details" + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const script = require('.github/scripts/save_pr_details.js') + await script({github, context, core}) + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: pr - path: pr/ + path: pr.txt + retention-days: 1 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 59633a634b6..53698e4fb3e 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -1,15 +1,32 @@ name: Release Drafter +# PROCESS +# +# 1. Enumerate all PRs in merged state +# 2. Filter out any PR labeled `skip-changelog` +# 3. Updates or creates a new release in Draft mode + +# USAGE +# +# Always run on merged PRs or manually via GitHub UI for debugging purposes. +# +# see .github/release-drafter.yml for configuration + on: push: - # branches to consider in the event; optional, defaults to all branches: - develop + workflow_dispatch: + +permissions: + contents: read jobs: update_release_draft: runs-on: ubuntu-latest + permissions: + contents: write # create release in draft mode steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v5.20.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-v3.yml b/.github/workflows/release-v3.yml new file mode 100644 index 00000000000..f4085454d5f --- /dev/null +++ b/.github/workflows/release-v3.yml @@ -0,0 +1,398 @@ +name: Release V3 + +# RELEASE PROCESS +# +# === Automated activities === +# +# 1. [Seal] Bump to release version and export source code with integrity hash +# 2. [Quality check] Restore sealed source code, run tests, linting, security and complexity base line +# 3. [Build] Restore sealed source code, create and export hashed build artifact for PyPi release (wheel, tarball) +# 4. [Provenance] Generates provenance for build, signs attestation with GitHub OIDC claims to confirm it came from this release pipeline, commit, org, repo, branch, hash, etc. +# 5. [Release] Restore built artifact, and publish package to PyPi prod repository +# 6. [Create Tag] Restore sealed source code, create a new git tag using released version, uploads provenance to latest draft release +# 7. [PR to bump version] Restore sealed source code, and create a PR to update trunk with latest released project metadata +# 8. [Publish Layer v3] Compile Layer in multiple Python versions and kick off pipeline for beta, prod, and canary releases +# 9. [Publish Layer v3] Update docs with latest Layer ARNs and Changelog +# 10. [Publish Layer v3] Create PR to update trunk so staged docs also point to the latest Layer ARN, when merged +# 12. [Post release] Close all issues labeled "pending-release" and notify customers about the release +# +# === Manual activities === +# +# 1. Kick off this workflow with the intended version +# 2. Update draft release notes after this workflow completes +# 3. If not already set, use `v<new version>` as a tag, e.g., v3.0.0, and select develop as target branch + +# NOTE +# +# See MAINTAINERS.md "Releasing a new version" for release mechanisms +# +# Every job is isolated and starts a new fresh container. + +env: + RELEASE_COMMIT: ${{ github.sha }} + RELEASE_TAG_VERSION: ${{ inputs.version_to_publish }} + +on: + workflow_dispatch: + inputs: + version_to_publish: + description: "Version to be released in PyPi, Docs, and Lambda Layer, e.g. v3.0.0, v3.0.0a0 (pre-release)" + default: v3.0.0 + required: true + layer_documentation_version: + description: "Lambda layer version to be updated in our documentation. e.g. if the current layer number is 3, this value must be 4." + type: string + required: true + skip_pypi: + description: "Skip publishing to PyPi as it can't publish more than once. Useful for semi-failed releases" + default: false + type: boolean + required: false + skip_lambda_layer: + description: "Skip publishing Lambda Layers as it can publish duplicated versions of the same layer. Useful for semi-failed releases" + default: false + type: boolean + required: false + skip_code_quality: + description: "Skip tests, linting, and baseline. Only use if release fail for reasons beyond our control and you need a quick release." + default: false + type: boolean + required: false + pre_release: + description: "Publishes documentation using a pre-release tag (v3.0.0a0). You are still responsible for passing a pre-release version tag to the workflow." + default: false + type: boolean + required: false + +permissions: + contents: read + +jobs: + + # This job bumps the package version to the release version + # creates an integrity hash from the source code + # uploads the artifact with the integrity hash as the key name + # so subsequent jobs can restore from a trusted point in time to prevent tampering + seal: + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + integrity_hash: ${{ steps.seal_source_code.outputs.integrity_hash }} + artifact_name: ${{ steps.seal_source_code.outputs.artifact_name }} + RELEASE_VERSION: ${{ steps.release_version.outputs.RELEASE_VERSION }} + steps: + - name: Export release version + id: release_version + # transform tag format `v<version` to `<version>` + run: | + RELEASE_VERSION="${RELEASE_TAG_VERSION:1}" + echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + # We use a pinned version of Poetry to be certain it won't modify source code before we create a hash + - name: Install poetry + run: | + pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + pipx inject poetry git+https://github.com/monim67/poetry-bumpversion@348de6f247222e2953d649932426e63492e0a6bf # v0.3.3 + + - name: Bump package version + id: versioning + run: poetry version "${RELEASE_VERSION}" + env: + RELEASE_VERSION: ${{ steps.release_version.outputs.RELEASE_VERSION}} + + - name: Seal and upload + id: seal_source_code + uses: ./.github/actions/seal + with: + artifact_name_prefix: "source" + + # This job runs our automated test suite, complexity and security baselines + # it ensures previously merged have been tested as part of the pull request process + # + # NOTE + # + # we don't upload the artifact after testing to prevent any tampering of our source code dependencies + quality_check: + needs: seal + runs-on: ubuntu-latest + permissions: + contents: read + steps: + # NOTE: we need actions/checkout to configure git first (pre-commit hooks in make dev) + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + + - name: Debug cache restore + run: cat pyproject.toml + + - name: Install poetry + run: pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + cache: "poetry" + - name: Install dependencies + run: make dev + - name: Run all tests, linting and baselines + run: make pr + + # This job creates a release artifact (tar.gz, wheel) + # it checks out code from release commit for custom actions to work + # then restores the sealed source code (overwrites any potential tampering) + # it's done separately from release job to enforce least privilege. + # We export just the final build artifact for release + build: + runs-on: ubuntu-latest + needs: [quality_check, seal] + permissions: + contents: read + outputs: + integrity_hash: ${{ steps.seal_build.outputs.integrity_hash }} + artifact_name: ${{ steps.seal_build.outputs.artifact_name }} + attestation_hashes: ${{ steps.encoded_hash.outputs.attestation_hashes }} + steps: + # NOTE: we need actions/checkout to configure git first (pre-commit hooks in make dev) + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + + - name: Install poetry + run: pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + cache: "poetry" + + - name: Build python package and wheel + run: poetry build + + - name: Seal and upload + id: seal_build + uses: ./.github/actions/seal + with: + artifact_name_prefix: "build" + files: "dist/" + + # NOTE: SLSA retraces our build to its artifact to ensure it wasn't tampered + # coupled with GitHub OIDC, SLSA can then confidently sign it came from this release pipeline+commit+branch+org+repo+actor+integrity hash + - name: Create attestation encoded hash for provenance + id: encoded_hash + working-directory: dist + run: echo "attestation_hashes=$(sha256sum ./* | base64 -w0)" >> "$GITHUB_OUTPUT" + + # This job creates a provenance file that describes how our release was built (all steps) + # after it verifies our build is reproducible within the same pipeline + # it confirms that its own software and the CI build haven't been tampered with (Trust but verify) + # lastly, it creates and sign an attestation (multiple.intoto.jsonl) that confirms + # this build artifact came from this GitHub org, branch, actor, commit ID, inputs that triggered this pipeline, and matches its integrity hash + # NOTE: supply chain threats review (we protect against all of them now): https://slsa.dev/spec/v1.0/threats-overview + provenance: + needs: [seal, build] + permissions: + contents: write # nested job explicitly require despite upload assets being set to false + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + # NOTE: provenance fails if we use action pinning... it's a Github limitation + # because SLSA needs to trace & attest it came from a given branch; pinning doesn't expose that information + # https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/generic/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 + with: + base64-subjects: ${{ needs.build.outputs.attestation_hashes }} + upload-assets: false # we upload its attestation in create_tag job, otherwise it creates a new release + + # This job uses release artifact to publish to PyPi + # it exchanges JWT tokens with GitHub to obtain PyPi credentials + # since it's already registered as a Trusted Publisher. + # It uses the sealed build artifact (.whl, .tar.gz) to release it + release: + needs: [build, seal, provenance] + environment: release + runs-on: ubuntu-latest + permissions: + id-token: write # OIDC for PyPi Trusted Publisher feature + env: + RELEASE_VERSION: ${{ needs.seal.outputs.RELEASE_VERSION }} + steps: + # NOTE: we need actions/checkout in order to use our local actions (e.g., ./.github/actions) + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.build.outputs.integrity_hash }} + artifact_name: ${{ needs.build.outputs.artifact_name }} + + - name: Upload to PyPi prod + if: ${{ !inputs.skip_pypi }} + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + + # PyPi test maintenance affected us numerous times, leaving for history purposes + # - name: Upload to PyPi test + # if: ${{ !inputs.skip_pypi }} + # uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + # with: + # repository-url: https://test.pypi.org/legacy/ + + # We create a Git Tag using our release version (e.g., v3.16.0) + # using our sealed source code we created earlier. + # Because we bumped version of our project as part of CI + # we need to add this into git before pushing the tag + # otherwise the release commit will be used as the basis for the tag. + # Later, we create a PR to update trunk with our newest release version (e.g., bump_version job) + create_tag: + needs: [release, seal, provenance] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + # NOTE: we need actions/checkout to authenticate and configure git first + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + + - id: setup-git + name: Git client setup and refresh tip + run: | + git config user.name "Powertools for AWS Lambda (Python) bot" + git config user.email "151832416+aws-powertools-bot@users.noreply.github.com" + git config remote.origin.url >&- + + - name: Create Git Tag + run: | + git add pyproject.toml aws_lambda_powertools/shared/version.py + git commit -m "chore: version bump" + git tag -a v"${RELEASE_VERSION}" -m "release_version: v${RELEASE_VERSION}" + git push origin v"${RELEASE_VERSION}" + env: + RELEASE_VERSION: ${{ needs.seal.outputs.RELEASE_VERSION }} + + - name: Upload provenance + id: upload-provenance + uses: ./.github/actions/upload-release-provenance + with: + release_version: ${{ needs.seal.outputs.RELEASE_VERSION }} + provenance_name: ${{needs.provenance.outputs.provenance-name}} + github_token: ${{ secrets.GITHUB_TOKEN }} + + # Creates a PR with the latest version we've just released + # since our trunk is protected against any direct pushes from automation + bump_version: + needs: [release, seal] + permissions: + contents: write # create-pr action creates a temporary branch + pull-requests: write # create-pr action creates a PR using the temporary branch + runs-on: ubuntu-latest + steps: + # NOTE: we need actions/checkout to authenticate and configure git first + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + + - name: Create PR + id: create-pr + uses: ./.github/actions/create-pr + with: + files: "pyproject.toml aws_lambda_powertools/shared/version.py" + temp_branch_prefix: "ci-bump" + pull_request_title: "chore(ci): bump version to ${{ needs.seal.outputs.RELEASE_VERSION }}" + github_token: ${{ secrets.GITHUB_TOKEN }} + + # This job compiles a Lambda Layer optimized for space and speed (e.g., Cython) + # It then deploys to Layer's Beta and Prod account, including SAR Beta and Prod account. + # It uses canaries to attest Layers can be used and imported between stages. + # Lastly, it updates our documentation with the latest Layer ARN for all regions + # + # NOTE + # + # Watch out for the depth limit of 4 nested workflow_calls. + # publish_layer -> publish_3_layer -> reusable_deploy_v3_layer_stack + publish_layer: + needs: [seal, release, create_tag] + secrets: inherit + permissions: + id-token: write + contents: write + pages: write + pull-requests: write + uses: ./.github/workflows/publish_v3_layer.yml + with: + latest_published_version: ${{ needs.seal.outputs.RELEASE_VERSION }} + layer_documentation_version: ${{ inputs.layer_documentation_version }} + pre_release: ${{ inputs.pre_release }} + source_code_artifact_name: ${{ needs.seal.outputs.artifact_name }} + source_code_integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + skip_lambda_layer: ${{ inputs.skip_lambda_layer }} + + post_release: + needs: [seal, release, publish_layer] + permissions: + contents: read + issues: write + discussions: write + pull-requests: write + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ needs.seal.outputs.RELEASE_VERSION }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ needs.seal.outputs.integrity_hash }} + artifact_name: ${{ needs.seal.outputs.artifact_name }} + - name: Close issues related to this release + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const post_release = require('.github/scripts/post_release.js') + await post_release({github, context, core}) + + update_ssm: + needs: [seal, release, publish_layer] + secrets: inherit + permissions: + id-token: write + contents: read + uses: ./.github/workflows/update_ssm.yml + with: + environment: "Prod" + write_latest: true + package_version: ${{ needs.seal.outputs.RELEASE_VERSION }} + layer_version: ${{ inputs.layer_documentation_version }} diff --git a/.github/workflows/reusable_deploy_v3_layer_stack.yml b/.github/workflows/reusable_deploy_v3_layer_stack.yml new file mode 100644 index 00000000000..0e1984db3af --- /dev/null +++ b/.github/workflows/reusable_deploy_v3_layer_stack.yml @@ -0,0 +1,223 @@ +name: Deploy CDK Layer v3 stack + +# PROCESS +# +# 1. Split what AWS regions support ARM vs regions that Lambda support ARM +# 2. We build the Lambda layer for 3.9 to 3.13 Python runtime and both x86_64 and arm64 (see `matrix` section) +# 3. Deploy previously built layer for each AWS commercial region +# 4. Export all published Layers as JSON +# 5. Deploy Canaries to every deployed region to test whether Powertools can be imported etc. + +# USAGE +# +# NOTE: meant to be used with ./.github/workflows/publish_v3_layer.yml +# +# beta: +# needs: build-layer +# # lower privilege propagated from parent workflow (release.yml) +# permissions: +# id-token: write +# contents: read +# pages: write # docs will be updated with latest Layer ARNs +# pull-requests: write # creation-action will create a PR with Layer ARN updates +# uses: ./.github/workflows/reusable_deploy_v3_layer_stack.yml +# secrets: inherit +# with: +# stage: "BETA" +# environment: "layer-beta" +# source_code_artifact_name: code.zip +# source_code_integrity_hash: sha256string + +on: + workflow_call: + inputs: + stage: + description: "Deployment stage (BETA, PROD)" + required: true + type: string + environment: + description: "GitHub Environment to use for encrypted secrets" + required: true + type: string + source_code_artifact_name: + description: "Artifact name to restore sealed source code" + type: string + required: true + source_code_integrity_hash: + description: "Sealed source code integrity hash" + type: string + required: true + +permissions: + contents: read + +env: + RELEASE_COMMIT: ${{ github.sha }} # it gets propagated from the caller for security reasons + +jobs: + deploy-cdk-stack: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + # lower privilege propagated from parent workflow (publish_v3_layer.yml) + permissions: + id-token: write + pull-requests: none + contents: read + pages: none + defaults: + run: + working-directory: ./layer_v3 + strategy: + fail-fast: false + matrix: + # To get a list of current regions, use: + # aws ec2 describe-regions --all-regions --query "Regions[].RegionName" --output text | tr "\t" "\n" | sort + region: ["af-south-1", "ap-east-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", + "ap-south-1", "ap-south-2", "ap-southeast-1", "ap-southeast-2", "ap-southeast-3", + "ap-southeast-4", "ap-southeast-5", "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", "eu-central-2", + "eu-north-1", "eu-south-1", "eu-south-2", "eu-west-1", "eu-west-2", "eu-west-3", + "il-central-1", "me-central-1", "me-south-1", "mx-central-1", "sa-east-1", "us-east-1", + "us-east-2", "us-west-1", "us-west-2"] + python-version: ["3.9","3.10","3.11","3.12","3.13"] + include: + - region: "af-south-1" + has_arm64_support: "true" + - region: "ap-east-1" + has_arm64_support: "true" + - region: "ap-northeast-1" + has_arm64_support: "true" + - region: "ap-northeast-2" + has_arm64_support: "true" + - region: "ap-northeast-3" + has_arm64_support: "true" + - region: "ap-south-1" + has_arm64_support: "true" + - region: "ap-south-2" + has_arm64_support: "true" + - region: "ap-southeast-1" + has_arm64_support: "true" + - region: "ap-southeast-2" + has_arm64_support: "true" + - region: "ap-southeast-3" + has_arm64_support: "true" + - region: "ap-southeast-4" + has_arm64_support: "true" + - region: "ap-southeast-5" + has_arm64_support: "true" + - region: "ap-southeast-7" + has_arm64_support: "true" + - region: "ca-central-1" + has_arm64_support: "true" + - region: "ca-west-1" + has_arm64_support: "true" + - region: "eu-central-1" + has_arm64_support: "true" + - region: "eu-central-2" + has_arm64_support: "true" + - region: "eu-north-1" + has_arm64_support: "true" + - region: "eu-south-1" + has_arm64_support: "true" + - region: "eu-south-2" + has_arm64_support: "true" + - region: "eu-west-1" + has_arm64_support: "true" + - region: "eu-west-2" + has_arm64_support: "true" + - region: "eu-west-3" + has_arm64_support: "true" + - region: "il-central-1" + has_arm64_support: "true" + - region: "me-central-1" + has_arm64_support: "true" + - region: "me-south-1" + has_arm64_support: "true" + - region: "mx-central-1" + has_arm64_support: "true" + - region: "sa-east-1" + has_arm64_support: "true" + - region: "us-east-1" + has_arm64_support: "true" + - region: "us-east-2" + has_arm64_support: "true" + - region: "us-west-1" + has_arm64_support: "true" + - region: "us-west-2" + has_arm64_support: "true" + steps: + - name: checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ inputs.source_code_integrity_hash }} + artifact_name: ${{ inputs.source_code_artifact_name }} + + - name: Install poetry + run: | + pipx install git+https://github.com/python-poetry/poetry@bd500dd3bdfaec3de6894144c9cedb3a9358be84 # v2.0.1 + pipx inject poetry git+https://github.com/python-poetry/poetry-plugin-export@8c83d26603ca94f2e203bfded7b6d7f530960e06 # v1.8.0 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + aws-region: ${{ matrix.region }} + role-to-assume: ${{ secrets.AWS_LAYERS_ROLE_ARN }} + mask-aws-account-id: true + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "18.20.4" + - name: Setup python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + - name: Resolve and install project dependencies + # CDK spawns system python when compiling stack + # therefore it ignores both activated virtual env and cached interpreter by GH + run: | + poetry export --format requirements.txt --output requirements.txt + pip install --require-hashes -r requirements.txt + - name: install cdk and deps + working-directory: ./ + run: | + npm ci + npx cdk --version + - name: install deps + run: poetry install + - name: Download artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: cdk-layer-artifact-py${{ matrix.python-version }} + path: layer_v3 + - name: unzip artefact + run: unzip cdk.py${{ matrix.python-version }}.out.zip + - name: Define constants + id: constants + run: | + PYTHON_VERSION=$(echo ${{ matrix.python-version }} | tr -d '.') + echo "PYTHON_VERSION=${PYTHON_VERSION}" >> "$GITHUB_OUTPUT" + LAYER_VERSION=${{ matrix.region }}-$PYTHON_VERSION-layer-version.txt + echo "LAYER_VERSION=${LAYER_VERSION}" >> "$GITHUB_OUTPUT" + - name: CDK Deploy Layer + run: npx cdk deploy --app cdk.out --context region=${{ matrix.region }} --parameters HasARM64Support=${{ matrix.has_arm64_support }} "LayerV3Stack-python${{steps.constants.outputs.PYTHON_VERSION}}" --require-approval never --verbose --outputs-file cdk-outputs.json + - name: Store latest Layer ARN + if: ${{ inputs.stage == 'PROD' }} + run: | + mkdir cdk-layer-stack + jq -r -c ".[\"LayerV3Stack-python${{steps.constants.outputs.PYTHON_VERSION}}\"].LatestLayerArn" cdk-outputs.json > cdk-layer-stack/${{steps.constants.outputs.LAYER_VERSION}} + jq -r -c ".[\"LayerV3Stack-python${{steps.constants.outputs.PYTHON_VERSION}}\"].LatestLayerArm64Arn" cdk-outputs.json >> cdk-layer-stack/${{steps.constants.outputs.LAYER_VERSION}} + cat cdk-layer-stack/${{steps.constants.outputs.LAYER_VERSION}} + - name: Save Layer ARN artifact + if: ${{ inputs.stage == 'PROD' }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: cdk-layer-stack-${{ matrix.region }}-${{ matrix.python-version }} + path: ./layer_v3/cdk-layer-stack/* # NOTE: upload-artifact does not inherit working-directory setting. + if-no-files-found: error + retention-days: 1 + - name: CDK Deploy Canary + run: npx cdk deploy --app cdk.out --context region=${{ matrix.region }} --parameters DeployStage="${{ inputs.stage }}" --parameters HasARM64Support=${{ matrix.has_arm64_support }} "CanaryV3Stack-python${{steps.constants.outputs.PYTHON_VERSION}}" --require-approval never --verbose diff --git a/.github/workflows/reusable_deploy_v3_sar.yml b/.github/workflows/reusable_deploy_v3_sar.yml new file mode 100644 index 00000000000..f81bb48a2fa --- /dev/null +++ b/.github/workflows/reusable_deploy_v3_sar.yml @@ -0,0 +1,211 @@ +name: Deploy V3 SAR + +# PROCESS +# +# 1. This workflow starts after the layer artifact is produced on `publish_v3_layer` +# 2. We use the same layer artifact to ensure the SAR app is consistent with the published Lambda Layer +# 3. We publish the SAR for 3.9 to 3.13 Python runtime and both x86_64 and arm64 (see `matrix` section) +# 4. We use `sam package` and `sam publish` to publish the SAR app +# 5. We remove the previous Canary stack (if present) and deploy a new one to test the SAR App. We retain the Canary in the account for debugging purposes +# 6. Finally the published SAR app is made public on the PROD environment + +# USAGE +# +# NOTE: meant to be used with ./.github/workflows/publish_v3_layer.yml +# +# sar-beta: +# needs: build-layer +# permissions: +# # lower privilege propagated from parent workflow (release.yml) +# id-token: write +# contents: read +# pull-requests: none +# pages: none +# uses: ./.github/workflows/reusable_deploy_v3_sar.yml +# secrets: inherit +# with: +# stage: "BETA" +# environment: "layer-beta" +# package-version: ${{ inputs.latest_published_version }} +# source_code_artifact_name: ${{ inputs.source_code_artifact_name }} +# source_code_integrity_hash: ${{ inputs.source_code_integrity_hash }} + +permissions: + id-token: write + contents: read + +env: + NODE_VERSION: 18.20.4 + AWS_REGION: eu-west-1 + SAR_NAME: aws-lambda-powertools-python-layer-v3 + TEST_STACK_NAME: serverlessrepo-v3-powertools-layer-test-stack + RELEASE_COMMIT: ${{ github.sha }} # it gets propagated from the caller for security reasons + +on: + workflow_call: + inputs: + stage: + description: "Deployment stage (BETA, PROD)" + required: true + type: string + package-version: + description: "The version of the package to deploy" + required: true + type: string + environment: + description: "GitHub Environment to use for encrypted secrets" + required: true + type: string + source_code_artifact_name: + description: "Artifact name to restore sealed source code" + type: string + required: true + source_code_integrity_hash: + description: "Sealed source code integrity hash" + type: string + required: true + +jobs: + deploy-sar-app: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + strategy: + matrix: + architecture: ["x86_64", "arm64"] + python-version: ["3.9","3.10","3.11","3.12","3.13"] + steps: + - name: checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + + - name: Restore sealed source code + uses: ./.github/actions/seal-restore + with: + integrity_hash: ${{ inputs.source_code_integrity_hash }} + artifact_name: ${{ inputs.source_code_artifact_name }} + + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + aws-region: ${{ env.AWS_REGION }} + role-to-assume: ${{ secrets.AWS_LAYERS_ROLE_ARN }} + mask-aws-account-id: true + + # NOTE + # We connect to Layers account to log our intent to publish a SAR Layer + # we then jump to our specific SAR Account with the correctly scoped IAM Role + # this allows us to have a single trail when a release occurs for a given layer (beta+prod+SAR beta+SAR prod) + - name: AWS credentials SAR role + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + id: aws-credentials-sar-role + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ env.AWS_SESSION_TOKEN }} + role-duration-seconds: 1200 + aws-region: ${{ env.AWS_REGION }} + role-to-assume: ${{ secrets.AWS_SAR_V3_ROLE_ARN }} + mask-aws-account-id: true + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Download artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: cdk-layer-artifact-py${{ matrix.python-version }} + - name: Unzip artefact + run: unzip cdk.py${{ matrix.python-version }}.out.zip + - name: normalize Python Version + run: | + PYTHON_VERSION=$(echo ${{ matrix.python-version }} | tr -d '.') + echo "PYTHON_VERSION=${PYTHON_VERSION}" >> "$GITHUB_ENV" + - name: Configure SAR name + run: | + if [[ "${{ inputs.stage }}" == "BETA" ]]; then + SAR_NAME="test-${SAR_NAME}" + fi + ARCH_NAME=$(echo ${{ matrix.architecture }} | tr '_' '-') + SAR_NAME="${SAR_NAME}-python${{env.PYTHON_VERSION}}-${ARCH_NAME}" + echo SAR_NAME="${SAR_NAME}" >> "$GITHUB_ENV" + - name: Prepare SAR App + run: | + # From the generated LayerStack cdk.out artifact, find the layer asset path for the correct architecture. + # We'll use this as the source directory of our SAR. This way we are re-using the same layer asset for our SAR. + PYTHON_VERSION=$(echo ${{ matrix.python-version }} | tr -d '.') + asset_cdk=$(jq -jc '.Resources[] | select(.Properties.CompatibleArchitectures == ["${{ matrix.architecture }}"]) | .Metadata."aws:asset:path"' "cdk.out/LayerV3Stack-python${PYTHON_VERSION}.template.json") + + echo "Normalizing the asset variable" + asset=$(echo $asset_cdk | sed -E 's/^(asset\.[^.]+).*\1/\1/') + + VERSION=$(echo ${{ inputs.package-version }} | sed 's/^v//') + echo $asset + echo $VERSION + + # fill in the SAR SAM template + sed \ + -e "s|<VERSION>|${VERSION}|g" \ + -e "s/<SAR_APP_NAME>/${{ env.SAR_NAME }}/g" \ + -e "s|<LAYER_CONTENT_PATH>|./cdk.out/$asset|g" \ + -e "s|<PYTHON_RUNTIME>|${{ matrix.python-version }}|g" \ + -e "s|<ARCHITECTURE>|${{ matrix.architecture }}|g" \ + layer_v3/sar/template.txt > template.yml + + # SAR needs a README and a LICENSE, so just copy the ones from the repo + cp README.md LICENSE "./cdk.out/$asset/" + - name: Deploy SAR + run: | + # Debug purposes + cat template.yml + + # Package the SAR to our SAR S3 bucket, and publish it + sam package --template-file template.yml --output-template-file packaged.yml --s3-bucket ${{ secrets.AWS_SAR_S3_BUCKET_V3 }} + cat packaged.yml + sam publish --template packaged.yml --region "$AWS_REGION" + - name: Deploy BETA canary + if: ${{ inputs.stage == 'BETA' }} + run: | + ARCH_NAME=$(echo ${{ matrix.architecture }} | tr -d '_') + TEST_STACK_NAME="${TEST_STACK_NAME}-python${{env.PYTHON_VERSION}}-${ARCH_NAME}" + + echo "Check if stack does not exist" + stack_exists=$(aws cloudformation list-stacks --query "StackSummaries[?(StackName == '$TEST_STACK_NAME' && StackStatus == 'CREATE_COMPLETE')].{StackId:StackId, StackName:StackName, CreationTime:CreationTime, StackStatus:StackStatus}" --output text) + + if [[ -n "$stack_exists" ]] ; then + echo "Found test deployment stack, removing..." + aws cloudformation delete-stack --stack-name "$TEST_STACK_NAME" + aws cloudformation wait stack-delete-complete --stack-name "$TEST_STACK_NAME" + fi + + echo "Creating canary stack" + echo "Stack name: $TEST_STACK_NAME" + aws serverlessrepo create-cloud-formation-change-set \ + --application-id arn:aws:serverlessrepo:${{ env.AWS_REGION }}:${{ secrets.AWS_SAR_V3_ACCOUNTID }}:applications/${{ env.SAR_NAME }} \ + --stack-name "${TEST_STACK_NAME/serverlessrepo-/}" \ + --capabilities CAPABILITY_NAMED_IAM + + CHANGE_SET_ID=$(aws cloudformation list-change-sets --stack-name "$TEST_STACK_NAME" --query 'Summaries[*].ChangeSetId' --output text) + aws cloudformation wait change-set-create-complete --change-set-name "$CHANGE_SET_ID" + aws cloudformation execute-change-set --change-set-name "$CHANGE_SET_ID" + aws cloudformation wait stack-create-complete --stack-name "$TEST_STACK_NAME" + + echo "Waiting until stack deployment completes..." + + echo "Exit with error if stack is not in CREATE_COMPLETE" + stack_exists=$(aws cloudformation list-stacks --query "StackSummaries[?(StackName == '$TEST_STACK_NAME' && StackStatus == 'CREATE_COMPLETE')].{StackId:StackId, StackName:StackName, CreationTime:CreationTime, StackStatus:StackStatus}") + if [[ -z "$stack_exists" ]] ; then + echo "Could find successful deployment, exit error..." + exit 1 + fi + echo "Deployment successful" + - name: Publish SAR + if: ${{ inputs.stage == 'PROD' }} + run: | + # wait until SAR registers the app, otherwise it fails to make it public + sleep 15 + echo "Make SAR app public" + aws serverlessrepo put-application-policy \ + --application-id arn:aws:serverlessrepo:${{ env.AWS_REGION }}:${{ secrets.AWS_SAR_V3_ACCOUNTID }}:applications/${{ env.SAR_NAME }} \ + --statements Principals='*',Actions=Deploy diff --git a/.github/workflows/reusable_export_pr_details.yml b/.github/workflows/reusable_export_pr_details.yml new file mode 100644 index 00000000000..f4f1bab630e --- /dev/null +++ b/.github/workflows/reusable_export_pr_details.yml @@ -0,0 +1,115 @@ +name: Export previously recorded PR + +# PROCESS +# +# 1. Fetch PR details previously saved from untrusted location +# 2. Parse details for safety +# 3. Export only what's needed for automation, e.g., PR number, title, body, author, action, whether is merged + +# USAGE +# +# see .github/workflows/on_merged_pr.yml and related for full example. +# +# NOTE: meant to be used with workflows that react to a given PR state (labeling, new, merged, etc.) +# done separately to isolate security practices and make it reusable. + + +on: + workflow_call: + inputs: + record_pr_workflow_id: + description: "Record PR workflow execution ID to download PR details" + required: true + type: number + workflow_origin: # see https://github.com/aws-powertools/powertools-lambda-python/issues/1349 + description: "Repository full name for runner integrity" + required: true + type: string + secrets: + token: + description: "GitHub Actions temporary and scoped token" + required: true + # Map the workflow outputs to job outputs + outputs: + prNumber: + description: "PR Number" + value: ${{ jobs.export_pr_details.outputs.prNumber }} + prTitle: + description: "PR Title" + value: ${{ jobs.export_pr_details.outputs.prTitle }} + prBody: + description: "PR Body as string" + value: ${{ jobs.export_pr_details.outputs.prBody }} + prAuthor: + description: "PR author username" + value: ${{ jobs.export_pr_details.outputs.prAuthor }} + prAction: + description: "PR event action" + value: ${{ jobs.export_pr_details.outputs.prAction }} + prIsMerged: + description: "Whether PR is merged" + value: ${{ jobs.export_pr_details.outputs.prIsMerged }} + prLabels: + description: "PR Labels" + value: ${{ jobs.export_pr_details.outputs.prLabels }} + +permissions: + contents: read + +jobs: + export_pr_details: + permissions: + actions: read # download PR artifact + # see https://github.com/aws-powertools/powertools-lambda-python/issues/1349 + if: inputs.workflow_origin == 'aws-powertools/powertools-lambda-python' + runs-on: ubuntu-latest + env: + FILENAME: pr.txt + # Map the job outputs to step outputs + outputs: + prNumber: ${{ steps.prNumber.outputs.prNumber }} + prTitle: ${{ steps.prTitle.outputs.prTitle }} + prBody: ${{ steps.prBody.outputs.prBody }} + prAuthor: ${{ steps.prAuthor.outputs.prAuthor }} + prAction: ${{ steps.prAction.outputs.prAction }} + prIsMerged: ${{ steps.prIsMerged.outputs.prIsMerged }} + prLabels: ${{ steps.prLabels.outputs.prLabels }} + steps: + - name: Checkout repository # in case caller workflow doesn't checkout thus failing with file not found + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: "Download previously saved PR" + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + WORKFLOW_ID: ${{ inputs.record_pr_workflow_id }} + # For security, we only download artifacts tied to the successful PR recording workflow + with: + github-token: ${{ secrets.token }} + script: | + const script = require('.github/scripts/download_pr_artifact.js') + await script({github, context, core}) + # NodeJS standard library doesn't provide ZIP capabilities; use system `unzip` command instead + - name: "Unzip PR artifact" + run: unzip pr.zip + # NOTE: We need separate steps for each mapped output and respective IDs + # otherwise the parent caller won't see them regardless on how outputs are set. + - name: "Export Pull Request Number" + id: prNumber + run: echo prNumber="$(jq -c '.number' "${FILENAME}")" >> "$GITHUB_OUTPUT" + - name: "Export Pull Request Title" + id: prTitle + run: echo prTitle="$(jq -c '.pull_request.title' "${FILENAME}")" >> "$GITHUB_OUTPUT" + - name: "Export Pull Request Body" + id: prBody + run: echo prBody="$(jq -c '.pull_request.body' "${FILENAME}")" >> "$GITHUB_OUTPUT" + - name: "Export Pull Request Author" + id: prAuthor + run: echo prAuthor="$(jq -c '.pull_request.user.login' "${FILENAME}")" >> "$GITHUB_OUTPUT" + - name: "Export Pull Request Action" + id: prAction + run: echo prAction="$(jq -c '.action' "${FILENAME}")" >> "$GITHUB_OUTPUT" + - name: "Export Pull Request Merged status" + id: prIsMerged + run: echo prIsMerged="$(jq -c '.pull_request.merged' "${FILENAME}")" >> "$GITHUB_OUTPUT" + - name: "Export Pull Request labels" + id: prLabels + run: echo prLabels="$(jq -c '.labels' "${FILENAME}")" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/reusable_publish_changelog.yml b/.github/workflows/reusable_publish_changelog.yml new file mode 100644 index 00000000000..a0bc289e669 --- /dev/null +++ b/.github/workflows/reusable_publish_changelog.yml @@ -0,0 +1,41 @@ +name: Build and publish latest changelog + +# see ./.github/workflows/build_changelog.yml for docs + +on: + workflow_call: + +env: + TEMP_BRANCH_PREFIX: "ci-changelog" + PULL_REQUEST_TITLE: "chore(ci): changelog rebuild" + FILES_TO_COMMIT: "CHANGELOG.md" + +permissions: + contents: read + +jobs: + publish_changelog: + if: github.repository == 'aws-powertools/powertools-lambda-python' + # Force Github action to run only a single job at a time (based on the group name) + # This is to prevent race-condition and inconsistencies with changelog push + concurrency: + group: changelog-build + runs-on: ubuntu-latest + permissions: + contents: write # create temporary branch with changelog + pull-requests: write # create PR + steps: + - name: Checkout repository # reusable workflows start clean, so we need to checkout again + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + - name: "Generate latest changelog" + run: make changelog + - name: Create PR + id: create-pr + uses: ./.github/actions/create-pr + with: + files: ${{ env.FILES_TO_COMMIT }} + temp_branch_prefix: ${{ env.TEMP_BRANCH_PREFIX }} + pull_request_title: ${{ env.PULL_REQUEST_TITLE }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/reusable_publish_docs.yml b/.github/workflows/reusable_publish_docs.yml new file mode 100644 index 00000000000..89a3a72a5ee --- /dev/null +++ b/.github/workflows/reusable_publish_docs.yml @@ -0,0 +1,123 @@ +name: Reusable publish documentation + +# see .github/workflows/on_push_docs.yml for docs + +env: + ORIGIN: aws-powertools/powertools-lambda-python + +on: + workflow_call: + inputs: + version: + description: "Version to build and publish docs (1.28.0, develop)" + required: true + type: string + alias: + description: "Alias to associate version (latest, stage)" + required: true + type: string + detached_mode: + description: "Whether it's running in git detached mode to ensure git is sync'd" + required: false + default: false + type: boolean + git_ref: + description: "Branch or commit ID to checkout from" + required: false + type: string + default: develop + +permissions: + contents: read + +jobs: + publish_docs: + if: github.repository == 'aws-powertools/powertools-lambda-python' + # Force Github action to run only a single job at a time (based on the group name) + # This is to prevent "race-condition" in publishing a new version of doc to `gh-pages` + concurrency: + group: on-docs-rebuild + runs-on: ubuntu-latest + environment: "Docs" + permissions: + id-token: write # trade JWT token for AWS credentials in AWS Docs account + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + ref: ${{ inputs.git_ref }} + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + - name: Install doc generation dependencies + run: | + cat docs/requirements.txt + pip install --require-hashes -r docs/requirements.txt + - name: Git client setup + run: | + git config --global user.name Docs deploy + git config --global user.email aws-devax-open-source@amazon.com + - name: Git refresh tip (detached mode) + # Git Detached mode (release notes) doesn't have origin + if: ${{ inputs.detached_mode }} + run: | + git config pull.rebase true + git config remote.origin.url >&- || git remote add origin https://github.com/"$ORIGIN" + git pull origin "$BRANCH" + env: + BRANCH: ${{ inputs.git_ref }} + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + mask-aws-account-id: true + - name: Build docs + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} + run: | + rm -rf site + mkdocs build + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-python/${{ env.VERSION }}/ + - name: Deploy Docs (Alias) + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-python/${{ env.ALIAS }}/ + - name: Deploy Docs (Version JSON) + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} + # We originally used "mike" from PyPi to manage versions for us, but since we moved to S3, we can't use it to manage versions any more. + # Instead, we're using some shell script that manages the versions. + # + # Operations: + # 1. Download the versions.json file from S3 + # 2. Find any reference to the alias and delete it from the versions file + # 3. This is voodoo (don't use JQ): + # - we assign the input as $o and the new version/alias as $n, + # - we check if the version number exists in the file already (for republishing docs) + # - if it's an alias (stage/latest/*) or old version, we do nothing and output $o (original input) + # - if it's a new version number, we add it at position 0 in the array. + # 4. Once done, we'll upload it back to S3. + run: | + aws s3 cp \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-python/versions.json \ + versions_old.json + jq 'del(.[].aliases[] | select(. == "${{ env.ALIAS }}"))' < versions_old.json > versions_proc.json + jq '. as $o | [{"title": "${{ env.VERSION }}", "version": "${{ env.VERSION }}", "aliases": ["${{ env.ALIAS }}"] }] as $n | $n | if .[0].title | test("[a-z]+") or any($o[].title == $n[0].title;.) then [($o | .[] | select(.title == $n[0].title).aliases += $n[0].aliases | . )] else $n + $o end' < versions_proc.json > versions.json + aws s3 cp \ + versions.json \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-python/versions.json diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml new file mode 100644 index 00000000000..89f3963217c --- /dev/null +++ b/.github/workflows/run-e2e-tests.yml @@ -0,0 +1,81 @@ +name: Run end-to-end tests + +# PROCESS +# +# 1. Install all dependencies and spin off containers for all supported Python versions +# 2. Install pinned CDK version +# 3. Trade JWT token for AWS credentials to Test account +# 4. Run E2E in parallel for each feature + +# USAGE +# +# see MAINTAINERS.md#internals for full details on mechanics. +# +# Always triggered on new PR merge. + +on: + workflow_dispatch: + + push: + branches: + - develop + - v3 + paths: + - "aws_lambda_powertools/**" + - "tests/e2e/**" + - "pyproject.toml" + - "poetry.lock" + - "mypy.ini" + - "parallel_run_e2e.py" + +# Maintenance: Add support for triggering on `run-e2e` label +# and enforce repo origin to prevent abuse + +env: + AWS_DEFAULT_REGION: us-east-1 + +concurrency: e2e + +permissions: + contents: read + +jobs: + run: + runs-on: aws-powertools_ubuntu-latest_8-core + permissions: + id-token: write # needed to request JWT with GitHub's OIDC Token endpoint. docs: https://bit.ly/3MNgQO9 + contents: read # checkout code + strategy: + fail-fast: false # needed so if a version fails, the others will still be able to complete and cleanup + matrix: + version: ["3.9", "3.10", "3.11", "3.12","3.13"] + if: ${{ github.actor != 'dependabot[bot]' && github.repository == 'aws-powertools/powertools-lambda-python' }} + steps: + - name: "Checkout" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Install poetry + run: pipx install poetry + - name: "Use Python" + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ matrix.version }} + architecture: "x64" + cache: "poetry" + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "20.10.0" + - name: Install CDK CLI + run: | + npm ci + npx cdk --version + - name: Install dependencies + run: make dev-quality-code + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + role-to-assume: ${{ secrets.AWS_TEST_ROLE_ARN }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} + mask-aws-account-id: true + - name: Test + run: make e2e-test diff --git a/.github/workflows/secure_workflows.yml b/.github/workflows/secure_workflows.yml new file mode 100644 index 00000000000..84a1cba7cd3 --- /dev/null +++ b/.github/workflows/secure_workflows.yml @@ -0,0 +1,39 @@ +name: Lockdown untrusted workflows + +# PROCESS +# +# 1. Scans for any external GitHub Action being used without version pinning (@<commit-sha> vs @v3) +# 2. Scans for insecure practices for inline bash scripts (shellcheck) +# 3. Fail CI and prevent PRs to be merged if any malpractice is found + +# USAGE +# +# Always triggered on new PR, PR changes and PR merge. + + +on: + push: + paths: + - ".github/workflows/**" + pull_request: + paths: + - ".github/workflows/**" + +permissions: + contents: read + +jobs: + enforce_pinned_workflows: + name: Harden Security + runs-on: ubuntu-latest + permissions: + contents: read # checkout code and subsequently GitHub action workflows + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Ensure 3rd party workflows have SHA pinned + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@fc87bb5b5a97953d987372e74478de634726b3e5 # v3.0.25 + with: + allowlist: | + slsa-framework/slsa-github-generator + aws-powertools/actions diff --git a/.github/workflows/update_ssm.yml b/.github/workflows/update_ssm.yml new file mode 100644 index 00000000000..26116099f40 --- /dev/null +++ b/.github/workflows/update_ssm.yml @@ -0,0 +1,128 @@ +name: SSM Parameters + +# SSM Parameters update +# +# PROCESS +# Creates parameters in regional AWS accounts for each layer we create, using the inputs to target specific releases +# * environment: will prefix /beta/ into the parameter +# * write_latest: will create a latest alias instead of a version number in the parameter +# * package_version: semantic version number of the released layer (3.x.y) +# * layer_version: this is sequential layer version from the ARN +# +# A successful parameter would look similar to: +# /aws/service/powertools/python/arm64/python3.13/3.1.0 +# And will have a value of: +# arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV3-python313-arm64:4 + +on: + workflow_dispatch: + inputs: + environment: + description: Environment to deploy to + type: choice + options: + - Beta + - Prod + required: true + + write_latest: + description: Write to the latest path + type: boolean + required: false + + package_version: + description: Semantic Version of published layer + type: string + required: true + + layer_version: + description: Layer version + type: string + required: true + + workflow_call: + inputs: + environment: + description: Environment to deploy to, one of `Prod` or `Beta` + type: string + required: true + + write_latest: + description: Write to the latest path + type: boolean + required: false + default: true + + package_version: + description: Semantic Version of published layer + type: string + required: true + + layer_version: + description: Layer version + type: string + required: true + +run-name: SSM Parameters - Python - Layer version ${{ inputs.layer_version }} - v${{ inputs.package_version }} + +permissions: + contents: read + +jobs: + python: + runs-on: ubuntu-latest + environment: SSM + strategy: + matrix: + region: ["af-south-1", "ap-east-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", + "ap-south-1", "ap-south-2", "ap-southeast-1", "ap-southeast-2", "ap-southeast-3", + "ap-southeast-4", "ap-southeast-5", "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", "eu-central-2", + "eu-north-1", "eu-south-1", "eu-south-2", "eu-west-1", "eu-west-2", "eu-west-3", + "il-central-1", "me-central-1", "me-south-1", "mx-central-1", "sa-east-1", "us-east-1", + "us-east-2", "us-west-1", "us-west-2"] + + permissions: + contents: read + id-token: write + steps: + - id: transform + run: | + echo 'CONVERTED_REGION=${{ matrix.region }}' | tr 'a-z\-' 'A-Z_' >> "$GITHUB_OUTPUT" + - id: creds + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + with: + aws-region: ${{ matrix.region }} + role-to-assume: ${{ secrets[format('{0}', steps.transform.outputs.CONVERTED_REGION)] }} + mask-aws-account-id: true + - id: write-version + env: + prefix: ${{ inputs.environment == 'beta' && '/aws/service/powertools/beta' || '/aws/service/powertools' }} + run: | + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.9/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python39-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.10/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python310-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.11/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python311-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.12/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python312-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.13/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python313-arm64:${{ inputs.layer_version }}" --type String --overwrite + + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.9/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python39-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.10/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python310-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.11/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python311-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.12/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python312-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.13/${{ inputs.package_version }} --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python313-x86_64:${{ inputs.layer_version }}" --type String --overwrite + + - id: write-latest + if: inputs.write_latest == true + env: + prefix: ${{ inputs.environment == 'beta' && '/aws/service/powertools/beta' || '/aws/service/powertools' }} + run: | + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.9/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python39-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.10/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python310-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.11/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python311-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.12/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python312-arm64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/arm64/python3.13/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python313-arm64:${{ inputs.layer_version }}" --type String --overwrite + + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.9/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python39-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.10/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python310-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.11/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python311-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.12/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python312-x86_64:${{ inputs.layer_version }}" --type String --overwrite + aws ssm put-parameter --name ${{ env.prefix }}/python/x86_64/python3.13/latest --value "arn:aws:lambda:${{ matrix.region }}:017000801446:layer:AWSLambdaPowertoolsPythonV3-python313-x86_64:${{ inputs.layer_version }}" --type String --overwrite diff --git a/.gitignore b/.gitignore index ce5e32bd3e1..9f244805f60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - # Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode # Edit at https://www.gitignore.io/?templates=osx,linux,python,windows,pycharm,visualstudiocode @@ -253,6 +252,7 @@ dmypy.json .pyre/ ### VisualStudioCode ### +.vscode .vscode/* !.vscode/tasks.json !.vscode/launch.json @@ -304,3 +304,17 @@ node_modules api/ site/ !404.html +!docs/overrides/*.html + +# CDK +.cdk + +!.github/workflows/lib +examples/**/sam/.aws-sam + +cdk.out +# NOTE: different accounts will be used for E2E thus creating unnecessary git clutter +cdk.context.json + +# vim +*.swp diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000000..0622b57c118 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,15 @@ +# Title for the gitleaks configuration file. +title = "Gitleaks" + +[extend] +# useDefault will extend the base configuration with the default gitleaks config: +# https://github.com/zricethezav/gitleaks/blob/master/config/gitleaks.toml +useDefault = true + +[allowlist] +description = "Allow list false positive" + +# Allow list paths to ignore due to false positives. +paths = [ + '''tests/unit/parser/test_kinesis_firehose\.py''', +] diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 00000000000..d501e5cc212 --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1,3 @@ +examples/batch_processing/src/context_manager_access_output_pydantic.txt:aws-access-token:10 +examples/batch_processing/src/context_manager_access_output_pydantic.txt:aws-access-token:15 +examples/batch_processing/src/context_manager_access_output.txt:aws-access-token:10 diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 00000000000..9fa927ddac6 --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,11 @@ +# See here all gitpod images available: https://hub.docker.com/r/gitpod/workspace-python-3.11/tags +# Current python version: 3.11.9 +FROM gitpod/workspace-python-3.11@sha256:2d9a242844bef5710ab4622899a5254a0c59f0ac58c0d3ac998f749323f43951 + +WORKDIR /app +ADD . /app + +# Installing pre-commit as system package and not user package. Git needs this to execute pre-commit hooks. +RUN export PIP_USER=no +# pre-commit v3.7.1 +RUN python3 -m pip install --require-hashes -r .gitpod_requirements.txt \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000000..d831f067bdd --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,19 @@ +image: + file: .gitpod.Dockerfile +tasks: + - init: make dev-gitpod +vscode: + extensions: + - ms-python.python # IntelliSense (Pylance), Linting, Debugging (multi-threaded, remote), Jupyter Notebooks, code formatting, refactoring, unit tests, and more. + - littlefoxteam.vscode-python-test-adapter # Run your Python tests in the Sidebar of Visual Studio Code + - ms-azuretools.vscode-docker # Makes it easy to create, manage, and debug containerized applications. + - davidanson.vscode-markdownlint # Markdown linting and style checking for Visual Studio Code + - bungcip.better-toml # Better TOML Language support + - oderwat.indent-rainbow # Makes indentation easier to read + - yzhang.markdown-all-in-one # Autoformat, better visualization, snippets, and markdown export to multiple fmts + - bierner.markdown-mermaid # Previews mermaid diagrams when previewing markdown + - matangover.mypy # Highlight mypy issues + - njpwerner.autodocstring # Auto-generate docsstrings in numpy format that we use + - netcorext.uuid-generator # For those helping create code snippets for docs + - streetsidesoftware.code-spell-checker # Spell checker that works with camel case too + - bungcip.better-toml # In case GitPod doesn't have support for TOML pyproject.toml diff --git a/.gitpod_requirements.in b/.gitpod_requirements.in new file mode 100644 index 00000000000..b427b003fa9 --- /dev/null +++ b/.gitpod_requirements.in @@ -0,0 +1 @@ +pre-commit==3.7.1 \ No newline at end of file diff --git a/.gitpod_requirements.txt b/.gitpod_requirements.txt new file mode 100644 index 00000000000..a9643d7dfdf --- /dev/null +++ b/.gitpod_requirements.txt @@ -0,0 +1,85 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --generate-hashes --output-file=.gitpod_requirements.txt .gitpod_requirements.in +# +cfgv==3.3.1 \ + --hash=sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426 \ + --hash=sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736 + # via pre-commit +distlib==0.3.6 \ + --hash=sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46 \ + --hash=sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e + # via virtualenv +filelock==3.12.2 \ + --hash=sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81 \ + --hash=sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec + # via virtualenv +identify==2.5.24 \ + --hash=sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4 \ + --hash=sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d + # via pre-commit +nodeenv==1.8.0 \ + --hash=sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2 \ + --hash=sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec + # via pre-commit +platformdirs==3.8.0 \ + --hash=sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc \ + --hash=sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e + # via virtualenv +pre-commit==3.7.1 \ + --hash=sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a \ + --hash=sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5 + # via -r .gitpod_requirements.in +pyyaml==6.0 \ + --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \ + --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ + --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ + --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ + --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ + --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ + --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ + --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ + --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ + --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ + --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ + --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ + --hash=sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782 \ + --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1 \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7 \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358 \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f \ + --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ + --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 + # via pre-commit +virtualenv==20.23.1 \ + --hash=sha256:34da10f14fea9be20e0fd7f04aba9732f84e593dac291b757ce42e3368a39419 \ + --hash=sha256:8ff19a38c1021c742148edc4f81cb43d7f8c6816d2ede2ab72af5b84c749ade1 + # via pre-commit + +# WARNING: The following packages were not pinned, but pip requires them to be +# pinned when the requirements file includes hashes and the requirement is not +# satisfied by a package already installed. Consider using the --allow-unsafe flag. +# setuptools diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 00000000000..4529480ad19 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,240 @@ +# Rules: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time +MD001: true + +# MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading +# NOTE: We use h2 due to font size +MD002: false + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: "consistent" + +# MD004/ul-style - Unordered list style +MD004: + # List style + style: "consistent" + +# MD005/list-indent - Inconsistent indentation for list items at the same level +MD005: true + +# MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line +MD006: true + +# MD007/ul-indent - Unordered list indentation +MD007: + # Spaces for indent + indent: 4 + # Whether to indent the first level of the list + start_indented: false + # Spaces for first level indent (when start_indented is set) + start_indent: 2 + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +# NOTE: Mkdocs Material theme features like code annotations, tabbed content require it +MD010: false + +# MD011/no-reversed-links - Reversed link syntax +MD011: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length - Line length +MD013: + # Number of characters + line_length: 380 + # Number of characters for headings + heading_line_length: 80 + # Number of characters for code blocks + code_block_line_length: 265 + # Include code blocks + code_blocks: true + # Include tables + tables: false + # Include headings + headings: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD014/commands-show-output - Dollar signs used before commands without showing output +MD014: true + +# MD018/no-missing-space-atx - No space after hash on atx style heading +MD018: true + +# MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading +MD019: true + +# MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading +MD020: true + +# MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading +MD021: true + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line +MD023: true + +# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content +MD024: + # Only check sibling headings + siblings_only: false + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: + # Punctuation characters + punctuation: ".,;:!。,;:!" + +# MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol +MD027: true + +# MD028/no-blanks-blockquote - Blank line inside blockquote +MD028: true + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines +MD031: + # Include list items + list_items: true + +# MD032/blanks-around-lists - Lists should be surrounded by blank lines +MD032: true + +# MD033/no-inline-html - Inline HTML +# NOTE: Some content like Logger '<module>' triggers false positives +MD033: false + +# MD034/no-bare-urls - Bare URL used +MD034: true + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: "consistent" + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +# NOTE: We use **<topic>** instead of yet another sub-heading that might not appear in the navigation. +# this is a trade-off we make to not a gigantic right-navigation +MD036: false + +# MD037/no-space-in-emphasis - Spaces inside emphasis markers +MD037: true + +# MD038/no-space-in-code - Spaces inside code span elements +# mkdocs-material requires these in tab content +MD038: false + +# MD039/no-space-in-links - Spaces inside link text +MD039: true + +# MD040/fenced-code-language - Fenced code blocks should have a language specified +MD040: true + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 2 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD042/no-empty-links - No empty links +# NOTE: Clipboard links like Lambda Layers use empty links +MD042: false + +# MD043/required-headings/required-headers - Required heading structure +# NOTE: Enforce our minimum headers across the docs +MD043: + # List of headings + headings: + [ + "*", + "## Key features", + "*", + "## Getting started", + "*", + "## Advanced", + "*", + "## Testing your code", + "*", + ] + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: [] + # Include code blocks + code_blocks: true + # Include HTML elements + html_elements: true + +# MD045/no-alt-text - Images should have alternate text (alt text) +MD045: true + +# MD046/code-block-style - Code block style +# Material theme tabbed content feature use indented and simple use fenced; can't support both +MD046: false + +# MD047/single-trailing-newline - Files should end with a single newline character +MD047: true + +# MD048/code-fence-style - Code fence style +MD048: false + +# MD051/link-fragments - Link fragments should be valid +MD051: true + +# MD052/reference-links-images - Reference links and images should use a label that is defined +MD052: true + +# MD053/link-image-reference-definitions - Link and image reference definitions should be needed +MD053: true diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 00000000000..11b6d7ffe29 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,2 @@ +docs/core/metrics/index.md +includes/abbreviations.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61e98378017..de0c36b21e0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,30 +4,50 @@ # All checks can be run locally via `make pr` repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.4.0 - hooks: - - id: check-merge-conflict - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-toml - - repo: local - hooks: - - id: black - name: formatting::black - entry: poetry run black - language: system - types: [python] - - id: isort - name: formatting::isort - entry: poetry run isort - language: system - types: [python] - - repo: local - hooks: - - id: flake8 - name: linting::flake8 - entry: poetry run flake8 - language: system - types: [python] - exclude: example + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: "f71fa2c1f9cf5cb705f73dffe4b21f7c61470ba9" # v4.4.0 + hooks: + - id: check-merge-conflict + - id: trailing-whitespace + - id: check-toml + - repo: local + hooks: + - id: ruff + name: formatting::ruff + entry: poetry run ruff format + language: system + types: [python] + - id: ruff + name: linting::ruff + entry: poetry run ruff check + language: system + types: [python] + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: "ce0d77ac47dc921b62429804fe763d4d35f66a76" # v0.34.0 + hooks: + - id: markdownlint-docker + args: ["--fix"] + - repo: local + hooks: + - id: cloudformation + name: linting::cloudformation + entry: poetry run cfn-lint + language: system + types: [yaml] + exclude: examples/homepage/install/.*?/serverless\.yml$ + files: examples/.* + - repo: https://github.com/rhysd/actionlint + rev: "fd7ba3c382e13dcc0248e425b4cbc3f1185fa3ee" # v1.6.24 + hooks: + - id: actionlint-docker + args: [-pyflakes=] + - repo: https://github.com/antonbabenko/pre-commit-terraform + rev: 3420134c37197c21edffc7e6093b14ffae8402f2 # v1.81.0 + hooks: + - id: terraform_fmt + args: + - --args=-recursive + - repo: https://github.com/gitleaks/gitleaks + rev: "7804d652c0460f0b61ea9bb2c1f202bfcdbbf144" # v8.16.3 + hooks: + - id: gitleaks diff --git a/404.html b/404.html index 2ee34f1988f..f09a446555b 100644 --- a/404.html +++ b/404.html @@ -10,7 +10,7 @@ const versionRegex = /(\w.+)\/(latest|([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?)/; if (websitePath.search(versionRegex) === -1) { - let projectName = "aws-lambda-powertools-python" + let projectName = "powertools-lambda-python" // redirect old links to latest version alias window.location = websitePath.replace(projectName, `${projectName}/latest`) } diff --git a/CHANGELOG.md b/CHANGELOG.md index 36e435cb6f3..0ee0f18c856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,833 +1,6967 @@ -# Changelog +<!-- changelog is partially generated, so it doesn't follow headings and required structure, so we disable it. --> +<!-- markdownlint-disable --> -All notable changes to this project will be documented in this file. +<a name="unreleased"></a> +# Unreleased -This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format for changes and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Maintenance -## 1.24.1 - 2022-01-20 -### Bug Fixes +<a name="v3.17.0"></a> +## [v3.17.0] - 2025-07-15 +## Bug Fixes + +* enable response compression when using multi-value headers ([#6936](https://github.com/aws-powertools/powertools-lambda-python/issues/6936)) + +## Documentation + +* **event-handler:** remove Amplify transformers section ([#6937](https://github.com/aws-powertools/powertools-lambda-python/issues/6937)) +* **event_handler:** revert deleted file ([#6947](https://github.com/aws-powertools/powertools-lambda-python/issues/6947)) +* **roadmap:** update roadmap items ([#6955](https://github.com/aws-powertools/powertools-lambda-python/issues/6955)) +* **we_made_this:** add MCP server template ([#6851](https://github.com/aws-powertools/powertools-lambda-python/issues/6851)) + +## Features + +* **event_handler:** add support for externalDocs attribute in OpenAPI schema ([#6945](https://github.com/aws-powertools/powertools-lambda-python/issues/6945)) +* **parser:** Added Cognito trigger schemas ([#6737](https://github.com/aws-powertools/powertools-lambda-python/issues/6737)) + +## Maintenance + +* version bump +* **ci:** fix ssm workflow ([#6980](https://github.com/aws-powertools/powertools-lambda-python/issues/6980)) +* **ci:** new pre-release 3.16.1a7 ([#6974](https://github.com/aws-powertools/powertools-lambda-python/issues/6974)) +* **ci:** new pre-release 3.16.1a6 ([#6969](https://github.com/aws-powertools/powertools-lambda-python/issues/6969)) +* **ci:** new pre-release 3.16.1a5 ([#6967](https://github.com/aws-powertools/powertools-lambda-python/issues/6967)) +* **ci:** new pre-release 3.16.1a1 ([#6943](https://github.com/aws-powertools/powertools-lambda-python/issues/6943)) +* **ci:** new pre-release 3.16.1a4 ([#6964](https://github.com/aws-powertools/powertools-lambda-python/issues/6964)) +* **ci:** new pre-release 3.16.1a0 ([#6933](https://github.com/aws-powertools/powertools-lambda-python/issues/6933)) +* **ci:** new pre-release 3.16.1a2 ([#6954](https://github.com/aws-powertools/powertools-lambda-python/issues/6954)) +* **ci:** new pre-release 3.16.1a3 ([#6959](https://github.com/aws-powertools/powertools-lambda-python/issues/6959)) +* **deps:** bump aws-encryption-sdk from 4.0.1 to 4.0.2 ([#6932](https://github.com/aws-powertools/powertools-lambda-python/issues/6932)) +* **deps:** bump mkdocs-llmstxt from 0.2.0 to 0.3.0 in /docs ([#6979](https://github.com/aws-powertools/powertools-lambda-python/issues/6979)) +* **deps:** bump mkdocs-material from 9.6.14 to 9.6.15 ([#6931](https://github.com/aws-powertools/powertools-lambda-python/issues/6931)) +* **deps:** bump mkdocs-llmstxt from 0.2.0 to 0.3.0 ([#6978](https://github.com/aws-powertools/powertools-lambda-python/issues/6978)) +* **deps:** bump typing-extensions from 4.14.0 to 4.14.1 ([#6951](https://github.com/aws-powertools/powertools-lambda-python/issues/6951)) +* **deps-dev:** bump types-python-dateutil from 2.9.0.20250516 to 2.9.0.20250708 ([#6962](https://github.com/aws-powertools/powertools-lambda-python/issues/6962)) +* **deps-dev:** bump aws-cdk-lib from 2.203.1 to 2.204.0 ([#6949](https://github.com/aws-powertools/powertools-lambda-python/issues/6949)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.203.1a0 to 2.204.0a0 ([#6950](https://github.com/aws-powertools/powertools-lambda-python/issues/6950)) +* **deps-dev:** bump bandit from 1.8.5 to 1.8.6 ([#6957](https://github.com/aws-powertools/powertools-lambda-python/issues/6957)) +* **deps-dev:** bump boto3-stubs from 1.39.2 to 1.39.3 ([#6940](https://github.com/aws-powertools/powertools-lambda-python/issues/6940)) +* **deps-dev:** bump coverage from 7.9.1 to 7.9.2 ([#6941](https://github.com/aws-powertools/powertools-lambda-python/issues/6941)) +* **deps-dev:** bump aws-cdk from 2.1020.1 to 2.1020.2 ([#6942](https://github.com/aws-powertools/powertools-lambda-python/issues/6942)) +* **deps-dev:** bump ruff from 0.12.1 to 0.12.2 ([#6938](https://github.com/aws-powertools/powertools-lambda-python/issues/6938)) +* **deps-dev:** bump types-protobuf from 6.30.2.20250516 to 6.30.2.20250703 ([#6939](https://github.com/aws-powertools/powertools-lambda-python/issues/6939)) +* **deps-dev:** bump cfn-lint from 1.37.0 to 1.37.1 ([#6958](https://github.com/aws-powertools/powertools-lambda-python/issues/6958)) +* **deps-dev:** bump cfn-lint from 1.37.1 to 1.37.2 ([#6963](https://github.com/aws-powertools/powertools-lambda-python/issues/6963)) +* **deps-dev:** bump boto3-stubs from 1.39.3 to 1.39.4 ([#6966](https://github.com/aws-powertools/powertools-lambda-python/issues/6966)) +* **deps-dev:** bump ruff from 0.12.2 to 0.12.3 ([#6971](https://github.com/aws-powertools/powertools-lambda-python/issues/6971)) +* **deps-dev:** bump mypy from 1.16.1 to 1.17.0 ([#6977](https://github.com/aws-powertools/powertools-lambda-python/issues/6977)) +* **deps-dev:** bump aws-cdk-lib from 2.203.0 to 2.203.1 ([#6928](https://github.com/aws-powertools/powertools-lambda-python/issues/6928)) +* **deps-dev:** bump boto3-stubs from 1.39.1 to 1.39.2 ([#6930](https://github.com/aws-powertools/powertools-lambda-python/issues/6930)) +* **deps-dev:** bump aws-cdk from 2.1020.0 to 2.1020.1 ([#6927](https://github.com/aws-powertools/powertools-lambda-python/issues/6927)) +* **deps-dev:** bump cfn-lint from 1.37.2 to 1.38.0 ([#6976](https://github.com/aws-powertools/powertools-lambda-python/issues/6976)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.203.0a0 to 2.203.1a0 ([#6929](https://github.com/aws-powertools/powertools-lambda-python/issues/6929)) + + +<a name="v3.16.0"></a> +## [v3.16.0] - 2025-07-02 +## Bug Fixes + +* **event_source:** fix decode headers with signed bytes ([#6878](https://github.com/aws-powertools/powertools-lambda-python/issues/6878)) +* **logger:** caplog working with parent Logger ([#6847](https://github.com/aws-powertools/powertools-lambda-python/issues/6847)) +* **logger:** fix exception on flush without buffer ([#6794](https://github.com/aws-powertools/powertools-lambda-python/issues/6794)) + +## Documentation + +* **kafka:** refactor kafka documentation ([#6854](https://github.com/aws-powertools/powertools-lambda-python/issues/6854)) + +## Features + +* **ci:** Partition workflow scripts updated to work ([#6900](https://github.com/aws-powertools/powertools-lambda-python/issues/6900)) +* **ci:** Deploy to AWS China partitions ([#6867](https://github.com/aws-powertools/powertools-lambda-python/issues/6867)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.15.2a6 ([#6923](https://github.com/aws-powertools/powertools-lambda-python/issues/6923)) +* **ci:** incorporate SSM update workflow into release pipeline ([#6914](https://github.com/aws-powertools/powertools-lambda-python/issues/6914)) +* **ci:** new pre-release 3.15.2a3 ([#6876](https://github.com/aws-powertools/powertools-lambda-python/issues/6876)) +* **ci:** new pre-release 3.15.2a2 ([#6865](https://github.com/aws-powertools/powertools-lambda-python/issues/6865)) +* **ci:** new pre-release 3.15.2a5 ([#6910](https://github.com/aws-powertools/powertools-lambda-python/issues/6910)) +* **ci:** new pre-release 3.15.2a4 ([#6881](https://github.com/aws-powertools/powertools-lambda-python/issues/6881)) +* **ci:** pinning versions in actions + refactor tests ([#6904](https://github.com/aws-powertools/powertools-lambda-python/issues/6904)) +* **ci:** stop publishing to gh-pages branch ([#6903](https://github.com/aws-powertools/powertools-lambda-python/issues/6903)) +* **ci:** stop publishing to gh-pages branch ([#6902](https://github.com/aws-powertools/powertools-lambda-python/issues/6902)) +* **ci:** stop publishing to gh-pages branch ([#6901](https://github.com/aws-powertools/powertools-lambda-python/issues/6901)) +* **ci:** stop publishing to gh-pages branch ([#6899](https://github.com/aws-powertools/powertools-lambda-python/issues/6899)) +* **ci:** remove v2 deployment files ([#6895](https://github.com/aws-powertools/powertools-lambda-python/issues/6895)) +* **ci:** bump urllib3 version ([#6893](https://github.com/aws-powertools/powertools-lambda-python/issues/6893)) +* **ci:** new pre-release 3.15.2a0 ([#6852](https://github.com/aws-powertools/powertools-lambda-python/issues/6852)) +* **ci:** refactor thread safe tests ([#6889](https://github.com/aws-powertools/powertools-lambda-python/issues/6889)) +* **ci:** new pre-release 3.15.2a1 ([#6860](https://github.com/aws-powertools/powertools-lambda-python/issues/6860)) +* **ci:** fix command to replace layer number ([#6868](https://github.com/aws-powertools/powertools-lambda-python/issues/6868)) +* **deps:** bump docker/setup-buildx-action from 3.10.0 to 3.11.1 ([#6823](https://github.com/aws-powertools/powertools-lambda-python/issues/6823)) +* **deps:** bump pydantic from 2.11.5 to 2.11.7 ([#6844](https://github.com/aws-powertools/powertools-lambda-python/issues/6844)) +* **deps:** bump mkdocs-material from 9.6.14 to 9.6.15 in /docs ([#6920](https://github.com/aws-powertools/powertools-lambda-python/issues/6920)) +* **deps:** bump redis from 5.3.0 to 6.2.0 ([#6827](https://github.com/aws-powertools/powertools-lambda-python/issues/6827)) +* **deps:** bump pydantic-settings from 2.9.1 to 2.10.1 ([#6872](https://github.com/aws-powertools/powertools-lambda-python/issues/6872)) +* **deps:** bump datadog-lambda from 6.110.0 to 6.111.0 ([#6857](https://github.com/aws-powertools/powertools-lambda-python/issues/6857)) +* **deps:** bump valkey-glide from 1.3.5 to 2.0.1 ([#6871](https://github.com/aws-powertools/powertools-lambda-python/issues/6871)) +* **deps:** bump squidfunk/mkdocs-material from `eb04b60` to `0bfdba4` in /docs ([#6915](https://github.com/aws-powertools/powertools-lambda-python/issues/6915)) +* **deps:** bump mkdocs-material from 9.5.50 to 9.6.14 in /docs ([#6908](https://github.com/aws-powertools/powertools-lambda-python/issues/6908)) +* **deps-dev:** bump sentry-sdk from 2.29.1 to 2.31.0 ([#6870](https://github.com/aws-powertools/powertools-lambda-python/issues/6870)) +* **deps-dev:** bump aws-cdk from 2.1019.1 to 2.1019.2 ([#6875](https://github.com/aws-powertools/powertools-lambda-python/issues/6875)) +* **deps-dev:** bump pytest from 8.4.0 to 8.4.1 ([#6874](https://github.com/aws-powertools/powertools-lambda-python/issues/6874)) +* **deps-dev:** bump cfn-lint from 1.35.4 to 1.36.1 ([#6855](https://github.com/aws-powertools/powertools-lambda-python/issues/6855)) +* **deps-dev:** bump boto3-stubs from 1.38.42 to 1.38.43 ([#6864](https://github.com/aws-powertools/powertools-lambda-python/issues/6864)) +* **deps-dev:** bump bandit from 1.8.3 to 1.8.5 ([#6856](https://github.com/aws-powertools/powertools-lambda-python/issues/6856)) +* **deps-dev:** bump boto3-stubs from 1.38.43 to 1.38.44 ([#6873](https://github.com/aws-powertools/powertools-lambda-python/issues/6873)) +* **deps-dev:** bump aws-cdk from 2.1018.1 to 2.1019.1 ([#6837](https://github.com/aws-powertools/powertools-lambda-python/issues/6837)) +* **deps-dev:** bump boto3-stubs from 1.38.41 to 1.38.42 ([#6858](https://github.com/aws-powertools/powertools-lambda-python/issues/6858)) +* **deps-dev:** bump boto3-stubs from 1.38.45 to 1.38.46 ([#6884](https://github.com/aws-powertools/powertools-lambda-python/issues/6884)) +* **deps-dev:** bump sentry-sdk from 2.31.0 to 2.32.0 ([#6885](https://github.com/aws-powertools/powertools-lambda-python/issues/6885)) +* **deps-dev:** bump ruff from 0.11.8 to 0.12.1 ([#6879](https://github.com/aws-powertools/powertools-lambda-python/issues/6879)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.202.0a0 to 2.203.0a0 ([#6919](https://github.com/aws-powertools/powertools-lambda-python/issues/6919)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.38.0 to 1.39.0 in the boto-typing group ([#6905](https://github.com/aws-powertools/powertools-lambda-python/issues/6905)) +* **deps-dev:** bump pytest-xdist from 3.7.0 to 3.8.0 ([#6921](https://github.com/aws-powertools/powertools-lambda-python/issues/6921)) +* **deps-dev:** bump mypy from 1.16.0 to 1.16.1 ([#6828](https://github.com/aws-powertools/powertools-lambda-python/issues/6828)) +* **deps-dev:** bump boto3-stubs from 1.39.0 to 1.39.1 ([#6916](https://github.com/aws-powertools/powertools-lambda-python/issues/6916)) +* **deps-dev:** bump boto3-stubs from 1.38.34 to 1.38.41 ([#6845](https://github.com/aws-powertools/powertools-lambda-python/issues/6845)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.200.1a0 to 2.202.0a0 ([#6846](https://github.com/aws-powertools/powertools-lambda-python/issues/6846)) +* **deps-dev:** bump aws-cdk from 2.1019.2 to 2.1020.0 ([#6917](https://github.com/aws-powertools/powertools-lambda-python/issues/6917)) +* **deps-dev:** bump cfn-lint from 1.36.1 to 1.37.0 ([#6918](https://github.com/aws-powertools/powertools-lambda-python/issues/6918)) +* **deps-dev:** bump boto3-stubs from 1.38.44 to 1.38.45 ([#6880](https://github.com/aws-powertools/powertools-lambda-python/issues/6880)) + + +<a name="v3.15.1"></a> +## [v3.15.1] - 2025-06-20 +## Features + +* **kafka:** add logic to handle protobuf deserialization ([#6841](https://github.com/aws-powertools/powertools-lambda-python/issues/6841)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.15.1a0 ([#6839](https://github.com/aws-powertools/powertools-lambda-python/issues/6839)) + + +<a name="v3.15.0"></a> +## [v3.15.0] - 2025-06-19 +## Bug Fixes + +* **bedrock_agent:** fix querystring field resolution ([#6777](https://github.com/aws-powertools/powertools-lambda-python/issues/6777)) + +## Documentation + +* **kafka:** add kafka documentation ([#6834](https://github.com/aws-powertools/powertools-lambda-python/issues/6834)) +* **public_reference:** add Instil as a public reference ([#6763](https://github.com/aws-powertools/powertools-lambda-python/issues/6763)) + +## Features + +* **kafka:** add support for Confluence Producers ([#6833](https://github.com/aws-powertools/powertools-lambda-python/issues/6833)) +* **kafka:** New Kafka utility ([#6821](https://github.com/aws-powertools/powertools-lambda-python/issues/6821)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.14.1a6 ([#6830](https://github.com/aws-powertools/powertools-lambda-python/issues/6830)) +* **ci:** new pre-release 3.14.1a5 ([#6820](https://github.com/aws-powertools/powertools-lambda-python/issues/6820)) +* **ci:** new pre-release 3.14.1a0 ([#6773](https://github.com/aws-powertools/powertools-lambda-python/issues/6773)) +* **ci:** new pre-release 3.14.1a4 ([#6812](https://github.com/aws-powertools/powertools-lambda-python/issues/6812)) +* **ci:** new pre-release 3.14.1a3 ([#6797](https://github.com/aws-powertools/powertools-lambda-python/issues/6797)) +* **ci:** new pre-release 3.14.1a1 ([#6778](https://github.com/aws-powertools/powertools-lambda-python/issues/6778)) +* **ci:** new pre-release 3.14.1a2 ([#6788](https://github.com/aws-powertools/powertools-lambda-python/issues/6788)) +* **deps:** bump mkdocstrings-python from 1.16.11 to 1.16.12 in /docs ([#6768](https://github.com/aws-powertools/powertools-lambda-python/issues/6768)) +* **deps:** bump mkdocstrings-python from 1.16.11 to 1.16.12 ([#6765](https://github.com/aws-powertools/powertools-lambda-python/issues/6765)) +* **deps:** bump protobuf from 6.31.0 to 6.31.1 ([#6815](https://github.com/aws-powertools/powertools-lambda-python/issues/6815)) +* **deps-dev:** bump boto3-stubs from 1.38.29 to 1.38.30 ([#6772](https://github.com/aws-powertools/powertools-lambda-python/issues/6772)) +* **deps-dev:** bump aws-cdk from 2.1017.1 to 2.1018.0 ([#6775](https://github.com/aws-powertools/powertools-lambda-python/issues/6775)) +* **deps-dev:** bump boto3-stubs from 1.38.33 to 1.38.35 ([#6796](https://github.com/aws-powertools/powertools-lambda-python/issues/6796)) +* **deps-dev:** bump aws-cdk from 2.1018.0 to 2.1018.1 ([#6803](https://github.com/aws-powertools/powertools-lambda-python/issues/6803)) +* **deps-dev:** bump boto3-stubs from 1.38.30 to 1.38.31 ([#6776](https://github.com/aws-powertools/powertools-lambda-python/issues/6776)) +* **deps-dev:** bump requests from 2.32.3 to 2.32.4 ([#6789](https://github.com/aws-powertools/powertools-lambda-python/issues/6789)) +* **deps-dev:** bump boto3-stubs from 1.38.28 to 1.38.29 ([#6764](https://github.com/aws-powertools/powertools-lambda-python/issues/6764)) +* **deps-dev:** bump ruff from 0.11.12 to 0.11.13 ([#6780](https://github.com/aws-powertools/powertools-lambda-python/issues/6780)) +* **deps-dev:** bump boto3-stubs from 1.38.31 to 1.38.33 ([#6786](https://github.com/aws-powertools/powertools-lambda-python/issues/6786)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.200.0a0 to 2.200.1a0 ([#6766](https://github.com/aws-powertools/powertools-lambda-python/issues/6766)) +* **deps-dev:** bump aws-cdk-lib from 2.200.0 to 2.200.1 ([#6767](https://github.com/aws-powertools/powertools-lambda-python/issues/6767)) +* **deps-dev:** bump pytest-cov from 6.1.1 to 6.2.1 ([#6800](https://github.com/aws-powertools/powertools-lambda-python/issues/6800)) +* **deps-dev:** bump requests from 2.32.3 to 2.32.4 ([#6787](https://github.com/aws-powertools/powertools-lambda-python/issues/6787)) + + +<a name="v3.14.0"></a> +## [v3.14.0] - 2025-06-03 +## Bug Fixes + +* **event_handler:** fix OpenAPI schema response for disabled validation ([#6720](https://github.com/aws-powertools/powertools-lambda-python/issues/6720)) + +## Features + +* **bedrock_agent:** add new Amazon Bedrock Agents Functions Resolver ([#6564](https://github.com/aws-powertools/powertools-lambda-python/issues/6564)) +* **event_handler:** enable support for custom deserializer to parse the request body ([#6601](https://github.com/aws-powertools/powertools-lambda-python/issues/6601)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.13.1a7 ([#6759](https://github.com/aws-powertools/powertools-lambda-python/issues/6759)) +* **ci:** new pre-release 3.13.1a0 ([#6696](https://github.com/aws-powertools/powertools-lambda-python/issues/6696)) +* **ci:** new pre-release 3.13.1a6 ([#6751](https://github.com/aws-powertools/powertools-lambda-python/issues/6751)) +* **ci:** new pre-release 3.13.1a1 ([#6704](https://github.com/aws-powertools/powertools-lambda-python/issues/6704)) +* **ci:** new pre-release 3.13.1a2 ([#6709](https://github.com/aws-powertools/powertools-lambda-python/issues/6709)) +* **ci:** new pre-release 3.13.1a5 ([#6744](https://github.com/aws-powertools/powertools-lambda-python/issues/6744)) +* **ci:** new pre-release 3.13.1a4 ([#6738](https://github.com/aws-powertools/powertools-lambda-python/issues/6738)) +* **ci:** add missing dependency to build docs ([#6717](https://github.com/aws-powertools/powertools-lambda-python/issues/6717)) +* **ci:** new pre-release 3.13.1a3 ([#6732](https://github.com/aws-powertools/powertools-lambda-python/issues/6732)) +* **deps:** bump pydantic from 2.11.4 to 2.11.5 ([#6711](https://github.com/aws-powertools/powertools-lambda-python/issues/6711)) +* **deps:** bump mkdocstrings-python from 1.16.10 to 1.16.11 ([#6724](https://github.com/aws-powertools/powertools-lambda-python/issues/6724)) +* **deps:** bump redis from 6.1.0 to 6.2.0 ([#6736](https://github.com/aws-powertools/powertools-lambda-python/issues/6736)) +* **deps:** bump ossf/scorecard-action from 2.4.1 to 2.4.2 ([#6746](https://github.com/aws-powertools/powertools-lambda-python/issues/6746)) +* **deps:** bump mkdocstrings-python from 1.16.10 to 1.16.11 in /docs ([#6722](https://github.com/aws-powertools/powertools-lambda-python/issues/6722)) +* **deps:** bump datadog-lambda from 6.109.0 to 6.110.0 ([#6714](https://github.com/aws-powertools/powertools-lambda-python/issues/6714)) +* **deps-dev:** bump pytest-mock from 3.14.0 to 3.14.1 ([#6723](https://github.com/aws-powertools/powertools-lambda-python/issues/6723)) +* **deps-dev:** bump pytest-xdist from 3.6.1 to 3.7.0 ([#6730](https://github.com/aws-powertools/powertools-lambda-python/issues/6730)) +* **deps-dev:** bump aws-cdk-lib from 2.198.0 to 2.199.0 ([#6731](https://github.com/aws-powertools/powertools-lambda-python/issues/6731)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.197.0a0 to 2.198.0a0 ([#6715](https://github.com/aws-powertools/powertools-lambda-python/issues/6715)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.198.0a0 to 2.199.0a0 ([#6729](https://github.com/aws-powertools/powertools-lambda-python/issues/6729)) +* **deps-dev:** bump coverage from 7.8.1 to 7.8.2 ([#6713](https://github.com/aws-powertools/powertools-lambda-python/issues/6713)) +* **deps-dev:** bump boto3-stubs from 1.38.22 to 1.38.23 ([#6712](https://github.com/aws-powertools/powertools-lambda-python/issues/6712)) +* **deps-dev:** bump aws-cdk from 2.1016.1 to 2.1017.0 ([#6734](https://github.com/aws-powertools/powertools-lambda-python/issues/6734)) +* **deps-dev:** bump boto3-stubs from 1.38.23 to 1.38.25 ([#6735](https://github.com/aws-powertools/powertools-lambda-python/issues/6735)) +* **deps-dev:** bump ruff from 0.11.11 to 0.11.12 ([#6741](https://github.com/aws-powertools/powertools-lambda-python/issues/6741)) +* **deps-dev:** bump boto3-stubs from 1.38.25 to 1.38.26 ([#6742](https://github.com/aws-powertools/powertools-lambda-python/issues/6742)) +* **deps-dev:** bump cfn-lint from 1.35.1 to 1.35.3 ([#6708](https://github.com/aws-powertools/powertools-lambda-python/issues/6708)) +* **deps-dev:** bump ruff from 0.11.10 to 0.11.11 ([#6706](https://github.com/aws-powertools/powertools-lambda-python/issues/6706)) +* **deps-dev:** bump boto3-stubs from 1.38.21 to 1.38.22 ([#6707](https://github.com/aws-powertools/powertools-lambda-python/issues/6707)) +* **deps-dev:** bump aws-cdk from 2.1016.0 to 2.1016.1 ([#6703](https://github.com/aws-powertools/powertools-lambda-python/issues/6703)) +* **deps-dev:** bump aws-cdk from 2.1017.0 to 2.1017.1 ([#6748](https://github.com/aws-powertools/powertools-lambda-python/issues/6748)) +* **deps-dev:** bump boto3-stubs from 1.38.26 to 1.38.27 ([#6747](https://github.com/aws-powertools/powertools-lambda-python/issues/6747)) +* **deps-dev:** bump coverage from 7.8.0 to 7.8.1 ([#6701](https://github.com/aws-powertools/powertools-lambda-python/issues/6701)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.196.1a0 to 2.197.0a0 ([#6700](https://github.com/aws-powertools/powertools-lambda-python/issues/6700)) +* **deps-dev:** bump aws-cdk-lib from 2.196.1 to 2.197.0 ([#6699](https://github.com/aws-powertools/powertools-lambda-python/issues/6699)) +* **deps-dev:** bump pytest from 8.3.5 to 8.4.0 ([#6757](https://github.com/aws-powertools/powertools-lambda-python/issues/6757)) +* **deps-dev:** bump cfn-lint from 1.35.3 to 1.35.4 ([#6755](https://github.com/aws-powertools/powertools-lambda-python/issues/6755)) +* **deps-dev:** bump boto3-stubs from 1.38.27 to 1.38.28 ([#6756](https://github.com/aws-powertools/powertools-lambda-python/issues/6756)) +* **deps-dev:** bump aws-cdk-lib from 2.196.0 to 2.196.1 ([#6695](https://github.com/aws-powertools/powertools-lambda-python/issues/6695)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.196.0a0 to 2.196.1a0 ([#6694](https://github.com/aws-powertools/powertools-lambda-python/issues/6694)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.199.0a0 to 2.200.0a0 ([#6758](https://github.com/aws-powertools/powertools-lambda-python/issues/6758)) +* **deps-dev:** bump boto3-stubs from 1.38.19 to 1.38.21 ([#6698](https://github.com/aws-powertools/powertools-lambda-python/issues/6698)) +* **docs:** Add llms.txt to documentation ([#6693](https://github.com/aws-powertools/powertools-lambda-python/issues/6693)) + + +<a name="v3.13.0"></a> +## [v3.13.0] - 2025-05-20 +## Code Refactoring + +* **idempotency:** replace Redis name with Cache and add valkey-glide support ([#6685](https://github.com/aws-powertools/powertools-lambda-python/issues/6685)) + +## Features + +* **event_source:** add support for tumbling windows in Kinesis and DynamoDB events ([#6658](https://github.com/aws-powertools/powertools-lambda-python/issues/6658)) +* **event_source:** export SQSRecord in data_classes module ([#6639](https://github.com/aws-powertools/powertools-lambda-python/issues/6639)) +* **parser:** add support to decompress Kinesis CloudWatch logs in Kinesis envelope ([#6656](https://github.com/aws-powertools/powertools-lambda-python/issues/6656)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.12.1a2 ([#6638](https://github.com/aws-powertools/powertools-lambda-python/issues/6638)) +* **ci:** include allowed licenses file in dependency review workflow ([#6618](https://github.com/aws-powertools/powertools-lambda-python/issues/6618)) +* **ci:** new pre-release 3.12.1a8 ([#6683](https://github.com/aws-powertools/powertools-lambda-python/issues/6683)) +* **ci:** new pre-release 3.12.1a3 ([#6647](https://github.com/aws-powertools/powertools-lambda-python/issues/6647)) +* **ci:** new pre-release 3.12.1a7 ([#6675](https://github.com/aws-powertools/powertools-lambda-python/issues/6675)) +* **ci:** new pre-release 3.12.1a0 ([#6621](https://github.com/aws-powertools/powertools-lambda-python/issues/6621)) +* **ci:** new pre-release 3.12.1a6 ([#6670](https://github.com/aws-powertools/powertools-lambda-python/issues/6670)) +* **ci:** new pre-release 3.12.1a1 ([#6626](https://github.com/aws-powertools/powertools-lambda-python/issues/6626)) +* **ci:** new pre-release 3.12.1a4 ([#6655](https://github.com/aws-powertools/powertools-lambda-python/issues/6655)) +* **ci:** new pre-release 3.12.1a5 ([#6664](https://github.com/aws-powertools/powertools-lambda-python/issues/6664)) +* **deps:** bump aws-actions/configure-aws-credentials from 4.2.0 to 4.2.1 ([#6667](https://github.com/aws-powertools/powertools-lambda-python/issues/6667)) +* **deps:** bump squidfunk/mkdocs-material from `f6c81d5` to `eb04b60` in /docs ([#6659](https://github.com/aws-powertools/powertools-lambda-python/issues/6659)) +* **deps:** bump datadog-lambda from 6.107.0 to 6.108.0 ([#6634](https://github.com/aws-powertools/powertools-lambda-python/issues/6634)) +* **deps:** bump actions/setup-go from 5.4.0 to 5.5.0 ([#6630](https://github.com/aws-powertools/powertools-lambda-python/issues/6630)) +* **deps:** bump actions/dependency-review-action from 4.7.0 to 4.7.1 ([#6663](https://github.com/aws-powertools/powertools-lambda-python/issues/6663)) +* **deps:** bump redis from 5.2.1 to 6.1.0 ([#6662](https://github.com/aws-powertools/powertools-lambda-python/issues/6662)) +* **deps:** bump actions/dependency-review-action from 4.6.0 to 4.7.0 ([#6629](https://github.com/aws-powertools/powertools-lambda-python/issues/6629)) +* **deps:** bump codecov/codecov-action from 5.4.2 to 5.4.3 ([#6672](https://github.com/aws-powertools/powertools-lambda-python/issues/6672)) +* **deps:** bump squidfunk/mkdocs-material from `95f2ff4` to `f6c81d5` in /docs ([#6650](https://github.com/aws-powertools/powertools-lambda-python/issues/6650)) +* **deps:** bump aws-actions/configure-aws-credentials from 4.1.0 to 4.2.0 ([#6619](https://github.com/aws-powertools/powertools-lambda-python/issues/6619)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.24 to 3.0.25 ([#6686](https://github.com/aws-powertools/powertools-lambda-python/issues/6686)) +* **deps:** bump datadog-lambda from 6.108.0 to 6.109.0 ([#6641](https://github.com/aws-powertools/powertools-lambda-python/issues/6641)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.308 to 0.1.309 ([#6651](https://github.com/aws-powertools/powertools-lambda-python/issues/6651)) +* **deps-dev:** bump boto3-stubs from 1.38.12 to 1.38.13 ([#6644](https://github.com/aws-powertools/powertools-lambda-python/issues/6644)) +* **deps-dev:** bump cfn-lint from 1.35.0 to 1.35.1 ([#6642](https://github.com/aws-powertools/powertools-lambda-python/issues/6642)) +* **deps-dev:** bump ruff from 0.11.8 to 0.11.9 ([#6643](https://github.com/aws-powertools/powertools-lambda-python/issues/6643)) +* **deps-dev:** bump boto3-stubs from 1.38.13 to 1.38.14 ([#6653](https://github.com/aws-powertools/powertools-lambda-python/issues/6653)) +* **deps-dev:** bump sentry-sdk from 2.27.0 to 2.28.0 ([#6652](https://github.com/aws-powertools/powertools-lambda-python/issues/6652)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.194.0a0 to 2.195.0a0 ([#6635](https://github.com/aws-powertools/powertools-lambda-python/issues/6635)) +* **deps-dev:** bump aws-cdk from 2.1013.0 to 2.1014.0 ([#6636](https://github.com/aws-powertools/powertools-lambda-python/issues/6636)) +* **deps-dev:** bump mkdocs-material from 9.6.12 to 9.6.13 ([#6654](https://github.com/aws-powertools/powertools-lambda-python/issues/6654)) +* **deps-dev:** bump boto3-stubs from 1.38.11 to 1.38.12 ([#6633](https://github.com/aws-powertools/powertools-lambda-python/issues/6633)) +* **deps-dev:** bump aws-cdk-lib from 2.194.0 to 2.195.0 ([#6632](https://github.com/aws-powertools/powertools-lambda-python/issues/6632)) +* **deps-dev:** bump boto3-stubs from 1.38.14 to 1.38.15 ([#6660](https://github.com/aws-powertools/powertools-lambda-python/issues/6660)) +* **deps-dev:** bump ijson from 3.3.0 to 3.4.0 ([#6631](https://github.com/aws-powertools/powertools-lambda-python/issues/6631)) +* **deps-dev:** bump mkdocs-material from 9.6.13 to 9.6.14 ([#6661](https://github.com/aws-powertools/powertools-lambda-python/issues/6661)) +* **deps-dev:** bump boto3-stubs from 1.38.15 to 1.38.16 ([#6669](https://github.com/aws-powertools/powertools-lambda-python/issues/6669)) +* **deps-dev:** bump aws-cdk from 2.1014.0 to 2.1015.0 ([#6668](https://github.com/aws-powertools/powertools-lambda-python/issues/6668)) +* **deps-dev:** bump cfn-lint from 1.34.2 to 1.35.0 ([#6623](https://github.com/aws-powertools/powertools-lambda-python/issues/6623)) +* **deps-dev:** bump types-python-dateutil from 2.9.0.20241206 to 2.9.0.20250516 ([#6678](https://github.com/aws-powertools/powertools-lambda-python/issues/6678)) +* **deps-dev:** bump ruff from 0.11.9 to 0.11.10 ([#6673](https://github.com/aws-powertools/powertools-lambda-python/issues/6673)) +* **deps-dev:** bump boto3-stubs from 1.38.16 to 1.38.17 ([#6674](https://github.com/aws-powertools/powertools-lambda-python/issues/6674)) +* **deps-dev:** bump boto3-stubs from 1.38.9 to 1.38.10 ([#6620](https://github.com/aws-powertools/powertools-lambda-python/issues/6620)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.195.0a0 to 2.196.0a0 ([#6677](https://github.com/aws-powertools/powertools-lambda-python/issues/6677)) +* **deps-dev:** bump aws-cdk from 2.1015.0 to 2.1016.0 ([#6680](https://github.com/aws-powertools/powertools-lambda-python/issues/6680)) +* **deps-dev:** bump boto3-stubs from 1.38.18 to 1.38.19 ([#6687](https://github.com/aws-powertools/powertools-lambda-python/issues/6687)) +* **deps-dev:** bump boto3-stubs from 1.38.10 to 1.38.11 ([#6624](https://github.com/aws-powertools/powertools-lambda-python/issues/6624)) + + +<a name="v3.12.0"></a> +## [v3.12.0] - 2025-05-06 +## Documentation + +* **appsync_events:** improve AppSync events documentation ([#6572](https://github.com/aws-powertools/powertools-lambda-python/issues/6572)) +* **community:** add Ran Isenberg blog post ([#6610](https://github.com/aws-powertools/powertools-lambda-python/issues/6610)) +* **i-made-this:** adding Michael's MCP server ([#6591](https://github.com/aws-powertools/powertools-lambda-python/issues/6591)) + +## Features + +* **bedrock_agents:** add optional fields to response payload ([#6336](https://github.com/aws-powertools/powertools-lambda-python/issues/6336)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.11.1a5 ([#6598](https://github.com/aws-powertools/powertools-lambda-python/issues/6598)) +* **ci:** new pre-release 3.11.1a0 ([#6561](https://github.com/aws-powertools/powertools-lambda-python/issues/6561)) +* **ci:** new pre-release 3.11.1a6 ([#6606](https://github.com/aws-powertools/powertools-lambda-python/issues/6606)) +* **ci:** new pre-release 3.11.1a1 ([#6574](https://github.com/aws-powertools/powertools-lambda-python/issues/6574)) +* **ci:** new pre-release 3.11.1a2 ([#6578](https://github.com/aws-powertools/powertools-lambda-python/issues/6578)) +* **ci:** new pre-release 3.11.1a4 ([#6589](https://github.com/aws-powertools/powertools-lambda-python/issues/6589)) +* **ci:** new pre-release 3.11.1a3 ([#6582](https://github.com/aws-powertools/powertools-lambda-python/issues/6582)) +* **deps:** bump pydantic from 2.11.3 to 2.11.4 ([#6585](https://github.com/aws-powertools/powertools-lambda-python/issues/6585)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.23 to 3.0.24 ([#6611](https://github.com/aws-powertools/powertools-lambda-python/issues/6611)) +* **deps-dev:** bump ruff from 0.11.7 to 0.11.8 ([#6595](https://github.com/aws-powertools/powertools-lambda-python/issues/6595)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.306 to 0.1.307 ([#6580](https://github.com/aws-powertools/powertools-lambda-python/issues/6580)) +* **deps-dev:** bump boto3-stubs from 1.38.4 to 1.38.5 ([#6581](https://github.com/aws-powertools/powertools-lambda-python/issues/6581)) +* **deps-dev:** bump aws-cdk from 2.1012.0 to 2.1013.0 ([#6588](https://github.com/aws-powertools/powertools-lambda-python/issues/6588)) +* **deps-dev:** bump boto3-stubs from 1.38.6 to 1.38.7 ([#6594](https://github.com/aws-powertools/powertools-lambda-python/issues/6594)) +* **deps-dev:** bump boto3-stubs from 1.38.3 to 1.38.4 ([#6577](https://github.com/aws-powertools/powertools-lambda-python/issues/6577)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.307 to 0.1.308 ([#6597](https://github.com/aws-powertools/powertools-lambda-python/issues/6597)) +* **deps-dev:** bump h11 from 0.14.0 to 0.16.0 ([#6575](https://github.com/aws-powertools/powertools-lambda-python/issues/6575)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.192.0a0 to 2.193.0a0 ([#6586](https://github.com/aws-powertools/powertools-lambda-python/issues/6586)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.193.0a0 to 2.194.0a0 ([#6602](https://github.com/aws-powertools/powertools-lambda-python/issues/6602)) +* **deps-dev:** bump boto3-stubs from 1.38.2 to 1.38.3 ([#6569](https://github.com/aws-powertools/powertools-lambda-python/issues/6569)) +* **deps-dev:** bump cfn-lint from 1.34.1 to 1.34.2 ([#6568](https://github.com/aws-powertools/powertools-lambda-python/issues/6568)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.305 to 0.1.306 ([#6567](https://github.com/aws-powertools/powertools-lambda-python/issues/6567)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.191.0a0 to 2.192.0a0 ([#6566](https://github.com/aws-powertools/powertools-lambda-python/issues/6566)) +* **deps-dev:** bump aws-cdk-lib from 2.191.0 to 2.192.0 ([#6565](https://github.com/aws-powertools/powertools-lambda-python/issues/6565)) +* **deps-dev:** bump aws-cdk-lib from 2.193.0 to 2.194.0 ([#6603](https://github.com/aws-powertools/powertools-lambda-python/issues/6603)) +* **deps-dev:** bump boto3-stubs from 1.38.7 to 1.38.9 ([#6612](https://github.com/aws-powertools/powertools-lambda-python/issues/6612)) +* **deps-dev:** bump boto3-stubs from 1.38.5 to 1.38.6 ([#6587](https://github.com/aws-powertools/powertools-lambda-python/issues/6587)) +* **docs:** fix youtube embed link in we made this ([#6593](https://github.com/aws-powertools/powertools-lambda-python/issues/6593)) + + +<a name="v3.11.0"></a> +## [v3.11.0] - 2025-04-24 +## Bug Fixes + +* **logger:** warn customers when the ALC log level is less verbose than log buffer ([#6509](https://github.com/aws-powertools/powertools-lambda-python/issues/6509)) +* **parser:** make key attribute optional in Kafka model ([#6523](https://github.com/aws-powertools/powertools-lambda-python/issues/6523)) + +## Code Refactoring + +* **batch:** use standard collections for types ([#6475](https://github.com/aws-powertools/powertools-lambda-python/issues/6475)) +* **data_masking:** use standard collections for types ([#6493](https://github.com/aws-powertools/powertools-lambda-python/issues/6493)) +* **e2e-tests:** use standard collections for types + refactor code ([#6505](https://github.com/aws-powertools/powertools-lambda-python/issues/6505)) +* **event_handler:** use standard collections for types + refactor code ([#6495](https://github.com/aws-powertools/powertools-lambda-python/issues/6495)) +* **event_source:** use standard collections for types ([#6479](https://github.com/aws-powertools/powertools-lambda-python/issues/6479)) +* **feature_flags:** use standard collections for type ([#6489](https://github.com/aws-powertools/powertools-lambda-python/issues/6489)) +* **general:** add support for `ruff format` ([#6512](https://github.com/aws-powertools/powertools-lambda-python/issues/6512)) +* **idempotency:** use standard collections for types ([#6487](https://github.com/aws-powertools/powertools-lambda-python/issues/6487)) +* **logger:** use standard collections for types ([#6471](https://github.com/aws-powertools/powertools-lambda-python/issues/6471)) +* **metrics:** use standard collections for types ([#6472](https://github.com/aws-powertools/powertools-lambda-python/issues/6472)) +* **middleware_factory:** use standard collections for types ([#6485](https://github.com/aws-powertools/powertools-lambda-python/issues/6485)) +* **parameters:** use standard collections for types ([#6481](https://github.com/aws-powertools/powertools-lambda-python/issues/6481)) +* **streaming:** use standard collections for types ([#6483](https://github.com/aws-powertools/powertools-lambda-python/issues/6483)) +* **tests:** use standard collections for types + refactor code ([#6497](https://github.com/aws-powertools/powertools-lambda-python/issues/6497)) +* **tracer:** use standard collections for types ([#6473](https://github.com/aws-powertools/powertools-lambda-python/issues/6473)) +* **validation:** use standard collections for types ([#6491](https://github.com/aws-powertools/powertools-lambda-python/issues/6491)) + +## Documentation + +* **bedrock:** fix BedrockServiceRole in template.yaml ([#6436](https://github.com/aws-powertools/powertools-lambda-python/issues/6436)) +* **bedrock_agents:** remove Pydantic v1 recommendation ([#6468](https://github.com/aws-powertools/powertools-lambda-python/issues/6468)) +* **event_handler:** add docs for AppSync event resolver ([#6557](https://github.com/aws-powertools/powertools-lambda-python/issues/6557)) +* **event_handler:** fix typo in api keys swagger url ([#6536](https://github.com/aws-powertools/powertools-lambda-python/issues/6536)) + +## Features + +* **bedrock:** add `openapi_extensions` in BedrockAgentResolver ([#6510](https://github.com/aws-powertools/powertools-lambda-python/issues/6510)) +* **data-masking:** add support for Pydantic models, dataclasses, and standard classes ([#6413](https://github.com/aws-powertools/powertools-lambda-python/issues/6413)) +* **event_handler:** add AppSync events resolver ([#6558](https://github.com/aws-powertools/powertools-lambda-python/issues/6558)) +* **event_handler:** add extras HTTP Error Code Exceptions ([#6454](https://github.com/aws-powertools/powertools-lambda-python/issues/6454)) +* **event_handler:** add route-level custom response validation in OpenAPI utility ([#6341](https://github.com/aws-powertools/powertools-lambda-python/issues/6341)) +* **logger:** add support for exception notes ([#6465](https://github.com/aws-powertools/powertools-lambda-python/issues/6465)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.10.1a7 ([#6518](https://github.com/aws-powertools/powertools-lambda-python/issues/6518)) +* **ci:** new pre-release 3.10.1a0 ([#6431](https://github.com/aws-powertools/powertools-lambda-python/issues/6431)) +* **ci:** new pre-release 3.10.1a1 ([#6437](https://github.com/aws-powertools/powertools-lambda-python/issues/6437)) +* **ci:** new pre-release 3.10.1a2 ([#6446](https://github.com/aws-powertools/powertools-lambda-python/issues/6446)) +* **ci:** new pre-release 3.10.1a10 ([#6538](https://github.com/aws-powertools/powertools-lambda-python/issues/6538)) +* **ci:** new pre-release 3.10.1a3 ([#6455](https://github.com/aws-powertools/powertools-lambda-python/issues/6455)) +* **ci:** new pre-release 3.10.1a4 ([#6463](https://github.com/aws-powertools/powertools-lambda-python/issues/6463)) +* **ci:** new pre-release 3.10.1a9 ([#6533](https://github.com/aws-powertools/powertools-lambda-python/issues/6533)) +* **ci:** new pre-release 3.10.1a5 ([#6498](https://github.com/aws-powertools/powertools-lambda-python/issues/6498)) +* **ci:** new pre-release 3.10.1a11 ([#6546](https://github.com/aws-powertools/powertools-lambda-python/issues/6546)) +* **ci:** new pre-release 3.10.1a8 ([#6526](https://github.com/aws-powertools/powertools-lambda-python/issues/6526)) +* **ci:** new pre-release 3.10.1a6 ([#6506](https://github.com/aws-powertools/powertools-lambda-python/issues/6506)) +* **deps:** bump pydantic-settings from 2.8.1 to 2.9.1 ([#6530](https://github.com/aws-powertools/powertools-lambda-python/issues/6530)) +* **deps:** bump pydantic from 2.11.2 to 2.11.3 ([#6427](https://github.com/aws-powertools/powertools-lambda-python/issues/6427)) +* **deps:** bump squidfunk/mkdocs-material from sha256:23b69789b1dd836c53ea25b32f62ef8e1a23366037acd07c90959a219fd1f285 to sha256:95f2ff42251979c043d6cb5b1c82e6ae8189e57e02105813dd1ce124021a418b in /docs ([#6513](https://github.com/aws-powertools/powertools-lambda-python/issues/6513)) +* **deps:** bump actions/download-artifact from 4.2.1 to 4.3.0 ([#6550](https://github.com/aws-powertools/powertools-lambda-python/issues/6550)) +* **deps:** bump actions/setup-python from 5.5.0 to 5.6.0 ([#6549](https://github.com/aws-powertools/powertools-lambda-python/issues/6549)) +* **deps:** bump typing-extensions from 4.13.1 to 4.13.2 ([#6451](https://github.com/aws-powertools/powertools-lambda-python/issues/6451)) +* **deps:** bump actions/setup-node from 4.3.0 to 4.4.0 ([#6457](https://github.com/aws-powertools/powertools-lambda-python/issues/6457)) +* **deps:** bump codecov/codecov-action from 5.4.0 to 5.4.2 ([#6458](https://github.com/aws-powertools/powertools-lambda-python/issues/6458)) +* **deps-dev:** bump mkdocs-material from 9.6.11 to 9.6.12 ([#6514](https://github.com/aws-powertools/powertools-lambda-python/issues/6514)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.302 to 0.1.304 ([#6531](https://github.com/aws-powertools/powertools-lambda-python/issues/6531)) +* **deps-dev:** bump sentry-sdk from 2.25.1 to 2.26.1 ([#6477](https://github.com/aws-powertools/powertools-lambda-python/issues/6477)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.189.1a0 to 2.190.0a0 ([#6529](https://github.com/aws-powertools/powertools-lambda-python/issues/6529)) +* **deps-dev:** bump boto3-stubs from 1.37.37 to 1.37.38 ([#6537](https://github.com/aws-powertools/powertools-lambda-python/issues/6537)) +* **deps-dev:** bump aws-cdk-lib from 2.189.0 to 2.189.1 ([#6461](https://github.com/aws-powertools/powertools-lambda-python/issues/6461)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.189.0a0 to 2.189.1a0 ([#6462](https://github.com/aws-powertools/powertools-lambda-python/issues/6462)) +* **deps-dev:** bump boto3-stubs from 1.37.33 to 1.37.34 ([#6459](https://github.com/aws-powertools/powertools-lambda-python/issues/6459)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.301 to 0.1.302 ([#6460](https://github.com/aws-powertools/powertools-lambda-python/issues/6460)) +* **deps-dev:** bump cfn-lint from 1.34.0 to 1.34.1 ([#6528](https://github.com/aws-powertools/powertools-lambda-python/issues/6528)) +* **deps-dev:** bump cfn-lint from 1.33.2 to 1.34.0 ([#6502](https://github.com/aws-powertools/powertools-lambda-python/issues/6502)) +* **deps-dev:** bump aws-cdk from 2.1010.0 to 2.1012.0 ([#6540](https://github.com/aws-powertools/powertools-lambda-python/issues/6540)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.37.0 to 1.38.0 in the boto-typing group ([#6541](https://github.com/aws-powertools/powertools-lambda-python/issues/6541)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.304 to 0.1.305 ([#6545](https://github.com/aws-powertools/powertools-lambda-python/issues/6545)) +* **deps-dev:** bump cfn-lint from 1.33.1 to 1.33.2 ([#6450](https://github.com/aws-powertools/powertools-lambda-python/issues/6450)) +* **deps-dev:** bump boto3-stubs from 1.37.31 to 1.37.33 ([#6449](https://github.com/aws-powertools/powertools-lambda-python/issues/6449)) +* **deps-dev:** bump boto3-stubs from 1.37.35 to 1.37.37 ([#6521](https://github.com/aws-powertools/powertools-lambda-python/issues/6521)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.190.0a0 to 2.191.0a0 ([#6543](https://github.com/aws-powertools/powertools-lambda-python/issues/6543)) +* **deps-dev:** bump h11 from 0.14.0 to 0.16.0 ([#6548](https://github.com/aws-powertools/powertools-lambda-python/issues/6548)) +* **deps-dev:** bump ruff from 0.11.4 to 0.11.5 ([#6443](https://github.com/aws-powertools/powertools-lambda-python/issues/6443)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.188.0a0 to 2.189.0a0 ([#6444](https://github.com/aws-powertools/powertools-lambda-python/issues/6444)) +* **deps-dev:** bump aws-cdk-lib from 2.188.0 to 2.189.0 ([#6445](https://github.com/aws-powertools/powertools-lambda-python/issues/6445)) +* **deps-dev:** bump cfn-lint from 1.33.0 to 1.33.1 ([#6442](https://github.com/aws-powertools/powertools-lambda-python/issues/6442)) +* **deps-dev:** bump ruff from 0.11.5 to 0.11.6 ([#6515](https://github.com/aws-powertools/powertools-lambda-python/issues/6515)) +* **deps-dev:** bump aws-cdk from 2.1007.0 to 2.1010.0 ([#6501](https://github.com/aws-powertools/powertools-lambda-python/issues/6501)) +* **deps-dev:** bump httpx from 0.25.1 to 0.28.1 ([#6554](https://github.com/aws-powertools/powertools-lambda-python/issues/6554)) +* **deps-dev:** bump boto3-stubs from 1.38.1 to 1.38.2 ([#6556](https://github.com/aws-powertools/powertools-lambda-python/issues/6556)) +* **deps-dev:** bump boto3-stubs from 1.37.29 to 1.37.31 ([#6433](https://github.com/aws-powertools/powertools-lambda-python/issues/6433)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.187.0a0 to 2.188.0a0 ([#6434](https://github.com/aws-powertools/powertools-lambda-python/issues/6434)) +* **deps-dev:** bump ruff from 0.11.3 to 0.11.4 ([#6428](https://github.com/aws-powertools/powertools-lambda-python/issues/6428)) +* **deps-dev:** bump pytest-cov from 6.1.0 to 6.1.1 ([#6429](https://github.com/aws-powertools/powertools-lambda-python/issues/6429)) +* **deps-dev:** bump cfn-lint from 1.32.4 to 1.33.0 ([#6430](https://github.com/aws-powertools/powertools-lambda-python/issues/6430)) +* **deps-dev:** bump multiprocess from 0.70.17 to 0.70.18 ([#6516](https://github.com/aws-powertools/powertools-lambda-python/issues/6516)) +* **deps-dev:** bump ruff from 0.11.6 to 0.11.7 ([#6555](https://github.com/aws-powertools/powertools-lambda-python/issues/6555)) +* **deps-dev:** bump sentry-sdk from 2.26.1 to 2.27.0 ([#6553](https://github.com/aws-powertools/powertools-lambda-python/issues/6553)) +* **deps-dev:** bump boto3-stubs from 1.37.34 to 1.37.35 ([#6504](https://github.com/aws-powertools/powertools-lambda-python/issues/6504)) + + +<a name="v3.10.0"></a> +## [v3.10.0] - 2025-04-08 +## Bug Fixes + +* **event_source:** Added missing properties in APIGatewayWebSocketEvent class ([#6411](https://github.com/aws-powertools/powertools-lambda-python/issues/6411)) +* **event_source:** fix HomeDirectoryDetails type in TransferFamilyAuthorizerResponse method ([#6403](https://github.com/aws-powertools/powertools-lambda-python/issues/6403)) +* **logger:** improve behavior with `exc_info=True` to prevent errors ([#6417](https://github.com/aws-powertools/powertools-lambda-python/issues/6417)) + +## Documentation + +* **homepage:** add SAR documentation ([#6347](https://github.com/aws-powertools/powertools-lambda-python/issues/6347)) + +## Features + +* **parser:** add AppSyncResolver model ([#6400](https://github.com/aws-powertools/powertools-lambda-python/issues/6400)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.9.1a4 ([#6377](https://github.com/aws-powertools/powertools-lambda-python/issues/6377)) +* **ci:** new pre-release 3.9.1a8 ([#6415](https://github.com/aws-powertools/powertools-lambda-python/issues/6415)) +* **ci:** new pre-release 3.9.1a9 ([#6422](https://github.com/aws-powertools/powertools-lambda-python/issues/6422)) +* **ci:** new pre-release 3.9.1a0 ([#6354](https://github.com/aws-powertools/powertools-lambda-python/issues/6354)) +* **ci:** new pre-release 3.9.1a5 ([#6385](https://github.com/aws-powertools/powertools-lambda-python/issues/6385)) +* **ci:** new pre-release 3.9.1a7 ([#6401](https://github.com/aws-powertools/powertools-lambda-python/issues/6401)) +* **ci:** new pre-release 3.9.1a1 ([#6356](https://github.com/aws-powertools/powertools-lambda-python/issues/6356)) +* **ci:** new pre-release 3.9.1a2 ([#6364](https://github.com/aws-powertools/powertools-lambda-python/issues/6364)) +* **ci:** new pre-release 3.9.1a6 ([#6392](https://github.com/aws-powertools/powertools-lambda-python/issues/6392)) +* **ci:** new pre-release 3.9.1a3 ([#6369](https://github.com/aws-powertools/powertools-lambda-python/issues/6369)) +* **deps:** bump aws-encryption-sdk from 4.0.0 to 4.0.1 ([#6360](https://github.com/aws-powertools/powertools-lambda-python/issues/6360)) +* **deps:** bump pydantic from 2.11.1 to 2.11.2 ([#6395](https://github.com/aws-powertools/powertools-lambda-python/issues/6395)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.22 to 3.0.23 ([#6371](https://github.com/aws-powertools/powertools-lambda-python/issues/6371)) +* **deps:** bump squidfunk/mkdocs-material from `3555052` to `23b6978` in /docs ([#6404](https://github.com/aws-powertools/powertools-lambda-python/issues/6404)) +* **deps:** bump datadog-lambda from 6.106.0 to 6.107.0 ([#6405](https://github.com/aws-powertools/powertools-lambda-python/issues/6405)) +* **deps:** bump squidfunk/mkdocs-material from `f226a2d` to `3555052` in /docs ([#6372](https://github.com/aws-powertools/powertools-lambda-python/issues/6372)) +* **deps:** bump pydantic from 2.10.6 to 2.11.1 ([#6383](https://github.com/aws-powertools/powertools-lambda-python/issues/6383)) +* **deps:** bump typing-extensions from 4.12.2 to 4.13.1 ([#6418](https://github.com/aws-powertools/powertools-lambda-python/issues/6418)) +* **deps:** bump actions/setup-python from 5.4.0 to 5.5.0 ([#6349](https://github.com/aws-powertools/powertools-lambda-python/issues/6349)) +* **deps:** bump actions/dependency-review-action from 4.5.0 to 4.6.0 ([#6380](https://github.com/aws-powertools/powertools-lambda-python/issues/6380)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.186.0a0 to 2.187.0a0 ([#6382](https://github.com/aws-powertools/powertools-lambda-python/issues/6382)) +* **deps-dev:** bump pytest-cov from 6.0.0 to 6.1.0 ([#6381](https://github.com/aws-powertools/powertools-lambda-python/issues/6381)) +* **deps-dev:** bump coverage from 7.7.1 to 7.8.0 ([#6376](https://github.com/aws-powertools/powertools-lambda-python/issues/6376)) +* **deps-dev:** bump mkdocs-material from 9.6.9 to 9.6.10 ([#6375](https://github.com/aws-powertools/powertools-lambda-python/issues/6375)) +* **deps-dev:** bump boto3-stubs from 1.37.23 to 1.37.24 ([#6374](https://github.com/aws-powertools/powertools-lambda-python/issues/6374)) +* **deps-dev:** bump boto3-stubs from 1.37.24 to 1.37.25 ([#6384](https://github.com/aws-powertools/powertools-lambda-python/issues/6384)) +* **deps-dev:** bump sentry-sdk from 2.24.1 to 2.25.0 ([#6373](https://github.com/aws-powertools/powertools-lambda-python/issues/6373)) +* **deps-dev:** bump aws-cdk from 2.1006.0 to 2.1007.0 ([#6387](https://github.com/aws-powertools/powertools-lambda-python/issues/6387)) +* **deps-dev:** bump boto3-stubs from 1.37.25 to 1.37.26 ([#6389](https://github.com/aws-powertools/powertools-lambda-python/issues/6389)) +* **deps-dev:** bump sentry-sdk from 2.25.0 to 2.25.1 ([#6391](https://github.com/aws-powertools/powertools-lambda-python/issues/6391)) +* **deps-dev:** bump boto3-stubs from 1.37.22 to 1.37.23 ([#6366](https://github.com/aws-powertools/powertools-lambda-python/issues/6366)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.298 to 0.1.299 ([#6390](https://github.com/aws-powertools/powertools-lambda-python/issues/6390)) +* **deps-dev:** bump cfn-lint from 1.32.1 to 1.32.3 ([#6388](https://github.com/aws-powertools/powertools-lambda-python/issues/6388)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.185.0a0 to 2.186.0a0 ([#6363](https://github.com/aws-powertools/powertools-lambda-python/issues/6363)) +* **deps-dev:** bump boto3-stubs from 1.37.20 to 1.37.22 ([#6362](https://github.com/aws-powertools/powertools-lambda-python/issues/6362)) +* **deps-dev:** bump testcontainers from 4.9.2 to 4.10.0 ([#6397](https://github.com/aws-powertools/powertools-lambda-python/issues/6397)) +* **deps-dev:** bump mkdocstrings-python from 1.16.8 to 1.16.10 ([#6399](https://github.com/aws-powertools/powertools-lambda-python/issues/6399)) +* **deps-dev:** bump ruff from 0.11.2 to 0.11.3 ([#6398](https://github.com/aws-powertools/powertools-lambda-python/issues/6398)) +* **deps-dev:** bump boto3-stubs from 1.37.26 to 1.37.28 ([#6406](https://github.com/aws-powertools/powertools-lambda-python/issues/6406)) +* **deps-dev:** bump pytest-asyncio from 0.25.3 to 0.26.0 ([#6352](https://github.com/aws-powertools/powertools-lambda-python/issues/6352)) +* **deps-dev:** bump aws-cdk-lib from 2.187.0 to 2.188.0 ([#6407](https://github.com/aws-powertools/powertools-lambda-python/issues/6407)) +* **deps-dev:** bump cfn-lint from 1.32.0 to 1.32.1 ([#6351](https://github.com/aws-powertools/powertools-lambda-python/issues/6351)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.299 to 0.1.300 ([#6408](https://github.com/aws-powertools/powertools-lambda-python/issues/6408)) +* **deps-dev:** bump aws-cdk from 2.1005.0 to 2.1006.0 ([#6350](https://github.com/aws-powertools/powertools-lambda-python/issues/6350)) +* **deps-dev:** bump cfn-lint from 1.32.3 to 1.32.4 ([#6419](https://github.com/aws-powertools/powertools-lambda-python/issues/6419)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.300 to 0.1.301 ([#6420](https://github.com/aws-powertools/powertools-lambda-python/issues/6420)) +* **deps-dev:** bump boto3-stubs from 1.37.28 to 1.37.29 ([#6421](https://github.com/aws-powertools/powertools-lambda-python/issues/6421)) +* **deps-dev:** bump boto3-stubs from 1.37.19 to 1.37.20 ([#6353](https://github.com/aws-powertools/powertools-lambda-python/issues/6353)) + + +<a name="v3.9.0"></a> +## [v3.9.0] - 2025-03-25 +## Bug Fixes + +* **idempotency:** include sk in error msgs when using composite key ([#6325](https://github.com/aws-powertools/powertools-lambda-python/issues/6325)) +* **metrics:** ensure proper type conversion for `DD_FLUSH_TO_LOG` env var ([#6280](https://github.com/aws-powertools/powertools-lambda-python/issues/6280)) + +## Code Refactoring + +* **data_classes:** Add base class with common code ([#6297](https://github.com/aws-powertools/powertools-lambda-python/issues/6297)) +* **data_classes:** remove duplicated code ([#6288](https://github.com/aws-powertools/powertools-lambda-python/issues/6288)) +* **data_classes:** simplify nested data classes ([#6289](https://github.com/aws-powertools/powertools-lambda-python/issues/6289)) +* **tests:** add LambdaContext type in tests ([#6214](https://github.com/aws-powertools/powertools-lambda-python/issues/6214)) + +## Documentation + +* **homepage:** update layer instructions link ([#6242](https://github.com/aws-powertools/powertools-lambda-python/issues/6242)) +* **public_reference:** add Guild as a public reference ([#6342](https://github.com/aws-powertools/powertools-lambda-python/issues/6342)) + +## Features + +* **data_classes:** add API Gateway Websocket event ([#6287](https://github.com/aws-powertools/powertools-lambda-python/issues/6287)) +* **event_handler:** add custom method for OpenAPI configuration ([#6204](https://github.com/aws-powertools/powertools-lambda-python/issues/6204)) +* **event_handler:** add custom response validation in OpenAPI utility ([#6189](https://github.com/aws-powertools/powertools-lambda-python/issues/6189)) +* **general:** make logger, tracer and metrics utilities aware of provisioned concurrency ([#6324](https://github.com/aws-powertools/powertools-lambda-python/issues/6324)) +* **metrics:** allow change ColdStart function_name dimension ([#6315](https://github.com/aws-powertools/powertools-lambda-python/issues/6315)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.8.1a8 ([#6307](https://github.com/aws-powertools/powertools-lambda-python/issues/6307)) +* **ci:** new pre-release 3.8.1a11 ([#6340](https://github.com/aws-powertools/powertools-lambda-python/issues/6340)) +* **ci:** new pre-release 3.8.1a0 ([#6244](https://github.com/aws-powertools/powertools-lambda-python/issues/6244)) +* **ci:** new pre-release 3.8.1a10 ([#6332](https://github.com/aws-powertools/powertools-lambda-python/issues/6332)) +* **ci:** new pre-release 3.8.1a1 ([#6250](https://github.com/aws-powertools/powertools-lambda-python/issues/6250)) +* **ci:** new pre-release 3.8.1a2 ([#6253](https://github.com/aws-powertools/powertools-lambda-python/issues/6253)) +* **ci:** new pre-release 3.8.1a9 ([#6322](https://github.com/aws-powertools/powertools-lambda-python/issues/6322)) +* **ci:** new pre-release 3.8.1a3 ([#6259](https://github.com/aws-powertools/powertools-lambda-python/issues/6259)) +* **ci:** new pre-release 3.8.1a4 ([#6268](https://github.com/aws-powertools/powertools-lambda-python/issues/6268)) +* **ci:** Fix SAR pipeline ([#6313](https://github.com/aws-powertools/powertools-lambda-python/issues/6313)) +* **ci:** new pre-release 3.8.1a5 ([#6276](https://github.com/aws-powertools/powertools-lambda-python/issues/6276)) +* **ci:** new pre-release 3.8.1a6 ([#6290](https://github.com/aws-powertools/powertools-lambda-python/issues/6290)) +* **ci:** new pre-release 3.8.1a7 ([#6298](https://github.com/aws-powertools/powertools-lambda-python/issues/6298)) +* **deps:** bump actions/setup-go from 5.3.0 to 5.4.0 ([#6304](https://github.com/aws-powertools/powertools-lambda-python/issues/6304)) +* **deps:** bump actions/upload-artifact from 4.6.1 to 4.6.2 ([#6302](https://github.com/aws-powertools/powertools-lambda-python/issues/6302)) +* **deps:** bump squidfunk/mkdocs-material from `047452c` to `479a06a` in /docs ([#6261](https://github.com/aws-powertools/powertools-lambda-python/issues/6261)) +* **deps:** bump squidfunk/mkdocs-material from `479a06a` to `f226a2d` in /docs ([#6279](https://github.com/aws-powertools/powertools-lambda-python/issues/6279)) +* **deps:** bump actions/download-artifact from 4.1.9 to 4.2.0 ([#6294](https://github.com/aws-powertools/powertools-lambda-python/issues/6294)) +* **deps:** bump actions/download-artifact from 4.2.0 to 4.2.1 ([#6303](https://github.com/aws-powertools/powertools-lambda-python/issues/6303)) +* **deps:** bump actions/setup-node from 4.2.0 to 4.3.0 ([#6278](https://github.com/aws-powertools/powertools-lambda-python/issues/6278)) +* **deps-dev:** bump mkdocs-material from 9.6.7 to 9.6.8 ([#6264](https://github.com/aws-powertools/powertools-lambda-python/issues/6264)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.296 to 0.1.297 ([#6281](https://github.com/aws-powertools/powertools-lambda-python/issues/6281)) +* **deps-dev:** bump boto3-stubs from 1.37.12 to 1.37.14 ([#6282](https://github.com/aws-powertools/powertools-lambda-python/issues/6282)) +* **deps-dev:** bump aws-cdk from 2.1004.0 to 2.1005.0 ([#6301](https://github.com/aws-powertools/powertools-lambda-python/issues/6301)) +* **deps-dev:** bump boto3-stubs from 1.37.15 to 1.37.16 ([#6305](https://github.com/aws-powertools/powertools-lambda-python/issues/6305)) +* **deps-dev:** bump mkdocs-material from 9.6.8 to 9.6.9 ([#6285](https://github.com/aws-powertools/powertools-lambda-python/issues/6285)) +* **deps-dev:** bump cfn-lint from 1.31.0 to 1.31.3 ([#6306](https://github.com/aws-powertools/powertools-lambda-python/issues/6306)) +* **deps-dev:** bump ruff from 0.9.10 to 0.11.0 ([#6273](https://github.com/aws-powertools/powertools-lambda-python/issues/6273)) +* **deps-dev:** bump sentry-sdk from 2.24.0 to 2.24.1 ([#6339](https://github.com/aws-powertools/powertools-lambda-python/issues/6339)) +* **deps-dev:** bump aws-cdk-lib from 2.183.0 to 2.184.1 ([#6272](https://github.com/aws-powertools/powertools-lambda-python/issues/6272)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.183.0a0 to 2.184.1a0 ([#6271](https://github.com/aws-powertools/powertools-lambda-python/issues/6271)) +* **deps-dev:** bump filelock from 3.17.0 to 3.18.0 ([#6270](https://github.com/aws-powertools/powertools-lambda-python/issues/6270)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.184.1a0 to 2.185.0a0 ([#6317](https://github.com/aws-powertools/powertools-lambda-python/issues/6317)) +* **deps-dev:** bump boto3-stubs from 1.37.11 to 1.37.12 ([#6266](https://github.com/aws-powertools/powertools-lambda-python/issues/6266)) +* **deps-dev:** bump cfn-lint from 1.31.3 to 1.32.0 ([#6316](https://github.com/aws-powertools/powertools-lambda-python/issues/6316)) +* **deps-dev:** bump cfn-lint from 1.30.0 to 1.31.0 ([#6296](https://github.com/aws-powertools/powertools-lambda-python/issues/6296)) +* **deps-dev:** bump cfn-lint from 1.29.1 to 1.30.0 ([#6263](https://github.com/aws-powertools/powertools-lambda-python/issues/6263)) +* **deps-dev:** bump aws-cdk from 2.1003.0 to 2.1004.0 ([#6262](https://github.com/aws-powertools/powertools-lambda-python/issues/6262)) +* **deps-dev:** bump boto3-stubs from 1.37.14 to 1.37.15 ([#6295](https://github.com/aws-powertools/powertools-lambda-python/issues/6295)) +* **deps-dev:** bump boto3-stubs from 1.37.8 to 1.37.10 ([#6248](https://github.com/aws-powertools/powertools-lambda-python/issues/6248)) +* **deps-dev:** bump mkdocstrings-python from 1.16.6 to 1.16.7 ([#6319](https://github.com/aws-powertools/powertools-lambda-python/issues/6319)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.182.0a0 to 2.183.0a0 ([#6258](https://github.com/aws-powertools/powertools-lambda-python/issues/6258)) +* **deps-dev:** bump aws-cdk-lib from 2.182.0 to 2.183.0 ([#6257](https://github.com/aws-powertools/powertools-lambda-python/issues/6257)) +* **deps-dev:** bump ruff from 0.11.0 to 0.11.1 ([#6320](https://github.com/aws-powertools/powertools-lambda-python/issues/6320)) +* **deps-dev:** bump ruff from 0.11.1 to 0.11.2 ([#6326](https://github.com/aws-powertools/powertools-lambda-python/issues/6326)) +* **deps-dev:** bump boto3-stubs from 1.37.10 to 1.37.11 ([#6252](https://github.com/aws-powertools/powertools-lambda-python/issues/6252)) +* **deps-dev:** bump coverage from 7.7.0 to 7.7.1 ([#6328](https://github.com/aws-powertools/powertools-lambda-python/issues/6328)) +* **deps-dev:** bump cfn-lint from 1.28.0 to 1.29.1 ([#6249](https://github.com/aws-powertools/powertools-lambda-python/issues/6249)) +* **deps-dev:** bump boto3-stubs from 1.37.16 to 1.37.18 ([#6327](https://github.com/aws-powertools/powertools-lambda-python/issues/6327)) +* **deps-dev:** bump sentry-sdk from 2.23.1 to 2.24.0 ([#6329](https://github.com/aws-powertools/powertools-lambda-python/issues/6329)) +* **deps-dev:** bump boto3-stubs from 1.37.18 to 1.37.19 ([#6337](https://github.com/aws-powertools/powertools-lambda-python/issues/6337)) +* **deps-dev:** bump mkdocstrings-python from 1.16.7 to 1.16.8 ([#6338](https://github.com/aws-powertools/powertools-lambda-python/issues/6338)) +* **deps-dev:** bump ruff from 0.9.9 to 0.9.10 ([#6241](https://github.com/aws-powertools/powertools-lambda-python/issues/6241)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.295 to 0.1.296 ([#6240](https://github.com/aws-powertools/powertools-lambda-python/issues/6240)) +* **deps-dev:** bump boto3-stubs from 1.37.7 to 1.37.8 ([#6239](https://github.com/aws-powertools/powertools-lambda-python/issues/6239)) +* **deps-dev:** bump coverage from 7.6.12 to 7.7.0 ([#6284](https://github.com/aws-powertools/powertools-lambda-python/issues/6284)) +* **documentation:** v2 end of support ([#6343](https://github.com/aws-powertools/powertools-lambda-python/issues/6343)) +* **logger:** clear prev request buffers in manual mode ([#6314](https://github.com/aws-powertools/powertools-lambda-python/issues/6314)) + + +<a name="v3.8.0"></a> +## [v3.8.0] - 2025-03-07 +## Bug Fixes + +* **event_handler:** revert regression when validating response ([#6234](https://github.com/aws-powertools/powertools-lambda-python/issues/6234)) + +## Code Refactoring + +* **tracer:** fix capture_lambda_handler return type annotation ([#6197](https://github.com/aws-powertools/powertools-lambda-python/issues/6197)) + +## Documentation + +* **layer:** Fix SSM parameter name for looking up layer ARN ([#6221](https://github.com/aws-powertools/powertools-lambda-python/issues/6221)) + +## Features + +* **logger:** add logger buffer feature ([#6060](https://github.com/aws-powertools/powertools-lambda-python/issues/6060)) +* **logger:** add new logic to sample debug logs ([#6142](https://github.com/aws-powertools/powertools-lambda-python/issues/6142)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.7.1a2 ([#6186](https://github.com/aws-powertools/powertools-lambda-python/issues/6186)) +* **ci:** new pre-release 3.7.1a0 ([#6166](https://github.com/aws-powertools/powertools-lambda-python/issues/6166)) +* **ci:** new pre-release 3.7.1a6 ([#6229](https://github.com/aws-powertools/powertools-lambda-python/issues/6229)) +* **ci:** new pre-release 3.7.1a7 ([#6233](https://github.com/aws-powertools/powertools-lambda-python/issues/6233)) +* **ci:** new pre-release 3.7.1a1 ([#6178](https://github.com/aws-powertools/powertools-lambda-python/issues/6178)) +* **ci:** enable SAR deployment ([#6104](https://github.com/aws-powertools/powertools-lambda-python/issues/6104)) +* **ci:** new pre-release 3.7.1a5 ([#6219](https://github.com/aws-powertools/powertools-lambda-python/issues/6219)) +* **ci:** new pre-release 3.7.1a3 ([#6201](https://github.com/aws-powertools/powertools-lambda-python/issues/6201)) +* **ci:** new pre-release 3.7.1a4 ([#6211](https://github.com/aws-powertools/powertools-lambda-python/issues/6211)) +* **deps:** bump docker/setup-qemu-action from 3.5.0 to 3.6.0 ([#6190](https://github.com/aws-powertools/powertools-lambda-python/issues/6190)) +* **deps:** bump actions/download-artifact from 4.1.8 to 4.1.9 ([#6174](https://github.com/aws-powertools/powertools-lambda-python/issues/6174)) +* **deps:** bump squidfunk/mkdocs-material from `2615302` to `047452c` in /docs ([#6210](https://github.com/aws-powertools/powertools-lambda-python/issues/6210)) +* **deps:** bump docker/setup-qemu-action from 3.4.0 to 3.5.0 ([#6176](https://github.com/aws-powertools/powertools-lambda-python/issues/6176)) +* **deps:** bump docker/setup-buildx-action from 3.9.0 to 3.10.0 ([#6175](https://github.com/aws-powertools/powertools-lambda-python/issues/6175)) +* **deps:** bump datadog-lambda from 6.105.0 to 6.106.0 ([#6218](https://github.com/aws-powertools/powertools-lambda-python/issues/6218)) +* **deps:** bump codecov/codecov-action from 5.3.1 to 5.4.0 ([#6180](https://github.com/aws-powertools/powertools-lambda-python/issues/6180)) +* **deps:** bump pydantic-settings from 2.8.0 to 2.8.1 ([#6182](https://github.com/aws-powertools/powertools-lambda-python/issues/6182)) +* **deps:** bump jinja2 from 3.1.5 to 3.1.6 in /docs ([#6223](https://github.com/aws-powertools/powertools-lambda-python/issues/6223)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.294 to 0.1.295 ([#6207](https://github.com/aws-powertools/powertools-lambda-python/issues/6207)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.293 to 0.1.294 ([#6193](https://github.com/aws-powertools/powertools-lambda-python/issues/6193)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.181.0a0 to 2.181.1a0 ([#6194](https://github.com/aws-powertools/powertools-lambda-python/issues/6194)) +* **deps-dev:** bump ruff from 0.9.8 to 0.9.9 ([#6195](https://github.com/aws-powertools/powertools-lambda-python/issues/6195)) +* **deps-dev:** bump aws-cdk-lib from 2.181.1 to 2.182.0 ([#6222](https://github.com/aws-powertools/powertools-lambda-python/issues/6222)) +* **deps-dev:** bump testcontainers from 4.9.1 to 4.9.2 ([#6225](https://github.com/aws-powertools/powertools-lambda-python/issues/6225)) +* **deps-dev:** bump cfn-lint from 1.26.1 to 1.27.0 ([#6192](https://github.com/aws-powertools/powertools-lambda-python/issues/6192)) +* **deps-dev:** bump boto3-stubs from 1.37.2 to 1.37.3 ([#6181](https://github.com/aws-powertools/powertools-lambda-python/issues/6181)) +* **deps-dev:** bump isort from 6.0.0 to 6.0.1 ([#6183](https://github.com/aws-powertools/powertools-lambda-python/issues/6183)) +* **deps-dev:** bump boto3-stubs from 1.37.5 to 1.37.6 ([#6227](https://github.com/aws-powertools/powertools-lambda-python/issues/6227)) +* **deps-dev:** bump ruff from 0.9.7 to 0.9.8 ([#6184](https://github.com/aws-powertools/powertools-lambda-python/issues/6184)) +* **deps-dev:** bump boto3-stubs from 1.37.4 to 1.37.5 ([#6217](https://github.com/aws-powertools/powertools-lambda-python/issues/6217)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.181.1a0 to 2.182.0a0 ([#6226](https://github.com/aws-powertools/powertools-lambda-python/issues/6226)) +* **deps-dev:** bump cfn-lint from 1.27.0 to 1.28.0 ([#6228](https://github.com/aws-powertools/powertools-lambda-python/issues/6228)) +* **deps-dev:** bump pytest from 8.3.4 to 8.3.5 ([#6206](https://github.com/aws-powertools/powertools-lambda-python/issues/6206)) +* **deps-dev:** bump boto3-stubs from 1.37.0 to 1.37.1 ([#6170](https://github.com/aws-powertools/powertools-lambda-python/issues/6170)) +* **deps-dev:** bump boto3-stubs from 1.37.3 to 1.37.4 ([#6205](https://github.com/aws-powertools/powertools-lambda-python/issues/6205)) +* **deps-dev:** bump mkdocs-material from 9.6.5 to 9.6.7 ([#6208](https://github.com/aws-powertools/powertools-lambda-python/issues/6208)) +* **deps-dev:** bump aws-cdk from 2.1000.3 to 2.1001.0 ([#6173](https://github.com/aws-powertools/powertools-lambda-python/issues/6173)) +* **deps-dev:** bump cfn-lint from 1.26.0 to 1.26.1 ([#6169](https://github.com/aws-powertools/powertools-lambda-python/issues/6169)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.180.0a0 to 2.181.0a0 ([#6172](https://github.com/aws-powertools/powertools-lambda-python/issues/6172)) +* **deps-dev:** bump jinja2 from 3.1.5 to 3.1.6 ([#6224](https://github.com/aws-powertools/powertools-lambda-python/issues/6224)) +* **deps-dev:** bump aws-cdk from 2.1002.0 to 2.1003.0 ([#6232](https://github.com/aws-powertools/powertools-lambda-python/issues/6232)) +* **deps-dev:** bump cfn-lint from 1.25.1 to 1.26.0 ([#6164](https://github.com/aws-powertools/powertools-lambda-python/issues/6164)) +* **deps-dev:** bump boto3-stubs from 1.36.26 to 1.37.0 ([#6165](https://github.com/aws-powertools/powertools-lambda-python/issues/6165)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.36.0 to 1.37.0 in the boto-typing group ([#6163](https://github.com/aws-powertools/powertools-lambda-python/issues/6163)) +* **deps-dev:** bump aws-cdk from 2.1000.2 to 2.1000.3 ([#6162](https://github.com/aws-powertools/powertools-lambda-python/issues/6162)) +* **deps-dev:** bump boto3-stubs from 1.37.6 to 1.37.7 ([#6231](https://github.com/aws-powertools/powertools-lambda-python/issues/6231)) +* **deps-dev:** bump aws-cdk from 2.1001.0 to 2.1002.0 ([#6209](https://github.com/aws-powertools/powertools-lambda-python/issues/6209)) + + +<a name="v3.7.0"></a> +## [v3.7.0] - 2025-02-25 +## Bug Fixes + +* **logger:** correctly pick powertools or custom handler in custom environments ([#6083](https://github.com/aws-powertools/powertools-lambda-python/issues/6083)) +* **openapi:** validate response serialization when falsy ([#6119](https://github.com/aws-powertools/powertools-lambda-python/issues/6119)) +* **parser:** fix data types for `sourceIPAddress` and `sequencer` fields in S3RecordModel Model ([#6154](https://github.com/aws-powertools/powertools-lambda-python/issues/6154)) +* **parser:** fix EventBridgeModel when working with scheduled events ([#6134](https://github.com/aws-powertools/powertools-lambda-python/issues/6134)) +* **security:** fix encryption_context handling in data masking operations ([#6074](https://github.com/aws-powertools/powertools-lambda-python/issues/6074)) + +## Documentation + +* **roadmap:** update roadmap ([#6077](https://github.com/aws-powertools/powertools-lambda-python/issues/6077)) + +## Features + +* **batch:** raise exception for invalid batch event ([#6088](https://github.com/aws-powertools/powertools-lambda-python/issues/6088)) +* **event_handler:** add support for defining OpenAPI examples in parameters ([#6086](https://github.com/aws-powertools/powertools-lambda-python/issues/6086)) +* **layers:** add new comercial region ap-southeast-7 and mx-central-1 ([#6109](https://github.com/aws-powertools/powertools-lambda-python/issues/6109)) +* **parser:** Event source dataclasses for IoT Core Registry Events ([#6123](https://github.com/aws-powertools/powertools-lambda-python/issues/6123)) +* **parser:** Add IoT registry events models ([#5892](https://github.com/aws-powertools/powertools-lambda-python/issues/5892)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.6.1a9 ([#6157](https://github.com/aws-powertools/powertools-lambda-python/issues/6157)) +* **ci:** new pre-release 3.6.1a8 ([#6152](https://github.com/aws-powertools/powertools-lambda-python/issues/6152)) +* **ci:** new pre-release 3.6.1a4 ([#6120](https://github.com/aws-powertools/powertools-lambda-python/issues/6120)) +* **ci:** new pre-release 3.6.1a3 ([#6107](https://github.com/aws-powertools/powertools-lambda-python/issues/6107)) +* **ci:** new pre-release 3.6.1a0 ([#6084](https://github.com/aws-powertools/powertools-lambda-python/issues/6084)) +* **ci:** new pre-release 3.6.1a5 ([#6124](https://github.com/aws-powertools/powertools-lambda-python/issues/6124)) +* **ci:** new pre-release 3.6.1a7 ([#6139](https://github.com/aws-powertools/powertools-lambda-python/issues/6139)) +* **ci:** new pre-release 3.6.1a1 ([#6090](https://github.com/aws-powertools/powertools-lambda-python/issues/6090)) +* **ci:** new pre-release 3.6.1a6 ([#6132](https://github.com/aws-powertools/powertools-lambda-python/issues/6132)) +* **ci:** new pre-release 3.6.1a2 ([#6098](https://github.com/aws-powertools/powertools-lambda-python/issues/6098)) +* **ci:** remove python3.8 runtime when bootstrapping a new region ([#6101](https://github.com/aws-powertools/powertools-lambda-python/issues/6101)) +* **deps:** bump squidfunk/mkdocs-material from `f5bcec4` to `2615302` in /docs ([#6135](https://github.com/aws-powertools/powertools-lambda-python/issues/6135)) +* **deps:** bump squidfunk/mkdocs-material from `c62453b` to `f5bcec4` in /docs ([#6087](https://github.com/aws-powertools/powertools-lambda-python/issues/6087)) +* **deps:** bump actions/upload-artifact from 4.6.0 to 4.6.1 ([#6144](https://github.com/aws-powertools/powertools-lambda-python/issues/6144)) +* **deps:** bump aws-actions/configure-aws-credentials from 4.0.3 to 4.1.0 ([#6082](https://github.com/aws-powertools/powertools-lambda-python/issues/6082)) +* **deps:** bump pydantic-settings from 2.7.1 to 2.8.0 ([#6147](https://github.com/aws-powertools/powertools-lambda-python/issues/6147)) +* **deps:** bump ossf/scorecard-action from 2.4.0 to 2.4.1 ([#6143](https://github.com/aws-powertools/powertools-lambda-python/issues/6143)) +* **deps:** bump slsa-framework/slsa-github-generator from 2.0.0 to 2.1.0 ([#6155](https://github.com/aws-powertools/powertools-lambda-python/issues/6155)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.21 to 3.0.22 ([#6113](https://github.com/aws-powertools/powertools-lambda-python/issues/6113)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.292 to 0.1.293 ([#6129](https://github.com/aws-powertools/powertools-lambda-python/issues/6129)) +* **deps-dev:** bump sentry-sdk from 2.21.0 to 2.22.0 ([#6114](https://github.com/aws-powertools/powertools-lambda-python/issues/6114)) +* **deps-dev:** bump bandit from 1.8.2 to 1.8.3 ([#6117](https://github.com/aws-powertools/powertools-lambda-python/issues/6117)) +* **deps-dev:** bump mkdocstrings-python from 1.15.0 to 1.16.0 ([#6118](https://github.com/aws-powertools/powertools-lambda-python/issues/6118)) +* **deps-dev:** bump boto3-stubs from 1.36.19 to 1.36.22 ([#6116](https://github.com/aws-powertools/powertools-lambda-python/issues/6116)) +* **deps-dev:** bump cfn-lint from 1.24.0 to 1.25.1 ([#6115](https://github.com/aws-powertools/powertools-lambda-python/issues/6115)) +* **deps-dev:** bump mkdocstrings-python from 1.16.0 to 1.16.1 ([#6128](https://github.com/aws-powertools/powertools-lambda-python/issues/6128)) +* **deps-dev:** bump boto3-stubs from 1.36.22 to 1.36.24 ([#6131](https://github.com/aws-powertools/powertools-lambda-python/issues/6131)) +* **deps-dev:** bump aws-cdk from 2.178.2 to 2.1000.2 ([#6126](https://github.com/aws-powertools/powertools-lambda-python/issues/6126)) +* **deps-dev:** bump sentry-sdk from 2.20.0 to 2.21.0 ([#6096](https://github.com/aws-powertools/powertools-lambda-python/issues/6096)) +* **deps-dev:** bump mkdocs-material from 9.6.3 to 9.6.4 ([#6097](https://github.com/aws-powertools/powertools-lambda-python/issues/6097)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.178.2a0 to 2.179.0a0 ([#6127](https://github.com/aws-powertools/powertools-lambda-python/issues/6127)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.178.1a0 to 2.178.2a0 ([#6095](https://github.com/aws-powertools/powertools-lambda-python/issues/6095)) +* **deps-dev:** bump boto3-stubs from 1.36.17 to 1.36.19 ([#6093](https://github.com/aws-powertools/powertools-lambda-python/issues/6093)) +* **deps-dev:** bump aws-cdk-lib from 2.178.2 to 2.179.0 ([#6130](https://github.com/aws-powertools/powertools-lambda-python/issues/6130)) +* **deps-dev:** bump ruff from 0.9.6 to 0.9.7 ([#6138](https://github.com/aws-powertools/powertools-lambda-python/issues/6138)) +* **deps-dev:** bump aws-cdk from 2.178.1 to 2.178.2 ([#6089](https://github.com/aws-powertools/powertools-lambda-python/issues/6089)) +* **deps-dev:** bump mkdocs-material from 9.6.4 to 9.6.5 ([#6136](https://github.com/aws-powertools/powertools-lambda-python/issues/6136)) +* **deps-dev:** bump boto3-stubs from 1.36.24 to 1.36.25 ([#6137](https://github.com/aws-powertools/powertools-lambda-python/issues/6137)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.179.0a0 to 2.180.0a0 ([#6145](https://github.com/aws-powertools/powertools-lambda-python/issues/6145)) +* **deps-dev:** bump aws-cdk-lib from 2.179.0 to 2.180.0 ([#6148](https://github.com/aws-powertools/powertools-lambda-python/issues/6148)) +* **deps-dev:** bump coverage from 7.6.11 to 7.6.12 ([#6080](https://github.com/aws-powertools/powertools-lambda-python/issues/6080)) +* **deps-dev:** bump mkdocstrings-python from 1.14.6 to 1.15.0 ([#6079](https://github.com/aws-powertools/powertools-lambda-python/issues/6079)) +* **deps-dev:** bump boto3-stubs from 1.36.16 to 1.36.17 ([#6078](https://github.com/aws-powertools/powertools-lambda-python/issues/6078)) +* **deps-dev:** bump boto3-stubs from 1.36.25 to 1.36.26 ([#6146](https://github.com/aws-powertools/powertools-lambda-python/issues/6146)) +* **docs:** enable sitemap generation ([#6103](https://github.com/aws-powertools/powertools-lambda-python/issues/6103)) + + +<a name="v3.6.0"></a> +## [v3.6.0] - 2025-02-11 +## Bug Fixes + +* **docs:** typo in a service name in Event Handler ([#5944](https://github.com/aws-powertools/powertools-lambda-python/issues/5944)) +* **logger:** child logger must respect log level ([#5950](https://github.com/aws-powertools/powertools-lambda-python/issues/5950)) + +## Code Refactoring + +* **metrics:** Improve type annotations for metrics decorator ([#6000](https://github.com/aws-powertools/powertools-lambda-python/issues/6000)) + +## Documentation + +* **api:** migrating the event handler utility to mkdocstrings ([#6023](https://github.com/aws-powertools/powertools-lambda-python/issues/6023)) +* **api:** migrating the metrics utility to mkdocstrings ([#6022](https://github.com/aws-powertools/powertools-lambda-python/issues/6022)) +* **api:** migrating the logger utility to mkdocstrings ([#6021](https://github.com/aws-powertools/powertools-lambda-python/issues/6021)) +* **api:** migrating the Middleware Factory utility to mkdocstrings ([#6019](https://github.com/aws-powertools/powertools-lambda-python/issues/6019)) +* **api:** migrating the tracer utility to mkdocstrings ([#6017](https://github.com/aws-powertools/powertools-lambda-python/issues/6017)) +* **api:** migrating the batch utility to mkdocstrings ([#6016](https://github.com/aws-powertools/powertools-lambda-python/issues/6016)) +* **api:** migrating the event source data classes utility to mkdocstrings ([#6015](https://github.com/aws-powertools/powertools-lambda-python/issues/6015)) +* **api:** migrating the data masking utility to mkdocstrings ([#6013](https://github.com/aws-powertools/powertools-lambda-python/issues/6013)) +* **api:** migrating the AppConfig utility to mkdocstrings ([#6008](https://github.com/aws-powertools/powertools-lambda-python/issues/6008)) +* **api:** migrating the idempotency utility to mkdocstrings ([#6007](https://github.com/aws-powertools/powertools-lambda-python/issues/6007)) +* **api:** migrating the jmespath utility to mkdocstrings ([#6006](https://github.com/aws-powertools/powertools-lambda-python/issues/6006)) +* **api:** migrating the parameters utility to mkdocstrings ([#6005](https://github.com/aws-powertools/powertools-lambda-python/issues/6005)) +* **api:** migrating the parser utility to mkdocstrings ([#6004](https://github.com/aws-powertools/powertools-lambda-python/issues/6004)) +* **api:** migrating the streaming utility to mkdocstrings ([#6003](https://github.com/aws-powertools/powertools-lambda-python/issues/6003)) +* **api:** migrating the typing utility to mkdocstrings ([#5996](https://github.com/aws-powertools/powertools-lambda-python/issues/5996)) +* **api:** migrating the validation utility to mkdocstrings ([#5972](https://github.com/aws-powertools/powertools-lambda-python/issues/5972)) +* **layer:** update layer version number - v3.5.0 ([#5952](https://github.com/aws-powertools/powertools-lambda-python/issues/5952)) + +## Features + +* **data-masking:** add custom mask functionalities ([#5837](https://github.com/aws-powertools/powertools-lambda-python/issues/5837)) +* **event_source:** add class APIGatewayAuthorizerResponseWebSocket ([#6058](https://github.com/aws-powertools/powertools-lambda-python/issues/6058)) +* **logger:** add clear_state method ([#5956](https://github.com/aws-powertools/powertools-lambda-python/issues/5956)) +* **metrics:** disable metrics flush via environment variables ([#6046](https://github.com/aws-powertools/powertools-lambda-python/issues/6046)) +* **openapi:** enhance support for tuple return type validation ([#5997](https://github.com/aws-powertools/powertools-lambda-python/issues/5997)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.5.1a9 ([#6069](https://github.com/aws-powertools/powertools-lambda-python/issues/6069)) +* **ci:** new pre-release 3.5.1a0 ([#5945](https://github.com/aws-powertools/powertools-lambda-python/issues/5945)) +* **ci:** new pre-release 3.5.1a1 ([#5954](https://github.com/aws-powertools/powertools-lambda-python/issues/5954)) +* **ci:** new pre-release 3.5.1a8 ([#6061](https://github.com/aws-powertools/powertools-lambda-python/issues/6061)) +* **ci:** install & configure mkdocstrings plugin ([#5959](https://github.com/aws-powertools/powertools-lambda-python/issues/5959)) +* **ci:** new pre-release 3.5.1a2 ([#5970](https://github.com/aws-powertools/powertools-lambda-python/issues/5970)) +* **ci:** new pre-release 3.5.1a3 ([#5998](https://github.com/aws-powertools/powertools-lambda-python/issues/5998)) +* **ci:** new pre-release 3.5.1a7 ([#6044](https://github.com/aws-powertools/powertools-lambda-python/issues/6044)) +* **ci:** new pre-release 3.5.1a4 ([#6018](https://github.com/aws-powertools/powertools-lambda-python/issues/6018)) +* **ci:** remove pdoc3 library ([#6024](https://github.com/aws-powertools/powertools-lambda-python/issues/6024)) +* **ci:** new pre-release 3.5.1a5 ([#6026](https://github.com/aws-powertools/powertools-lambda-python/issues/6026)) +* **ci:** add new script to bump Lambda layer version ([#6001](https://github.com/aws-powertools/powertools-lambda-python/issues/6001)) +* **ci:** new pre-release 3.5.1a6 ([#6033](https://github.com/aws-powertools/powertools-lambda-python/issues/6033)) +* **deps:** bump squidfunk/mkdocs-material from `471695f` to `7e841df` in /docs ([#6012](https://github.com/aws-powertools/powertools-lambda-python/issues/6012)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.20 to 3.0.21 ([#6064](https://github.com/aws-powertools/powertools-lambda-python/issues/6064)) +* **deps:** bump actions/setup-python from 5.3.0 to 5.4.0 ([#5960](https://github.com/aws-powertools/powertools-lambda-python/issues/5960)) +* **deps:** bump docker/setup-qemu-action from 3.2.0 to 3.3.0 ([#5961](https://github.com/aws-powertools/powertools-lambda-python/issues/5961)) +* **deps:** bump codecov/codecov-action from 5.1.2 to 5.3.1 ([#5964](https://github.com/aws-powertools/powertools-lambda-python/issues/5964)) +* **deps:** bump squidfunk/mkdocs-material from `7e841df` to `c62453b` in /docs ([#6052](https://github.com/aws-powertools/powertools-lambda-python/issues/6052)) +* **deps:** bump actions/setup-node from 4.1.0 to 4.2.0 ([#5963](https://github.com/aws-powertools/powertools-lambda-python/issues/5963)) +* **deps:** bump actions/upload-artifact from 4.5.0 to 4.6.0 ([#5962](https://github.com/aws-powertools/powertools-lambda-python/issues/5962)) +* **deps:** bump release-drafter/release-drafter from 6.0.0 to 6.1.0 ([#5976](https://github.com/aws-powertools/powertools-lambda-python/issues/5976)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.18 to 3.0.20 ([#5977](https://github.com/aws-powertools/powertools-lambda-python/issues/5977)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4 ([#5980](https://github.com/aws-powertools/powertools-lambda-python/issues/5980)) +* **deps:** bump docker/setup-buildx-action from 3.8.0 to 3.9.0 ([#6042](https://github.com/aws-powertools/powertools-lambda-python/issues/6042)) +* **deps:** bump docker/setup-qemu-action from 3.3.0 to 3.4.0 ([#6043](https://github.com/aws-powertools/powertools-lambda-python/issues/6043)) +* **deps:** bump aws-actions/configure-aws-credentials from 4.0.2 to 4.0.3 ([#5975](https://github.com/aws-powertools/powertools-lambda-python/issues/5975)) +* **deps:** bump squidfunk/mkdocs-material from `41942f7` to `471695f` in /docs ([#5979](https://github.com/aws-powertools/powertools-lambda-python/issues/5979)) +* **deps:** bump actions/setup-go from 5.2.0 to 5.3.0 ([#5978](https://github.com/aws-powertools/powertools-lambda-python/issues/5978)) +* **deps-dev:** bump aws-cdk from 2.178.0 to 2.178.1 ([#6053](https://github.com/aws-powertools/powertools-lambda-python/issues/6053)) +* **deps-dev:** bump mkdocstrings-python from 1.13.0 to 1.14.2 ([#6011](https://github.com/aws-powertools/powertools-lambda-python/issues/6011)) +* **deps-dev:** bump mkdocs-material from 9.6.1 to 9.6.2 ([#6009](https://github.com/aws-powertools/powertools-lambda-python/issues/6009)) +* **deps-dev:** bump aws-cdk-lib from 2.178.0 to 2.178.1 ([#6047](https://github.com/aws-powertools/powertools-lambda-python/issues/6047)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.178.0a0 to 2.178.1a0 ([#6048](https://github.com/aws-powertools/powertools-lambda-python/issues/6048)) +* **deps-dev:** bump boto3-stubs from 1.36.14 to 1.36.15 ([#6049](https://github.com/aws-powertools/powertools-lambda-python/issues/6049)) +* **deps-dev:** bump boto3-stubs from 1.36.10 to 1.36.11 ([#6010](https://github.com/aws-powertools/powertools-lambda-python/issues/6010)) +* **deps-dev:** bump boto3-stubs from 1.36.10 to 1.36.12 ([#6014](https://github.com/aws-powertools/powertools-lambda-python/issues/6014)) +* **deps-dev:** bump ruff from 0.9.5 to 0.9.6 ([#6066](https://github.com/aws-powertools/powertools-lambda-python/issues/6066)) +* **deps-dev:** bump mkdocstrings-python from 1.14.2 to 1.14.4 ([#6025](https://github.com/aws-powertools/powertools-lambda-python/issues/6025)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.177.0a0 to 2.178.0a0 ([#6041](https://github.com/aws-powertools/powertools-lambda-python/issues/6041)) +* **deps-dev:** bump mkdocs-material from 9.5.50 to 9.6.1 ([#5966](https://github.com/aws-powertools/powertools-lambda-python/issues/5966)) +* **deps-dev:** bump black from 24.10.0 to 25.1.0 ([#5968](https://github.com/aws-powertools/powertools-lambda-python/issues/5968)) +* **deps-dev:** bump ruff from 0.9.3 to 0.9.4 ([#5969](https://github.com/aws-powertools/powertools-lambda-python/issues/5969)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.291 to 0.1.292 ([#6051](https://github.com/aws-powertools/powertools-lambda-python/issues/6051)) +* **deps-dev:** bump cfn-lint from 1.22.7 to 1.23.1 ([#5967](https://github.com/aws-powertools/powertools-lambda-python/issues/5967)) +* **deps-dev:** bump mkdocstrings-python from 1.14.5 to 1.14.6 ([#6050](https://github.com/aws-powertools/powertools-lambda-python/issues/6050)) +* **deps-dev:** bump isort from 5.13.2 to 6.0.0 ([#5965](https://github.com/aws-powertools/powertools-lambda-python/issues/5965)) +* **deps-dev:** bump ruff from 0.9.4 to 0.9.5 ([#6039](https://github.com/aws-powertools/powertools-lambda-python/issues/6039)) +* **deps-dev:** bump aws-cdk-lib from 2.177.0 to 2.178.0 ([#6038](https://github.com/aws-powertools/powertools-lambda-python/issues/6038)) +* **deps-dev:** bump mypy from 1.14.1 to 1.15.0 ([#6028](https://github.com/aws-powertools/powertools-lambda-python/issues/6028)) +* **deps-dev:** bump mkdocstrings-python from 1.14.4 to 1.14.5 ([#6032](https://github.com/aws-powertools/powertools-lambda-python/issues/6032)) +* **deps-dev:** bump cfn-lint from 1.23.1 to 1.24.0 ([#6030](https://github.com/aws-powertools/powertools-lambda-python/issues/6030)) +* **deps-dev:** bump boto3-stubs from 1.36.14 to 1.36.16 ([#6057](https://github.com/aws-powertools/powertools-lambda-python/issues/6057)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.290 to 0.1.291 ([#6031](https://github.com/aws-powertools/powertools-lambda-python/issues/6031)) +* **deps-dev:** bump boto3-stubs from 1.36.12 to 1.36.14 ([#6029](https://github.com/aws-powertools/powertools-lambda-python/issues/6029)) +* **deps-dev:** bump mkdocs-material from 9.6.2 to 9.6.3 ([#6065](https://github.com/aws-powertools/powertools-lambda-python/issues/6065)) +* **deps-dev:** bump coverage from 7.6.10 to 7.6.11 ([#6067](https://github.com/aws-powertools/powertools-lambda-python/issues/6067)) +* **deps-dev:** bump aws-cdk from 2.177.0 to 2.178.0 ([#6040](https://github.com/aws-powertools/powertools-lambda-python/issues/6040)) +* **docs:** enable privacy plugin in docs ([#6036](https://github.com/aws-powertools/powertools-lambda-python/issues/6036)) + + +<a name="v3.5.0"></a> +## [v3.5.0] - 2025-01-28 +## Bug Fixes + +* **event_handler:** fixes typo in variable name `fronzen_openapi_extensions` ([#5929](https://github.com/aws-powertools/powertools-lambda-python/issues/5929)) +* **event_handler:** add tests for PEP 563 compatibility with OpenAPI ([#5886](https://github.com/aws-powertools/powertools-lambda-python/issues/5886)) +* **event_handler:** fix forward references resolution in OpenAPI ([#5885](https://github.com/aws-powertools/powertools-lambda-python/issues/5885)) +* **parser:** make identitySource optional for ApiGatewayAuthorizerRequestV2 model ([#5880](https://github.com/aws-powertools/powertools-lambda-python/issues/5880)) + +## Documentation + +* **data_classes:** improve Event Source Data Classes documentation ([#5916](https://github.com/aws-powertools/powertools-lambda-python/issues/5916)) +* **event_handler:** demonstrate handling optional security routes ([#5895](https://github.com/aws-powertools/powertools-lambda-python/issues/5895)) +* **layer:** update layer version number - v3.4.1 ([#5869](https://github.com/aws-powertools/powertools-lambda-python/issues/5869)) +* **parser:** improve documentation with Pydantic best practices ([#5925](https://github.com/aws-powertools/powertools-lambda-python/issues/5925)) + +## Features + +* **event_source:** add AWS Transfer Family classes ([#5912](https://github.com/aws-powertools/powertools-lambda-python/issues/5912)) +* **idempotency:** add support for custom Idempotency key prefix ([#5898](https://github.com/aws-powertools/powertools-lambda-python/issues/5898)) +* **logger:** add context manager for logger keys ([#5883](https://github.com/aws-powertools/powertools-lambda-python/issues/5883)) +* **parser:** add AWS Transfer Family model ([#5906](https://github.com/aws-powertools/powertools-lambda-python/issues/5906)) + +## Maintenance + +* version bump +* **ci:** adding poetry export plugin to support v2 ([#5941](https://github.com/aws-powertools/powertools-lambda-python/issues/5941)) +* **ci:** adding poetry export plugin to support v2 ([#5938](https://github.com/aws-powertools/powertools-lambda-python/issues/5938)) +* **ci:** adjust token permission ([#5867](https://github.com/aws-powertools/powertools-lambda-python/issues/5867)) +* **ci:** new pre-release 3.4.2a0 ([#5873](https://github.com/aws-powertools/powertools-lambda-python/issues/5873)) +* **ci:** make `pyproject.toml` fully compatible with Poetryv2 ([#5902](https://github.com/aws-powertools/powertools-lambda-python/issues/5902)) +* **ci:** drop support for Python 3.8 ([#5896](https://github.com/aws-powertools/powertools-lambda-python/issues/5896)) +* **ci:** update poetry version to v2 ([#5936](https://github.com/aws-powertools/powertools-lambda-python/issues/5936)) +* **ci:** fix permissions for gh pages ([#5866](https://github.com/aws-powertools/powertools-lambda-python/issues/5866)) +* **deps:** bump pydantic from 2.10.5 to 2.10.6 ([#5918](https://github.com/aws-powertools/powertools-lambda-python/issues/5918)) +* **deps:** bump squidfunk/mkdocs-material from `ba73db5` to `41942f7` in /docs ([#5890](https://github.com/aws-powertools/powertools-lambda-python/issues/5890)) +* **deps-dev:** bump boto3-stubs from 1.36.4 to 1.36.5 ([#5919](https://github.com/aws-powertools/powertools-lambda-python/issues/5919)) +* **deps-dev:** bump boto3-stubs from 1.36.4 to 1.36.6 ([#5923](https://github.com/aws-powertools/powertools-lambda-python/issues/5923)) +* **deps-dev:** bump cfn-lint from 1.22.6 to 1.22.7 ([#5910](https://github.com/aws-powertools/powertools-lambda-python/issues/5910)) +* **deps-dev:** bump testcontainers from 3.7.1 to 4.9.1 ([#5907](https://github.com/aws-powertools/powertools-lambda-python/issues/5907)) +* **deps-dev:** bump pytest-benchmark from 4.0.0 to 5.1.0 ([#5909](https://github.com/aws-powertools/powertools-lambda-python/issues/5909)) +* **deps-dev:** bump aws-cdk from 2.176.0 to 2.177.0 ([#5930](https://github.com/aws-powertools/powertools-lambda-python/issues/5930)) +* **deps-dev:** bump pytest-cov from 5.0.0 to 6.0.0 ([#5908](https://github.com/aws-powertools/powertools-lambda-python/issues/5908)) +* **deps-dev:** bump aws-cdk-lib from 2.176.0 to 2.177.0 ([#5931](https://github.com/aws-powertools/powertools-lambda-python/issues/5931)) +* **deps-dev:** bump cfn-lint from 1.22.5 to 1.22.6 ([#5900](https://github.com/aws-powertools/powertools-lambda-python/issues/5900)) +* **deps-dev:** bump boto3-stubs from 1.36.6 to 1.36.7 ([#5932](https://github.com/aws-powertools/powertools-lambda-python/issues/5932)) +* **deps-dev:** bump boto3-stubs from 1.36.2 to 1.36.3 ([#5894](https://github.com/aws-powertools/powertools-lambda-python/issues/5894)) +* **deps-dev:** bump pytest-asyncio from 0.24.0 to 0.25.2 ([#5920](https://github.com/aws-powertools/powertools-lambda-python/issues/5920)) +* **deps-dev:** bump mkdocs-material from 9.5.49 to 9.5.50 ([#5889](https://github.com/aws-powertools/powertools-lambda-python/issues/5889)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.175.1a0 to 2.176.0a0 ([#5882](https://github.com/aws-powertools/powertools-lambda-python/issues/5882)) +* **deps-dev:** bump boto3-stubs from 1.36.1 to 1.36.2 ([#5881](https://github.com/aws-powertools/powertools-lambda-python/issues/5881)) +* **deps-dev:** bump aws-cdk from 2.175.1 to 2.176.0 ([#5878](https://github.com/aws-powertools/powertools-lambda-python/issues/5878)) +* **deps-dev:** bump ruff from 0.9.1 to 0.9.2 ([#5877](https://github.com/aws-powertools/powertools-lambda-python/issues/5877)) +* **deps-dev:** bump aws-cdk-lib from 2.175.1 to 2.176.0 ([#5876](https://github.com/aws-powertools/powertools-lambda-python/issues/5876)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.35.93 to 1.36.0 in the boto-typing group ([#5875](https://github.com/aws-powertools/powertools-lambda-python/issues/5875)) +* **deps-dev:** bump sentry-sdk from 2.19.2 to 2.20.0 ([#5870](https://github.com/aws-powertools/powertools-lambda-python/issues/5870)) +* **deps-dev:** bump boto3-stubs from 1.35.97 to 1.35.99 ([#5874](https://github.com/aws-powertools/powertools-lambda-python/issues/5874)) +* **deps-dev:** bump cfn-lint from 1.22.4 to 1.22.5 ([#5872](https://github.com/aws-powertools/powertools-lambda-python/issues/5872)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.176.0a0 to 2.177.0a0 ([#5933](https://github.com/aws-powertools/powertools-lambda-python/issues/5933)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.289 to 0.1.290 ([#5917](https://github.com/aws-powertools/powertools-lambda-python/issues/5917)) +* **deps-dev:** bump ruff from 0.9.2 to 0.9.3 ([#5911](https://github.com/aws-powertools/powertools-lambda-python/issues/5911)) + + +<a name="v3.4.1"></a> +## [v3.4.1] - 2025-01-14 +## Bug Fixes + +* **appsync:** enhance consistency for custom resolver field naming in AppSync ([#5801](https://github.com/aws-powertools/powertools-lambda-python/issues/5801)) +* **idempotency:** add support for Optional type when serializing output ([#5590](https://github.com/aws-powertools/powertools-lambda-python/issues/5590)) + +## Documentation + +* **community:** data masking blog post ([#5831](https://github.com/aws-powertools/powertools-lambda-python/issues/5831)) +* **home:** fix date typo and shorten message. ([#5798](https://github.com/aws-powertools/powertools-lambda-python/issues/5798)) +* **layer:** update layer version number - v3.4.0 ([#5785](https://github.com/aws-powertools/powertools-lambda-python/issues/5785)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.4.1a7 ([#5816](https://github.com/aws-powertools/powertools-lambda-python/issues/5816)) +* **ci:** new pre-release 3.4.1a0 ([#5783](https://github.com/aws-powertools/powertools-lambda-python/issues/5783)) +* **ci:** change token permissions ([#5862](https://github.com/aws-powertools/powertools-lambda-python/issues/5862)) +* **ci:** change token permissions / update aws-credentials action ([#5861](https://github.com/aws-powertools/powertools-lambda-python/issues/5861)) +* **ci:** fix dependency resolution ([#5859](https://github.com/aws-powertools/powertools-lambda-python/issues/5859)) +* **ci:** fix dependency resolution ([#5858](https://github.com/aws-powertools/powertools-lambda-python/issues/5858)) +* **ci:** change token permissions ([#5865](https://github.com/aws-powertools/powertools-lambda-python/issues/5865)) +* **ci:** new pre-release 3.4.1a1 ([#5789](https://github.com/aws-powertools/powertools-lambda-python/issues/5789)) +* **ci:** new pre-release 3.4.1a2 ([#5791](https://github.com/aws-powertools/powertools-lambda-python/issues/5791)) +* **ci:** new pre-release 3.4.1a3 ([#5794](https://github.com/aws-powertools/powertools-lambda-python/issues/5794)) +* **ci:** new pre-release 3.4.1a10 ([#5845](https://github.com/aws-powertools/powertools-lambda-python/issues/5845)) +* **ci:** new pre-release 3.4.1a4 ([#5796](https://github.com/aws-powertools/powertools-lambda-python/issues/5796)) +* **ci:** new pre-release 3.4.1a5 ([#5807](https://github.com/aws-powertools/powertools-lambda-python/issues/5807)) +* **ci:** new pre-release 3.4.1a8 ([#5818](https://github.com/aws-powertools/powertools-lambda-python/issues/5818)) +* **ci:** new pre-release 3.4.1a6 ([#5813](https://github.com/aws-powertools/powertools-lambda-python/issues/5813)) +* **ci:** new pre-release 3.4.1a9 ([#5822](https://github.com/aws-powertools/powertools-lambda-python/issues/5822)) +* **deps:** bump pydantic from 2.10.4 to 2.10.5 ([#5848](https://github.com/aws-powertools/powertools-lambda-python/issues/5848)) +* **deps:** bump jinja2 from 3.1.4 to 3.1.5 in /docs ([#5787](https://github.com/aws-powertools/powertools-lambda-python/issues/5787)) +* **deps:** bump pydantic-settings from 2.7.0 to 2.7.1 ([#5815](https://github.com/aws-powertools/powertools-lambda-python/issues/5815)) +* **deps-dev:** bump ruff from 0.8.4 to 0.8.6 ([#5833](https://github.com/aws-powertools/powertools-lambda-python/issues/5833)) +* **deps-dev:** bump boto3-stubs from 1.35.90 to 1.35.92 ([#5827](https://github.com/aws-powertools/powertools-lambda-python/issues/5827)) +* **deps-dev:** bump aws-cdk from 2.173.4 to 2.174.0 ([#5832](https://github.com/aws-powertools/powertools-lambda-python/issues/5832)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.173.2a0 to 2.173.4a0 ([#5811](https://github.com/aws-powertools/powertools-lambda-python/issues/5811)) +* **deps-dev:** bump cfn-lint from 1.22.2 to 1.22.3 ([#5810](https://github.com/aws-powertools/powertools-lambda-python/issues/5810)) +* **deps-dev:** bump boto3-stubs from 1.35.89 to 1.35.90 ([#5809](https://github.com/aws-powertools/powertools-lambda-python/issues/5809)) +* **deps-dev:** bump mypy from 1.14.0 to 1.14.1 ([#5812](https://github.com/aws-powertools/powertools-lambda-python/issues/5812)) +* **deps-dev:** bump boto3-stubs from 1.35.92 to 1.35.93 ([#5835](https://github.com/aws-powertools/powertools-lambda-python/issues/5835)) +* **deps-dev:** bump aws-cdk-lib from 2.173.4 to 2.174.1 ([#5838](https://github.com/aws-powertools/powertools-lambda-python/issues/5838)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.35.0 to 1.35.93 in the boto-typing group ([#5840](https://github.com/aws-powertools/powertools-lambda-python/issues/5840)) +* **deps-dev:** bump aws-cdk-lib from 2.173.2 to 2.173.4 ([#5803](https://github.com/aws-powertools/powertools-lambda-python/issues/5803)) +* **deps-dev:** bump aws-cdk from 2.173.2 to 2.173.4 ([#5802](https://github.com/aws-powertools/powertools-lambda-python/issues/5802)) +* **deps-dev:** bump boto3-stubs from 1.35.87 to 1.35.89 ([#5804](https://github.com/aws-powertools/powertools-lambda-python/issues/5804)) +* **deps-dev:** bump jinja2 from 3.1.4 to 3.1.5 ([#5788](https://github.com/aws-powertools/powertools-lambda-python/issues/5788)) +* **deps-dev:** bump aws-cdk from 2.174.0 to 2.174.1 ([#5841](https://github.com/aws-powertools/powertools-lambda-python/issues/5841)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.173.4a0 to 2.174.1a0 ([#5842](https://github.com/aws-powertools/powertools-lambda-python/issues/5842)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.287 to 0.1.288 ([#5793](https://github.com/aws-powertools/powertools-lambda-python/issues/5793)) +* **deps-dev:** bump boto3-stubs from 1.35.93 to 1.35.94 ([#5844](https://github.com/aws-powertools/powertools-lambda-python/issues/5844)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.288 to 0.1.289 ([#5843](https://github.com/aws-powertools/powertools-lambda-python/issues/5843)) +* **deps-dev:** bump boto3-stubs from 1.35.94 to 1.35.95 ([#5847](https://github.com/aws-powertools/powertools-lambda-python/issues/5847)) +* **deps-dev:** bump cfn-lint from 1.22.3 to 1.22.4 ([#5849](https://github.com/aws-powertools/powertools-lambda-python/issues/5849)) +* **deps-dev:** bump boto3-stubs from 1.35.95 to 1.35.96 ([#5850](https://github.com/aws-powertools/powertools-lambda-python/issues/5850)) +* **deps-dev:** bump boto3-stubs from 1.35.96 to 1.35.97 ([#5852](https://github.com/aws-powertools/powertools-lambda-python/issues/5852)) +* **deps-dev:** bump boto3-stubs from 1.35.86 to 1.35.87 ([#5786](https://github.com/aws-powertools/powertools-lambda-python/issues/5786)) +* **deps-dev:** bump aws-cdk from 2.174.1 to 2.175.0 ([#5854](https://github.com/aws-powertools/powertools-lambda-python/issues/5854)) +* **deps-dev:** bump aws-cdk from 2.175.0 to 2.175.1 ([#5863](https://github.com/aws-powertools/powertools-lambda-python/issues/5863)) +* **deps-dev:** bump boto3-stubs from 1.35.85 to 1.35.86 ([#5780](https://github.com/aws-powertools/powertools-lambda-python/issues/5780)) +* **deps-dev:** bump mypy from 1.13.0 to 1.14.0 ([#5779](https://github.com/aws-powertools/powertools-lambda-python/issues/5779)) +* **deps-dev:** bump ruff from 0.8.6 to 0.9.1 ([#5853](https://github.com/aws-powertools/powertools-lambda-python/issues/5853)) +* **deps-dev:** bump aws-cdk-lib from 2.174.1 to 2.175.1 ([#5856](https://github.com/aws-powertools/powertools-lambda-python/issues/5856)) + + +<a name="v3.4.0"></a> +## [v3.4.0] - 2024-12-20 +## Bug Fixes + +* **ci:** add overwrite to SSM workflow ([#5775](https://github.com/aws-powertools/powertools-lambda-python/issues/5775)) +* **docs:** typo in homepage extra dependencies command ([#5681](https://github.com/aws-powertools/powertools-lambda-python/issues/5681)) +* **openapi:** Allow values of any type in the examples of the Schema Object. ([#5575](https://github.com/aws-powertools/powertools-lambda-python/issues/5575)) +* **parser:** remove AttributeError validation from event_parser function ([#5742](https://github.com/aws-powertools/powertools-lambda-python/issues/5742)) +* **parser:** remove 'aws:' prefix from SelfManagedKafka model ([#5584](https://github.com/aws-powertools/powertools-lambda-python/issues/5584)) + +## Code Refactoring + +* **event_handler:** add type annotations for router decorators ([#5601](https://github.com/aws-powertools/powertools-lambda-python/issues/5601)) +* **event_handler:** add type annotations for `resolve` function ([#5602](https://github.com/aws-powertools/powertools-lambda-python/issues/5602)) + +## Documentation + +* **layer:** update layer version number - v3.3.0 ([#5562](https://github.com/aws-powertools/powertools-lambda-python/issues/5562)) + +## Features + +* **event_handler:** mark API operation as deprecated for OpenAPI documentation ([#5732](https://github.com/aws-powertools/powertools-lambda-python/issues/5732)) +* **event_handler:** add exception handling mechanism for AppSyncResolver ([#5588](https://github.com/aws-powertools/powertools-lambda-python/issues/5588)) +* **event_source:** Extend CodePipeline Artifact Capabilities ([#5448](https://github.com/aws-powertools/powertools-lambda-python/issues/5448)) +* **layer:** add new ap-southeast-5 region ([#5769](https://github.com/aws-powertools/powertools-lambda-python/issues/5769)) +* **metrics:** warn when overwriting dimension ([#5653](https://github.com/aws-powertools/powertools-lambda-python/issues/5653)) +* **parser:** add models for API GW Websockets events ([#5597](https://github.com/aws-powertools/powertools-lambda-python/issues/5597)) +* **ssm:** Parameters for resolving to versioned layers ([#5754](https://github.com/aws-powertools/powertools-lambda-python/issues/5754)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.3.1a14 ([#5713](https://github.com/aws-powertools/powertools-lambda-python/issues/5713)) +* **ci:** new pre-release 3.3.1a21 ([#5773](https://github.com/aws-powertools/powertools-lambda-python/issues/5773)) +* **ci:** new pre-release 3.3.1a0 ([#5565](https://github.com/aws-powertools/powertools-lambda-python/issues/5565)) +* **ci:** new pre-release 3.3.1a1 ([#5577](https://github.com/aws-powertools/powertools-lambda-python/issues/5577)) +* **ci:** disable dry run in layer balancing workflow ([#5768](https://github.com/aws-powertools/powertools-lambda-python/issues/5768)) +* **ci:** new pre-release 3.3.1a20 ([#5766](https://github.com/aws-powertools/powertools-lambda-python/issues/5766)) +* **ci:** new pre-release 3.3.1a10 ([#5679](https://github.com/aws-powertools/powertools-lambda-python/issues/5679)) +* **ci:** add workflow to balance layers per region ([#5752](https://github.com/aws-powertools/powertools-lambda-python/issues/5752)) +* **ci:** new pre-release 3.3.1a9 ([#5668](https://github.com/aws-powertools/powertools-lambda-python/issues/5668)) +* **ci:** new pre-release 3.3.1a19 ([#5757](https://github.com/aws-powertools/powertools-lambda-python/issues/5757)) +* **ci:** new pre-release 3.3.1a8 ([#5663](https://github.com/aws-powertools/powertools-lambda-python/issues/5663)) +* **ci:** adding missing region in matrix ([#5777](https://github.com/aws-powertools/powertools-lambda-python/issues/5777)) +* **ci:** new pre-release 3.3.1a2 ([#5585](https://github.com/aws-powertools/powertools-lambda-python/issues/5585)) +* **ci:** new pre-release 3.3.1a11 ([#5688](https://github.com/aws-powertools/powertools-lambda-python/issues/5688)) +* **ci:** new pre-release 3.3.1a3 ([#5598](https://github.com/aws-powertools/powertools-lambda-python/issues/5598)) +* **ci:** new pre-release 3.3.1a7 ([#5656](https://github.com/aws-powertools/powertools-lambda-python/issues/5656)) +* **ci:** new pre-release 3.3.1a6 ([#5650](https://github.com/aws-powertools/powertools-lambda-python/issues/5650)) +* **ci:** new pre-release 3.3.1a12 ([#5697](https://github.com/aws-powertools/powertools-lambda-python/issues/5697)) +* **ci:** new pre-release 3.3.1a18 ([#5739](https://github.com/aws-powertools/powertools-lambda-python/issues/5739)) +* **ci:** replace closed-issue-message action with powertools action ([#5641](https://github.com/aws-powertools/powertools-lambda-python/issues/5641)) +* **ci:** new pre-release 3.3.1a17 ([#5733](https://github.com/aws-powertools/powertools-lambda-python/issues/5733)) +* **ci:** new pre-release 3.3.1a4 ([#5612](https://github.com/aws-powertools/powertools-lambda-python/issues/5612)) +* **ci:** new pre-release 3.3.1a13 ([#5707](https://github.com/aws-powertools/powertools-lambda-python/issues/5707)) +* **ci:** new pre-release 3.3.1a16 ([#5725](https://github.com/aws-powertools/powertools-lambda-python/issues/5725)) +* **ci:** remove poetry cache in quality check pipeline ([#5626](https://github.com/aws-powertools/powertools-lambda-python/issues/5626)) +* **ci:** revert closed issue action update ([#5637](https://github.com/aws-powertools/powertools-lambda-python/issues/5637)) +* **ci:** new pre-release 3.3.1a15 ([#5720](https://github.com/aws-powertools/powertools-lambda-python/issues/5720)) +* **ci:** new pre-release 3.3.1a5 ([#5639](https://github.com/aws-powertools/powertools-lambda-python/issues/5639)) +* **deps:** bump squidfunk/mkdocs-material from `ef0b45e` to `d063d84` in /docs ([#5649](https://github.com/aws-powertools/powertools-lambda-python/issues/5649)) +* **deps:** bump pydantic from 2.10.0 to 2.10.1 ([#5632](https://github.com/aws-powertools/powertools-lambda-python/issues/5632)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.12.2 to 1.12.3 ([#5709](https://github.com/aws-powertools/powertools-lambda-python/issues/5709)) +* **deps:** bump codecov/codecov-action from 5.0.3 to 5.0.7 ([#5617](https://github.com/aws-powertools/powertools-lambda-python/issues/5617)) +* **deps:** bump actions/dependency-review-action from 4.4.0 to 4.5.0 ([#5616](https://github.com/aws-powertools/powertools-lambda-python/issues/5616)) +* **deps:** bump squidfunk/mkdocs-material from `ce587cb` to `ef0b45e` in /docs ([#5603](https://github.com/aws-powertools/powertools-lambda-python/issues/5603)) +* **deps:** bump squidfunk/mkdocs-material from `d063d84` to `3f571e7` in /docs ([#5678](https://github.com/aws-powertools/powertools-lambda-python/issues/5678)) +* **deps:** bump redis from 5.2.0 to 5.2.1 ([#5701](https://github.com/aws-powertools/powertools-lambda-python/issues/5701)) +* **deps:** bump pydantic-settings from 2.6.1 to 2.7.0 ([#5735](https://github.com/aws-powertools/powertools-lambda-python/issues/5735)) +* **deps:** bump aws-actions/closed-issue-message from 80edfc24bdf1283400eb04d20a8a605ae8bf7d48 to 37548691e7cc75ba58f85c9f873f9eee43590449 ([#5606](https://github.com/aws-powertools/powertools-lambda-python/issues/5606)) +* **deps:** bump pydantic from 2.9.2 to 2.10.0 ([#5611](https://github.com/aws-powertools/powertools-lambda-python/issues/5611)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.17 to 3.0.18 ([#5743](https://github.com/aws-powertools/powertools-lambda-python/issues/5743)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.16 to 3.0.17 ([#5643](https://github.com/aws-powertools/powertools-lambda-python/issues/5643)) +* **deps:** bump squidfunk/mkdocs-material from `3f571e7` to `d485eb6` in /docs ([#5710](https://github.com/aws-powertools/powertools-lambda-python/issues/5710)) +* **deps:** bump codecov/codecov-action from 5.0.7 to 5.1.0 ([#5692](https://github.com/aws-powertools/powertools-lambda-python/issues/5692)) +* **deps:** bump pydantic from 2.10.1 to 2.10.2 ([#5654](https://github.com/aws-powertools/powertools-lambda-python/issues/5654)) +* **deps:** bump squidfunk/mkdocs-material from `d485eb6` to `ba73db5` in /docs ([#5746](https://github.com/aws-powertools/powertools-lambda-python/issues/5746)) +* **deps:** bump docker/setup-buildx-action from 3.7.1 to 3.8.0 ([#5744](https://github.com/aws-powertools/powertools-lambda-python/issues/5744)) +* **deps:** bump datadog-lambda from 6.101.0 to 6.102.0 ([#5570](https://github.com/aws-powertools/powertools-lambda-python/issues/5570)) +* **deps:** bump pydantic from 2.10.2 to 2.10.3 ([#5682](https://github.com/aws-powertools/powertools-lambda-python/issues/5682)) +* **deps:** bump aws-encryption-sdk from 3.3.0 to 4.0.0 ([#5564](https://github.com/aws-powertools/powertools-lambda-python/issues/5564)) +* **deps:** bump pydantic from 2.10.3 to 2.10.4 ([#5760](https://github.com/aws-powertools/powertools-lambda-python/issues/5760)) +* **deps:** bump actions/upload-artifact from 4.4.3 to 4.5.0 ([#5763](https://github.com/aws-powertools/powertools-lambda-python/issues/5763)) +* **deps:** bump codecov/codecov-action from 5.1.1 to 5.1.2 ([#5764](https://github.com/aws-powertools/powertools-lambda-python/issues/5764)) +* **deps:** bump codecov/codecov-action from 4.6.0 to 5.0.2 ([#5567](https://github.com/aws-powertools/powertools-lambda-python/issues/5567)) +* **deps:** bump fastjsonschema from 2.20.0 to 2.21.1 ([#5676](https://github.com/aws-powertools/powertools-lambda-python/issues/5676)) +* **deps:** bump datadog-lambda from 6.102.0 to 6.104.0 ([#5631](https://github.com/aws-powertools/powertools-lambda-python/issues/5631)) +* **deps:** bump codecov/codecov-action from 5.1.0 to 5.1.1 ([#5703](https://github.com/aws-powertools/powertools-lambda-python/issues/5703)) +* **deps:** bump codecov/codecov-action from 5.0.2 to 5.0.3 ([#5592](https://github.com/aws-powertools/powertools-lambda-python/issues/5592)) +* **deps-dev:** bump httpx from 0.27.2 to 0.28.0 ([#5665](https://github.com/aws-powertools/powertools-lambda-python/issues/5665)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.171.0a0 to 2.171.1a0 ([#5666](https://github.com/aws-powertools/powertools-lambda-python/issues/5666)) +* **deps-dev:** bump aws-cdk from 2.171.0 to 2.171.1 ([#5662](https://github.com/aws-powertools/powertools-lambda-python/issues/5662)) +* **deps-dev:** bump aws-cdk-lib from 2.171.0 to 2.171.1 ([#5661](https://github.com/aws-powertools/powertools-lambda-python/issues/5661)) +* **deps-dev:** bump boto3-stubs from 1.35.69 to 1.35.71 ([#5660](https://github.com/aws-powertools/powertools-lambda-python/issues/5660)) +* **deps-dev:** bump cfn-lint from 1.20.0 to 1.20.1 ([#5659](https://github.com/aws-powertools/powertools-lambda-python/issues/5659)) +* **deps-dev:** bump mkdocs-material from 9.5.46 to 9.5.47 ([#5677](https://github.com/aws-powertools/powertools-lambda-python/issues/5677)) +* **deps-dev:** bump cfn-lint from 1.20.1 to 1.20.2 ([#5686](https://github.com/aws-powertools/powertools-lambda-python/issues/5686)) +* **deps-dev:** bump boto3-stubs from 1.35.71 to 1.35.74 ([#5691](https://github.com/aws-powertools/powertools-lambda-python/issues/5691)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.284 to 0.1.285 ([#5642](https://github.com/aws-powertools/powertools-lambda-python/issues/5642)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.170.0a0 to 2.171.0a0 ([#5655](https://github.com/aws-powertools/powertools-lambda-python/issues/5655)) +* **deps-dev:** bump ruff from 0.8.1 to 0.8.2 ([#5693](https://github.com/aws-powertools/powertools-lambda-python/issues/5693)) +* **deps-dev:** bump pytest from 8.3.3 to 8.3.4 ([#5695](https://github.com/aws-powertools/powertools-lambda-python/issues/5695)) +* **deps-dev:** bump mkdocs-material from 9.5.45 to 9.5.46 ([#5645](https://github.com/aws-powertools/powertools-lambda-python/issues/5645)) +* **deps-dev:** bump sentry-sdk from 2.19.0 to 2.19.1 ([#5694](https://github.com/aws-powertools/powertools-lambda-python/issues/5694)) +* **deps-dev:** bump aws-cdk-lib from 2.170.0 to 2.171.0 ([#5647](https://github.com/aws-powertools/powertools-lambda-python/issues/5647)) +* **deps-dev:** bump aws-cdk from 2.170.0 to 2.171.0 ([#5648](https://github.com/aws-powertools/powertools-lambda-python/issues/5648)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.285 to 0.1.287 ([#5685](https://github.com/aws-powertools/powertools-lambda-python/issues/5685)) +* **deps-dev:** bump boto3-stubs from 1.35.67 to 1.35.69 ([#5652](https://github.com/aws-powertools/powertools-lambda-python/issues/5652)) +* **deps-dev:** bump sentry-sdk from 2.19.1 to 2.19.2 ([#5699](https://github.com/aws-powertools/powertools-lambda-python/issues/5699)) +* **deps-dev:** bump ruff from 0.7.4 to 0.8.0 ([#5630](https://github.com/aws-powertools/powertools-lambda-python/issues/5630)) +* **deps-dev:** bump types-python-dateutil from 2.9.0.20241003 to 2.9.0.20241206 ([#5700](https://github.com/aws-powertools/powertools-lambda-python/issues/5700)) +* **deps-dev:** bump httpx from 0.28.0 to 0.28.1 ([#5702](https://github.com/aws-powertools/powertools-lambda-python/issues/5702)) +* **deps-dev:** bump aws-cdk from 2.171.1 to 2.172.0 ([#5712](https://github.com/aws-powertools/powertools-lambda-python/issues/5712)) +* **deps-dev:** bump cfn-lint from 1.20.2 to 1.21.0 ([#5711](https://github.com/aws-powertools/powertools-lambda-python/issues/5711)) +* **deps-dev:** bump boto3-stubs from 1.35.76 to 1.35.77 ([#5716](https://github.com/aws-powertools/powertools-lambda-python/issues/5716)) +* **deps-dev:** bump aws-cdk-lib from 2.171.1 to 2.172.0 ([#5719](https://github.com/aws-powertools/powertools-lambda-python/issues/5719)) +* **deps-dev:** bump cfn-lint from 1.21.0 to 1.22.0 ([#5718](https://github.com/aws-powertools/powertools-lambda-python/issues/5718)) +* **deps-dev:** bump aws-cdk from 2.169.0 to 2.170.0 ([#5628](https://github.com/aws-powertools/powertools-lambda-python/issues/5628)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.167.2a0 to 2.170.0a0 ([#5629](https://github.com/aws-powertools/powertools-lambda-python/issues/5629)) +* **deps-dev:** bump boto3-stubs from 1.35.77 to 1.35.78 ([#5723](https://github.com/aws-powertools/powertools-lambda-python/issues/5723)) +* **deps-dev:** bump sentry-sdk from 2.18.0 to 2.19.0 ([#5633](https://github.com/aws-powertools/powertools-lambda-python/issues/5633)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.171.1a0 to 2.172.0a0 ([#5724](https://github.com/aws-powertools/powertools-lambda-python/issues/5724)) +* **deps-dev:** bump aws-cdk from 2.172.0 to 2.173.0 ([#5727](https://github.com/aws-powertools/powertools-lambda-python/issues/5727)) +* **deps-dev:** bump mkdocs-material from 9.5.44 to 9.5.45 ([#5610](https://github.com/aws-powertools/powertools-lambda-python/issues/5610)) +* **deps-dev:** bump ruff from 0.8.2 to 0.8.3 ([#5728](https://github.com/aws-powertools/powertools-lambda-python/issues/5728)) +* **deps-dev:** bump boto3-stubs from 1.35.64 to 1.35.67 ([#5621](https://github.com/aws-powertools/powertools-lambda-python/issues/5621)) +* **deps-dev:** bump aws-cdk-lib from 2.167.2 to 2.170.0 ([#5622](https://github.com/aws-powertools/powertools-lambda-python/issues/5622)) +* **deps-dev:** bump cfn-lint from 1.22.0 to 1.22.1 ([#5729](https://github.com/aws-powertools/powertools-lambda-python/issues/5729)) +* **deps-dev:** bump aws-cdk from 2.167.2 to 2.169.0 ([#5618](https://github.com/aws-powertools/powertools-lambda-python/issues/5618)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.282 to 0.1.284 ([#5607](https://github.com/aws-powertools/powertools-lambda-python/issues/5607)) +* **deps-dev:** bump boto3-stubs from 1.35.78 to 1.35.80 ([#5730](https://github.com/aws-powertools/powertools-lambda-python/issues/5730)) +* **deps-dev:** bump aws-cdk-lib from 2.172.0 to 2.173.0 ([#5731](https://github.com/aws-powertools/powertools-lambda-python/issues/5731)) +* **deps-dev:** bump mkdocs-material from 9.5.47 to 9.5.48 ([#5717](https://github.com/aws-powertools/powertools-lambda-python/issues/5717)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.172.0a0 to 2.173.0a0 ([#5736](https://github.com/aws-powertools/powertools-lambda-python/issues/5736)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.167.1a0 to 2.167.2a0 ([#5619](https://github.com/aws-powertools/powertools-lambda-python/issues/5619)) +* **deps-dev:** bump boto3-stubs from 1.35.80 to 1.35.81 ([#5750](https://github.com/aws-powertools/powertools-lambda-python/issues/5750)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.281 to 0.1.282 ([#5594](https://github.com/aws-powertools/powertools-lambda-python/issues/5594)) +* **deps-dev:** bump cfn-lint from 1.19.0 to 1.20.0 ([#5595](https://github.com/aws-powertools/powertools-lambda-python/issues/5595)) +* **deps-dev:** bump aws-cdk from 2.167.1 to 2.167.2 ([#5593](https://github.com/aws-powertools/powertools-lambda-python/issues/5593)) +* **deps-dev:** bump cfn-lint from 1.22.1 to 1.22.2 ([#5749](https://github.com/aws-powertools/powertools-lambda-python/issues/5749)) +* **deps-dev:** bump aws-cdk-lib from 2.167.1 to 2.167.2 ([#5596](https://github.com/aws-powertools/powertools-lambda-python/issues/5596)) +* **deps-dev:** bump aws-cdk from 2.173.0 to 2.173.1 ([#5745](https://github.com/aws-powertools/powertools-lambda-python/issues/5745)) +* **deps-dev:** bump boto3-stubs from 1.35.63 to 1.35.64 ([#5582](https://github.com/aws-powertools/powertools-lambda-python/issues/5582)) +* **deps-dev:** bump mkdocs-material from 9.5.48 to 9.5.49 ([#5748](https://github.com/aws-powertools/powertools-lambda-python/issues/5748)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.167.0a0 to 2.167.1a0 ([#5583](https://github.com/aws-powertools/powertools-lambda-python/issues/5583)) +* **deps-dev:** bump aws-cdk-lib from 2.173.0 to 2.173.1 ([#5747](https://github.com/aws-powertools/powertools-lambda-python/issues/5747)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.173.0a0 to 2.173.1a0 ([#5755](https://github.com/aws-powertools/powertools-lambda-python/issues/5755)) +* **deps-dev:** bump aws-cdk from 2.173.1 to 2.173.2 ([#5762](https://github.com/aws-powertools/powertools-lambda-python/issues/5762)) +* **deps-dev:** bump boto3-stubs from 1.35.81 to 1.35.84 ([#5765](https://github.com/aws-powertools/powertools-lambda-python/issues/5765)) +* **deps-dev:** bump boto3-stubs from 1.35.60 to 1.35.63 ([#5581](https://github.com/aws-powertools/powertools-lambda-python/issues/5581)) +* **deps-dev:** bump ruff from 0.8.0 to 0.8.1 ([#5671](https://github.com/aws-powertools/powertools-lambda-python/issues/5671)) +* **deps-dev:** bump aws-cdk from 2.167.0 to 2.167.1 ([#5572](https://github.com/aws-powertools/powertools-lambda-python/issues/5572)) +* **deps-dev:** bump boto3-stubs from 1.35.84 to 1.35.85 ([#5770](https://github.com/aws-powertools/powertools-lambda-python/issues/5770)) +* **deps-dev:** bump ruff from 0.7.3 to 0.7.4 ([#5569](https://github.com/aws-powertools/powertools-lambda-python/issues/5569)) +* **deps-dev:** bump aws-cdk-lib from 2.167.0 to 2.167.1 ([#5568](https://github.com/aws-powertools/powertools-lambda-python/issues/5568)) +* **deps-dev:** bump ruff from 0.8.3 to 0.8.4 ([#5772](https://github.com/aws-powertools/powertools-lambda-python/issues/5772)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.173.1a0 to 2.173.2a0 ([#5771](https://github.com/aws-powertools/powertools-lambda-python/issues/5771)) +* **deps-dev:** bump aws-cdk-lib from 2.173.1 to 2.173.2 ([#5759](https://github.com/aws-powertools/powertools-lambda-python/issues/5759)) +* **layers:** balance Python 3.13 layers in GovCloud partition ([#5579](https://github.com/aws-powertools/powertools-lambda-python/issues/5579)) + + +<a name="v3.3.0"></a> +## [v3.3.0] - 2024-11-14 +## Bug Fixes + +* **appsync:** make contextual data accessible for async functions ([#5317](https://github.com/aws-powertools/powertools-lambda-python/issues/5317)) +* **ci:** Update output to something easily copy/pasteable ([#5435](https://github.com/aws-powertools/powertools-lambda-python/issues/5435)) +* **ci:** remove space ([#5433](https://github.com/aws-powertools/powertools-lambda-python/issues/5433)) +* **metrics:** add warning for invalid dimension values; prevent their addition to EMF blobs ([#5542](https://github.com/aws-powertools/powertools-lambda-python/issues/5542)) +* **parameters:** fix force_fetch feature when working with get_parameters ([#5515](https://github.com/aws-powertools/powertools-lambda-python/issues/5515)) +* **parser:** support TypeAdapter instances as models ([#5535](https://github.com/aws-powertools/powertools-lambda-python/issues/5535)) + +## Documentation + +* **layer:** update layer version number - v3.2.0 ([#5426](https://github.com/aws-powertools/powertools-lambda-python/issues/5426)) +* **parser:** change parser documentation ([#5262](https://github.com/aws-powertools/powertools-lambda-python/issues/5262)) + +## Features + +* **event_handler:** mutualTLS Security Scheme for OpenAPI ([#5484](https://github.com/aws-powertools/powertools-lambda-python/issues/5484)) +* **layers:** introduce new CDK Python constructor for Powertools Lambda Layer ([#5320](https://github.com/aws-powertools/powertools-lambda-python/issues/5320)) +* **runtime:** add Python 3.13 support ([#5527](https://github.com/aws-powertools/powertools-lambda-python/issues/5527)) + +## Maintenance + +* version bump +* **ci:** Bump CDK version to build layers and fix imports ([#5555](https://github.com/aws-powertools/powertools-lambda-python/issues/5555)) +* **ci:** new pre-release 3.2.1a0 ([#5434](https://github.com/aws-powertools/powertools-lambda-python/issues/5434)) +* **ci:** new pre-release 3.2.1a15 ([#5551](https://github.com/aws-powertools/powertools-lambda-python/issues/5551)) +* **ci:** new pre-release 3.2.1a14 ([#5545](https://github.com/aws-powertools/powertools-lambda-python/issues/5545)) +* **ci:** fix imports to build Lambda layer ([#5557](https://github.com/aws-powertools/powertools-lambda-python/issues/5557)) +* **ci:** new pre-release 3.2.1a1 ([#5443](https://github.com/aws-powertools/powertools-lambda-python/issues/5443)) +* **ci:** bump minimum required pydantic version ([#5446](https://github.com/aws-powertools/powertools-lambda-python/issues/5446)) +* **ci:** new pre-release 3.2.1a2 ([#5456](https://github.com/aws-powertools/powertools-lambda-python/issues/5456)) +* **ci:** new pre-release 3.2.1a12 ([#5524](https://github.com/aws-powertools/powertools-lambda-python/issues/5524)) +* **ci:** new pre-release 3.2.1a3 ([#5465](https://github.com/aws-powertools/powertools-lambda-python/issues/5465)) +* **ci:** new pre-release 3.2.1a4 ([#5470](https://github.com/aws-powertools/powertools-lambda-python/issues/5470)) +* **ci:** new pre-release 3.2.1a5 ([#5473](https://github.com/aws-powertools/powertools-lambda-python/issues/5473)) +* **ci:** new pre-release 3.2.1a11 ([#5517](https://github.com/aws-powertools/powertools-lambda-python/issues/5517)) +* **ci:** new pre-release 3.2.1a6 ([#5480](https://github.com/aws-powertools/powertools-lambda-python/issues/5480)) +* **ci:** new pre-release 3.2.1a7 ([#5488](https://github.com/aws-powertools/powertools-lambda-python/issues/5488)) +* **ci:** new pre-release 3.2.1a10 ([#5509](https://github.com/aws-powertools/powertools-lambda-python/issues/5509)) +* **ci:** new pre-release 3.2.1a8 ([#5497](https://github.com/aws-powertools/powertools-lambda-python/issues/5497)) +* **ci:** new pre-release 3.2.1a9 ([#5504](https://github.com/aws-powertools/powertools-lambda-python/issues/5504)) +* **ci:** new pre-release 3.2.1a13 ([#5537](https://github.com/aws-powertools/powertools-lambda-python/issues/5537)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.10.3 to 1.11.0 ([#5477](https://github.com/aws-powertools/powertools-lambda-python/issues/5477)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.15 to 3.0.16 ([#5499](https://github.com/aws-powertools/powertools-lambda-python/issues/5499)) +* **deps:** bump actions/dependency-review-action from 4.3.4 to 4.3.5 ([#5431](https://github.com/aws-powertools/powertools-lambda-python/issues/5431)) +* **deps:** bump actions/setup-python from 5.2.0 to 5.3.0 ([#5529](https://github.com/aws-powertools/powertools-lambda-python/issues/5529)) +* **deps:** bump datadog-lambda from 6.99.0 to 6.100.0 ([#5491](https://github.com/aws-powertools/powertools-lambda-python/issues/5491)) +* **deps:** bump actions/checkout from 4.2.1 to 4.2.2 ([#5438](https://github.com/aws-powertools/powertools-lambda-python/issues/5438)) +* **deps:** bump actions/checkout from 4.2.0 to 4.2.2 ([#5531](https://github.com/aws-powertools/powertools-lambda-python/issues/5531)) +* **deps:** bump actions/setup-node from 4.0.4 to 4.1.0 ([#5450](https://github.com/aws-powertools/powertools-lambda-python/issues/5450)) +* **deps:** bump squidfunk/mkdocs-material from `2c2802b` to `ce587cb` in /docs ([#5507](https://github.com/aws-powertools/powertools-lambda-python/issues/5507)) +* **deps:** bump actions/setup-python from 5.2.0 to 5.3.0 ([#5449](https://github.com/aws-powertools/powertools-lambda-python/issues/5449)) +* **deps:** bump redis from 5.1.1 to 5.2.0 ([#5454](https://github.com/aws-powertools/powertools-lambda-python/issues/5454)) +* **deps:** bump docker/setup-buildx-action from 2.4.1 to 3.7.1 ([#5530](https://github.com/aws-powertools/powertools-lambda-python/issues/5530)) +* **deps:** bump squidfunk/mkdocs-material from `31eb7f7` to `2c2802b` in /docs ([#5487](https://github.com/aws-powertools/powertools-lambda-python/issues/5487)) +* **deps:** bump docker/setup-qemu-action from 2.1.0 to 3.2.0 ([#5528](https://github.com/aws-powertools/powertools-lambda-python/issues/5528)) +* **deps:** bump actions/dependency-review-action from 4.3.5 to 4.4.0 ([#5469](https://github.com/aws-powertools/powertools-lambda-python/issues/5469)) +* **deps:** bump datadog-lambda from 6.100.0 to 6.101.0 ([#5513](https://github.com/aws-powertools/powertools-lambda-python/issues/5513)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.11.0 to 1.12.1 ([#5514](https://github.com/aws-powertools/powertools-lambda-python/issues/5514)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.12.1 to 1.12.2 ([#5519](https://github.com/aws-powertools/powertools-lambda-python/issues/5519)) +* **deps-dev:** bump sentry-sdk from 2.17.0 to 2.18.0 ([#5502](https://github.com/aws-powertools/powertools-lambda-python/issues/5502)) +* **deps-dev:** bump boto3-stubs from 1.35.51 to 1.35.52 ([#5478](https://github.com/aws-powertools/powertools-lambda-python/issues/5478)) +* **deps-dev:** bump mkdocs-material from 9.5.43 to 9.5.44 ([#5506](https://github.com/aws-powertools/powertools-lambda-python/issues/5506)) +* **deps-dev:** bump cfn-lint from 1.18.2 to 1.18.3 ([#5479](https://github.com/aws-powertools/powertools-lambda-python/issues/5479)) +* **deps-dev:** bump boto3-stubs from 1.35.49 to 1.35.51 ([#5472](https://github.com/aws-powertools/powertools-lambda-python/issues/5472)) +* **deps-dev:** bump aws-cdk from 2.165.0 to 2.166.0 ([#5520](https://github.com/aws-powertools/powertools-lambda-python/issues/5520)) +* **deps-dev:** bump aws-cdk-lib from 2.165.0 to 2.166.0 ([#5522](https://github.com/aws-powertools/powertools-lambda-python/issues/5522)) +* **deps-dev:** bump boto3-stubs from 1.35.52 to 1.35.53 ([#5485](https://github.com/aws-powertools/powertools-lambda-python/issues/5485)) +* **deps-dev:** bump cfn-lint from 1.18.1 to 1.18.2 ([#5468](https://github.com/aws-powertools/powertools-lambda-python/issues/5468)) +* **deps-dev:** bump boto3-stubs from 1.35.54 to 1.35.56 ([#5523](https://github.com/aws-powertools/powertools-lambda-python/issues/5523)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.163.1a0 to 2.164.1a0 ([#5467](https://github.com/aws-powertools/powertools-lambda-python/issues/5467)) +* **deps-dev:** bump mkdocs-material from 9.5.42 to 9.5.43 ([#5486](https://github.com/aws-powertools/powertools-lambda-python/issues/5486)) +* **deps-dev:** bump aws-cdk from 2.164.0 to 2.164.1 ([#5462](https://github.com/aws-powertools/powertools-lambda-python/issues/5462)) +* **deps-dev:** bump boto3-stubs from 1.35.46 to 1.35.49 ([#5460](https://github.com/aws-powertools/powertools-lambda-python/issues/5460)) +* **deps-dev:** bump aws-cdk-lib from 2.164.0 to 2.164.1 ([#5459](https://github.com/aws-powertools/powertools-lambda-python/issues/5459)) +* **deps-dev:** bump ruff from 0.7.0 to 0.7.1 ([#5451](https://github.com/aws-powertools/powertools-lambda-python/issues/5451)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.278 to 0.1.279 ([#5512](https://github.com/aws-powertools/powertools-lambda-python/issues/5512)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.165.0a0 to 2.166.0a0 ([#5533](https://github.com/aws-powertools/powertools-lambda-python/issues/5533)) +* **deps-dev:** bump aws-cdk-lib from 2.163.1 to 2.164.0 ([#5453](https://github.com/aws-powertools/powertools-lambda-python/issues/5453)) +* **deps-dev:** bump aws-cdk from 2.163.1 to 2.164.0 ([#5452](https://github.com/aws-powertools/powertools-lambda-python/issues/5452)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.279 to 0.1.281 ([#5548](https://github.com/aws-powertools/powertools-lambda-python/issues/5548)) +* **deps-dev:** bump aws-cdk-lib from 2.164.1 to 2.165.0 ([#5490](https://github.com/aws-powertools/powertools-lambda-python/issues/5490)) +* **deps-dev:** bump boto3-stubs from 1.35.53 to 1.35.54 ([#5493](https://github.com/aws-powertools/powertools-lambda-python/issues/5493)) +* **deps-dev:** bump aws-cdk from 2.164.1 to 2.165.0 ([#5494](https://github.com/aws-powertools/powertools-lambda-python/issues/5494)) +* **deps-dev:** bump mypy from 1.11.2 to 1.13.0 ([#5440](https://github.com/aws-powertools/powertools-lambda-python/issues/5440)) +* **deps-dev:** bump ruff from 0.7.2 to 0.7.3 ([#5532](https://github.com/aws-powertools/powertools-lambda-python/issues/5532)) +* **deps-dev:** bump boto3-stubs from 1.35.56 to 1.35.58 ([#5540](https://github.com/aws-powertools/powertools-lambda-python/issues/5540)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.162.1a0 to 2.163.1a0 ([#5441](https://github.com/aws-powertools/powertools-lambda-python/issues/5441)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.277 to 0.1.278 ([#5439](https://github.com/aws-powertools/powertools-lambda-python/issues/5439)) +* **deps-dev:** bump cfn-lint from 1.18.3 to 1.18.4 ([#5501](https://github.com/aws-powertools/powertools-lambda-python/issues/5501)) +* **deps-dev:** bump cfn-lint from 1.18.4 to 1.19.0 ([#5544](https://github.com/aws-powertools/powertools-lambda-python/issues/5544)) +* **deps-dev:** bump ruff from 0.7.1 to 0.7.2 ([#5492](https://github.com/aws-powertools/powertools-lambda-python/issues/5492)) +* **deps-dev:** bump aws-cdk-lib from 2.162.1 to 2.163.1 ([#5429](https://github.com/aws-powertools/powertools-lambda-python/issues/5429)) +* **deps-dev:** bump boto3-stubs from 1.35.45 to 1.35.46 ([#5430](https://github.com/aws-powertools/powertools-lambda-python/issues/5430)) +* **deps-dev:** bump aws-cdk from 2.162.1 to 2.163.1 ([#5432](https://github.com/aws-powertools/powertools-lambda-python/issues/5432)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.164.1a0 to 2.165.0a0 ([#5500](https://github.com/aws-powertools/powertools-lambda-python/issues/5500)) +* **deps-dev:** bump xenon from 0.9.1 to 0.9.3 ([#5428](https://github.com/aws-powertools/powertools-lambda-python/issues/5428)) +* **deps-dev:** bump boto3-stubs from 1.35.58 to 1.35.59 ([#5549](https://github.com/aws-powertools/powertools-lambda-python/issues/5549)) +* **layers:** add pydantic-settings package to v3 Layer ([#5516](https://github.com/aws-powertools/powertools-lambda-python/issues/5516)) + + +<a name="v3.2.0"></a> +## [v3.2.0] - 2024-10-22 +## Bug Fixes + +* test command in verify step ([#5381](https://github.com/aws-powertools/powertools-lambda-python/issues/5381)) +* **ci:** Tables are nicer ([#5416](https://github.com/aws-powertools/powertools-lambda-python/issues/5416)) +* **ci:** GovCloud layer verification ([#5382](https://github.com/aws-powertools/powertools-lambda-python/issues/5382)) +* **ci:** Update partition name ([#5380](https://github.com/aws-powertools/powertools-lambda-python/issues/5380)) +* **layer:** update partition name in the GovCloud workflow ([#5379](https://github.com/aws-powertools/powertools-lambda-python/issues/5379)) + +## Documentation + +* Add GovCloud layer info ([#5414](https://github.com/aws-powertools/powertools-lambda-python/issues/5414)) +* **event_handler:** add Terraform payload info for API Gateway HTTP API ([#5351](https://github.com/aws-powertools/powertools-lambda-python/issues/5351)) +* **examples:** temporarily fix SAR version to v2.x ([#5360](https://github.com/aws-powertools/powertools-lambda-python/issues/5360)) +* **layer:** update layer version number ([#5344](https://github.com/aws-powertools/powertools-lambda-python/issues/5344)) +* **upgrade_guide:** update Lambda layer name ([#5347](https://github.com/aws-powertools/powertools-lambda-python/issues/5347)) + +## Features + +* **ci:** GovCloud Layer Workflow ([#5261](https://github.com/aws-powertools/powertools-lambda-python/issues/5261)) +* **logger:** add thread safe logging keys ([#5141](https://github.com/aws-powertools/powertools-lambda-python/issues/5141)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.1.1a0 ([#5353](https://github.com/aws-powertools/powertools-lambda-python/issues/5353)) +* **ci:** Add dump of govcloud layer info in verify step ([#5415](https://github.com/aws-powertools/powertools-lambda-python/issues/5415)) +* **deps:** bump squidfunk/mkdocs-material from `f9cb76d` to `0d4e687` in /docs ([#5395](https://github.com/aws-powertools/powertools-lambda-python/issues/5395)) +* **deps:** bump actions/upload-artifact from 4.4.1 to 4.4.3 ([#5357](https://github.com/aws-powertools/powertools-lambda-python/issues/5357)) +* **deps:** bump squidfunk/mkdocs-material from `8e8b333` to `f9cb76d` in /docs ([#5366](https://github.com/aws-powertools/powertools-lambda-python/issues/5366)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.14 to 3.0.15 ([#5418](https://github.com/aws-powertools/powertools-lambda-python/issues/5418)) +* **deps:** bump jsonpath-ng from 1.6.1 to 1.7.0 ([#5369](https://github.com/aws-powertools/powertools-lambda-python/issues/5369)) +* **deps:** bump squidfunk/mkdocs-material from `0d4e687` to `31eb7f7` in /docs ([#5417](https://github.com/aws-powertools/powertools-lambda-python/issues/5417)) +* **deps:** bump actions/upload-artifact from 4.4.0 to 4.4.3 ([#5373](https://github.com/aws-powertools/powertools-lambda-python/issues/5373)) +* **deps-dev:** bump boto3-stubs from 1.35.38 to 1.35.39 ([#5370](https://github.com/aws-powertools/powertools-lambda-python/issues/5370)) +* **deps-dev:** bump boto3-stubs from 1.35.39 to 1.35.41 ([#5392](https://github.com/aws-powertools/powertools-lambda-python/issues/5392)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.161.1a0 to 2.162.1a0 ([#5386](https://github.com/aws-powertools/powertools-lambda-python/issues/5386)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.274 to 0.1.275 ([#5406](https://github.com/aws-powertools/powertools-lambda-python/issues/5406)) +* **deps-dev:** bump boto3-stubs from 1.35.43 to 1.35.44 ([#5407](https://github.com/aws-powertools/powertools-lambda-python/issues/5407)) +* **deps-dev:** bump cfn-lint from 1.17.2 to 1.18.1 ([#5423](https://github.com/aws-powertools/powertools-lambda-python/issues/5423)) +* **deps-dev:** bump cfn-lint from 1.17.1 to 1.17.2 ([#5408](https://github.com/aws-powertools/powertools-lambda-python/issues/5408)) +* **deps-dev:** bump aws-cdk-lib from 2.161.1 to 2.162.1 ([#5371](https://github.com/aws-powertools/powertools-lambda-python/issues/5371)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.273 to 0.1.274 ([#5394](https://github.com/aws-powertools/powertools-lambda-python/issues/5394)) +* **deps-dev:** bump aws-cdk from 2.161.1 to 2.162.1 ([#5372](https://github.com/aws-powertools/powertools-lambda-python/issues/5372)) +* **deps-dev:** bump boto3-stubs from 1.35.41 to 1.35.42 ([#5397](https://github.com/aws-powertools/powertools-lambda-python/issues/5397)) +* **deps-dev:** bump cfn-lint from 1.16.1 to 1.17.1 ([#5404](https://github.com/aws-powertools/powertools-lambda-python/issues/5404)) +* **deps-dev:** bump mkdocs-material from 9.5.40 to 9.5.41 ([#5393](https://github.com/aws-powertools/powertools-lambda-python/issues/5393)) +* **deps-dev:** bump cfn-lint from 1.16.0 to 1.16.1 ([#5363](https://github.com/aws-powertools/powertools-lambda-python/issues/5363)) +* **deps-dev:** bump boto3-stubs from 1.35.37 to 1.35.38 ([#5364](https://github.com/aws-powertools/powertools-lambda-python/issues/5364)) +* **deps-dev:** bump mkdocs-material from 9.5.39 to 9.5.40 ([#5365](https://github.com/aws-powertools/powertools-lambda-python/issues/5365)) +* **deps-dev:** bump ruff from 0.6.9 to 0.7.0 ([#5403](https://github.com/aws-powertools/powertools-lambda-python/issues/5403)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.275 to 0.1.277 ([#5419](https://github.com/aws-powertools/powertools-lambda-python/issues/5419)) +* **deps-dev:** bump boto3-stubs from 1.35.42 to 1.35.43 ([#5402](https://github.com/aws-powertools/powertools-lambda-python/issues/5402)) +* **deps-dev:** bump boto3-stubs from 1.35.36 to 1.35.37 ([#5356](https://github.com/aws-powertools/powertools-lambda-python/issues/5356)) +* **deps-dev:** bump nox from 2024.4.15 to 2024.10.9 ([#5355](https://github.com/aws-powertools/powertools-lambda-python/issues/5355)) +* **deps-dev:** bump mkdocs-material from 9.5.41 to 9.5.42 ([#5420](https://github.com/aws-powertools/powertools-lambda-python/issues/5420)) +* **deps-dev:** bump boto3-stubs from 1.35.44 to 1.35.45 ([#5421](https://github.com/aws-powertools/powertools-lambda-python/issues/5421)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.161.0a0 to 2.161.1a0 ([#5349](https://github.com/aws-powertools/powertools-lambda-python/issues/5349)) +* **deps-dev:** bump boto3-stubs from 1.35.35 to 1.35.36 ([#5350](https://github.com/aws-powertools/powertools-lambda-python/issues/5350)) +* **deps-dev:** bump sentry-sdk from 2.15.0 to 2.16.0 ([#5348](https://github.com/aws-powertools/powertools-lambda-python/issues/5348)) +* **deps-dev:** bump sentry-sdk from 2.16.0 to 2.17.0 ([#5400](https://github.com/aws-powertools/powertools-lambda-python/issues/5400)) +* **docs:** remove layer callout from data masking docs ([#5377](https://github.com/aws-powertools/powertools-lambda-python/issues/5377)) + + +<a name="v3.1.0"></a> +## [v3.1.0] - 2024-10-08 +## Bug Fixes + +* **ci:** Layer Rename Fix ([#5291](https://github.com/aws-powertools/powertools-lambda-python/issues/5291)) +* **ci:** layer rename ([#5283](https://github.com/aws-powertools/powertools-lambda-python/issues/5283)) +* **idempotency:** fix response hook invocation when function returns None ([#5251](https://github.com/aws-powertools/powertools-lambda-python/issues/5251)) +* **layer:** reverting SSM parameter name ([#5340](https://github.com/aws-powertools/powertools-lambda-python/issues/5340)) +* **layers:** rename Lambda layer name from x86 to x86_64 ([#5226](https://github.com/aws-powertools/powertools-lambda-python/issues/5226)) +* **parser:** fallback to `validate_python` when using `type[Model]` and nested models ([#5313](https://github.com/aws-powertools/powertools-lambda-python/issues/5313)) +* **parser:** revert a regression in v3 when raising ValidationError ([#5259](https://github.com/aws-powertools/powertools-lambda-python/issues/5259)) +* **parser:** make size and etag optional for LifecycleExpiration events in S3 ([#5250](https://github.com/aws-powertools/powertools-lambda-python/issues/5250)) + +## Code Refactoring + +* **examples:** fix issues reported by SonarCloud and Scorecard ([#5315](https://github.com/aws-powertools/powertools-lambda-python/issues/5315)) + +## Documentation + +* **idempotency:** fix description in `Advanced` table ([#5191](https://github.com/aws-powertools/powertools-lambda-python/issues/5191)) +* **metrics:** fix test references ([#5265](https://github.com/aws-powertools/powertools-lambda-python/issues/5265)) +* **public_reference:** add Flyweight as a public reference ([#5322](https://github.com/aws-powertools/powertools-lambda-python/issues/5322)) +* **upgrade_guide:** update upgrade guide with Pydantic information ([#5316](https://github.com/aws-powertools/powertools-lambda-python/issues/5316)) +* **v3:** fix small things in the documentation ([#5224](https://github.com/aws-powertools/powertools-lambda-python/issues/5224)) +* **versioning:** add v2 maintainance mode banner ([#5240](https://github.com/aws-powertools/powertools-lambda-python/issues/5240)) + +## Features + +* **event_source:** add CodeDeploy Lifecycle Hook event ([#5219](https://github.com/aws-powertools/powertools-lambda-python/issues/5219)) +* **openapi:** enable direct list input in Examples model ([#5318](https://github.com/aws-powertools/powertools-lambda-python/issues/5318)) + +## Maintenance + +* version bump +* **ci:** new pre-release 3.0.1a7 ([#5299](https://github.com/aws-powertools/powertools-lambda-python/issues/5299)) +* **ci:** new pre-release 3.0.1a3 ([#5270](https://github.com/aws-powertools/powertools-lambda-python/issues/5270)) +* **ci:** new pre-release 3.0.1a4 ([#5277](https://github.com/aws-powertools/powertools-lambda-python/issues/5277)) +* **ci:** new pre-release 3.0.1a2 ([#5258](https://github.com/aws-powertools/powertools-lambda-python/issues/5258)) +* **ci:** new pre-release 3.0.1a5 ([#5288](https://github.com/aws-powertools/powertools-lambda-python/issues/5288)) +* **ci:** new pre-release 3.0.1a9 ([#5337](https://github.com/aws-powertools/powertools-lambda-python/issues/5337)) +* **ci:** new pre-release 3.0.1a8 ([#5323](https://github.com/aws-powertools/powertools-lambda-python/issues/5323)) +* **ci:** new pre-release 3.0.1a0 ([#5220](https://github.com/aws-powertools/powertools-lambda-python/issues/5220)) +* **ci:** new pre-release 3.0.1a1 ([#5247](https://github.com/aws-powertools/powertools-lambda-python/issues/5247)) +* **ci:** new pre-release 3.0.1a6 ([#5293](https://github.com/aws-powertools/powertools-lambda-python/issues/5293)) +* **deps:** bump actions/download-artifact from 4.1.7 to 4.1.8 ([#5203](https://github.com/aws-powertools/powertools-lambda-python/issues/5203)) +* **deps:** bump squidfunk/mkdocs-material from `22a429f` to `08fbf58` in /docs ([#5243](https://github.com/aws-powertools/powertools-lambda-python/issues/5243)) +* **deps:** bump docker/setup-buildx-action from 3.6.1 to 3.7.0 ([#5298](https://github.com/aws-powertools/powertools-lambda-python/issues/5298)) +* **deps:** bump actions/checkout from 4.1.7 to 4.2.0 ([#5244](https://github.com/aws-powertools/powertools-lambda-python/issues/5244)) +* **deps:** bump actions/setup-node from 4.0.3 to 4.0.4 ([#5186](https://github.com/aws-powertools/powertools-lambda-python/issues/5186)) +* **deps:** bump docker/setup-buildx-action from 3.7.0 to 3.7.1 ([#5310](https://github.com/aws-powertools/powertools-lambda-python/issues/5310)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.10.2 to 1.10.3 ([#5311](https://github.com/aws-powertools/powertools-lambda-python/issues/5311)) +* **deps:** bump squidfunk/mkdocs-material from `a2e3a31` to `22a429f` in /docs ([#5201](https://github.com/aws-powertools/powertools-lambda-python/issues/5201)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.10.1 to 1.10.2 ([#5202](https://github.com/aws-powertools/powertools-lambda-python/issues/5202)) +* **deps:** bump actions/checkout from 4.2.0 to 4.2.1 ([#5329](https://github.com/aws-powertools/powertools-lambda-python/issues/5329)) +* **deps:** bump squidfunk/mkdocs-material from `08fbf58` to `7aea359` in /docs ([#5253](https://github.com/aws-powertools/powertools-lambda-python/issues/5253)) +* **deps:** bump actions/setup-python from 5.1.0 to 5.2.0 ([#5204](https://github.com/aws-powertools/powertools-lambda-python/issues/5204)) +* **deps:** bump codecov/codecov-action from 4.5.0 to 4.6.0 ([#5287](https://github.com/aws-powertools/powertools-lambda-python/issues/5287)) +* **deps:** bump redis from 5.1.0 to 5.1.1 ([#5331](https://github.com/aws-powertools/powertools-lambda-python/issues/5331)) +* **deps:** bump actions/checkout from 4.1.6 to 4.1.7 ([#5206](https://github.com/aws-powertools/powertools-lambda-python/issues/5206)) +* **deps:** bump actions/upload-artifact from 4.4.0 to 4.4.1 ([#5328](https://github.com/aws-powertools/powertools-lambda-python/issues/5328)) +* **deps:** bump actions/upload-artifact from 4.3.3 to 4.4.0 ([#5217](https://github.com/aws-powertools/powertools-lambda-python/issues/5217)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.12 to 3.0.13 ([#5276](https://github.com/aws-powertools/powertools-lambda-python/issues/5276)) +* **deps:** bump redis from 5.0.8 to 5.1.0 ([#5264](https://github.com/aws-powertools/powertools-lambda-python/issues/5264)) +* **deps:** bump datadog-lambda from 6.98.0 to 6.99.0 ([#5333](https://github.com/aws-powertools/powertools-lambda-python/issues/5333)) +* **deps:** bump squidfunk/mkdocs-material from `7aea359` to `8e8b333` in /docs ([#5272](https://github.com/aws-powertools/powertools-lambda-python/issues/5272)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.13 to 3.0.14 ([#5330](https://github.com/aws-powertools/powertools-lambda-python/issues/5330)) +* **deps:** bump docker/setup-qemu-action from 3.0.0 to 3.2.0 ([#5205](https://github.com/aws-powertools/powertools-lambda-python/issues/5205)) +* **deps-dev:** bump mkdocs-material from 9.5.38 to 9.5.39 ([#5273](https://github.com/aws-powertools/powertools-lambda-python/issues/5273)) +* **deps-dev:** bump cfn-lint from 1.15.1 to 1.15.2 ([#5274](https://github.com/aws-powertools/powertools-lambda-python/issues/5274)) +* **deps-dev:** bump boto3-stubs from 1.35.28 to 1.35.29 ([#5263](https://github.com/aws-powertools/powertools-lambda-python/issues/5263)) +* **deps-dev:** bump boto3-stubs from 1.35.34 to 1.35.35 ([#5334](https://github.com/aws-powertools/powertools-lambda-python/issues/5334)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.270 to 0.1.271 ([#5284](https://github.com/aws-powertools/powertools-lambda-python/issues/5284)) +* **deps-dev:** bump mkdocs-material from 9.5.37 to 9.5.38 ([#5255](https://github.com/aws-powertools/powertools-lambda-python/issues/5255)) +* **deps-dev:** bump ruff from 0.6.7 to 0.6.8 ([#5254](https://github.com/aws-powertools/powertools-lambda-python/issues/5254)) +* **deps-dev:** bump boto3-stubs from 1.35.27 to 1.35.28 ([#5256](https://github.com/aws-powertools/powertools-lambda-python/issues/5256)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.269 to 0.1.270 ([#5257](https://github.com/aws-powertools/powertools-lambda-python/issues/5257)) +* **deps-dev:** bump sentry-sdk from 2.14.0 to 2.15.0 ([#5285](https://github.com/aws-powertools/powertools-lambda-python/issues/5285)) +* **deps-dev:** bump boto3-stubs from 1.35.29 to 1.35.31 ([#5286](https://github.com/aws-powertools/powertools-lambda-python/issues/5286)) +* **deps-dev:** bump boto3-stubs from 1.35.31 to 1.35.32 ([#5292](https://github.com/aws-powertools/powertools-lambda-python/issues/5292)) +* **deps-dev:** bump aws-cdk-lib from 2.161.0 to 2.161.1 ([#5335](https://github.com/aws-powertools/powertools-lambda-python/issues/5335)) +* **deps-dev:** bump boto3-stubs from 1.35.32 to 1.35.33 ([#5295](https://github.com/aws-powertools/powertools-lambda-python/issues/5295)) +* **deps-dev:** bump types-python-dateutil from 2.9.0.20240906 to 2.9.0.20241003 ([#5296](https://github.com/aws-powertools/powertools-lambda-python/issues/5296)) +* **deps-dev:** bump boto3-stubs from 1.35.26 to 1.35.27 ([#5242](https://github.com/aws-powertools/powertools-lambda-python/issues/5242)) +* **deps-dev:** bump mkdocs-material from 9.5.36 to 9.5.37 ([#5241](https://github.com/aws-powertools/powertools-lambda-python/issues/5241)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.271 to 0.1.272 ([#5297](https://github.com/aws-powertools/powertools-lambda-python/issues/5297)) +* **deps-dev:** bump boto3-stubs from 1.35.25 to 1.35.26 ([#5234](https://github.com/aws-powertools/powertools-lambda-python/issues/5234)) +* **deps-dev:** bump aws-cdk from 2.159.1 to 2.160.0 ([#5233](https://github.com/aws-powertools/powertools-lambda-python/issues/5233)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.159.1a0 to 2.160.0a0 ([#5235](https://github.com/aws-powertools/powertools-lambda-python/issues/5235)) +* **deps-dev:** bump aws-cdk-lib from 2.159.1 to 2.160.0 ([#5230](https://github.com/aws-powertools/powertools-lambda-python/issues/5230)) +* **deps-dev:** bump cfn-lint from 1.15.0 to 1.15.1 ([#5232](https://github.com/aws-powertools/powertools-lambda-python/issues/5232)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.158.0a0 to 2.159.1a0 ([#5231](https://github.com/aws-powertools/powertools-lambda-python/issues/5231)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.268 to 0.1.269 ([#5229](https://github.com/aws-powertools/powertools-lambda-python/issues/5229)) +* **deps-dev:** bump aws-cdk-lib from 2.160.0 to 2.161.0 ([#5304](https://github.com/aws-powertools/powertools-lambda-python/issues/5304)) +* **deps-dev:** bump boto3-stubs from 1.35.33 to 1.35.34 ([#5306](https://github.com/aws-powertools/powertools-lambda-python/issues/5306)) +* **deps-dev:** bump types-redis from 4.6.0.20240903 to 4.6.0.20241004 ([#5307](https://github.com/aws-powertools/powertools-lambda-python/issues/5307)) +* **deps-dev:** bump aws-cdk-lib from 2.158.0 to 2.159.1 ([#5208](https://github.com/aws-powertools/powertools-lambda-python/issues/5208)) +* **deps-dev:** bump ruff from 0.6.4 to 0.6.7 ([#5207](https://github.com/aws-powertools/powertools-lambda-python/issues/5207)) +* **deps-dev:** bump aws-cdk from 2.157.0 to 2.159.1 ([#5194](https://github.com/aws-powertools/powertools-lambda-python/issues/5194)) +* **deps-dev:** bump aws-cdk from 2.160.0 to 2.161.0 ([#5309](https://github.com/aws-powertools/powertools-lambda-python/issues/5309)) +* **deps-dev:** bump ruff from 0.6.8 to 0.6.9 ([#5308](https://github.com/aws-powertools/powertools-lambda-python/issues/5308)) +* **deps-dev:** bump cfn-lint from 1.15.2 to 1.16.0 ([#5305](https://github.com/aws-powertools/powertools-lambda-python/issues/5305)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.160.0a0 to 2.161.0a0 ([#5332](https://github.com/aws-powertools/powertools-lambda-python/issues/5332)) +* **deps-dev:** bump aws-cdk from 2.161.0 to 2.161.1 ([#5327](https://github.com/aws-powertools/powertools-lambda-python/issues/5327)) +* **deps-dev:** bump mkdocs-material from 9.5.34 to 9.5.36 ([#5210](https://github.com/aws-powertools/powertools-lambda-python/issues/5210)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.272 to 0.1.273 ([#5336](https://github.com/aws-powertools/powertools-lambda-python/issues/5336)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.264 to 0.1.268 ([#5216](https://github.com/aws-powertools/powertools-lambda-python/issues/5216)) +* **deps-dev:** bump multiprocess from 0.70.16 to 0.70.17 ([#5275](https://github.com/aws-powertools/powertools-lambda-python/issues/5275)) +* **deps-dev:** bump boto3-stubs from 1.35.17 to 1.35.25 ([#5218](https://github.com/aws-powertools/powertools-lambda-python/issues/5218)) +* **deps-dev:** bump bandit from 1.7.9 to 1.7.10 ([#5214](https://github.com/aws-powertools/powertools-lambda-python/issues/5214)) +* **deps-dev:** bump cfn-lint from 1.12.4 to 1.15.0 ([#5215](https://github.com/aws-powertools/powertools-lambda-python/issues/5215)) +* **docs:** recreate requirements.txt file for mkdocs container ([#5246](https://github.com/aws-powertools/powertools-lambda-python/issues/5246)) +* **tests:** fix e2e tests in Idempotency utility ([#5280](https://github.com/aws-powertools/powertools-lambda-python/issues/5280)) + + +<a name="v3.0.0"></a> +## [v3.0.0] - 2024-09-23 +## Bug Fixes + +* **v3:** revert unnecessary changes that impacts v3 ([#5087](https://github.com/aws-powertools/powertools-lambda-python/issues/5087)) + +## Code Refactoring + +* **batch:** add from __future__ import annotations ([#4993](https://github.com/aws-powertools/powertools-lambda-python/issues/4993)) +* **batch_processing:** mark batch_processor and async_batch_processor as deprecated ([#4910](https://github.com/aws-powertools/powertools-lambda-python/issues/4910)) +* **data_classes:** add from __future__ import annotations ([#4939](https://github.com/aws-powertools/powertools-lambda-python/issues/4939)) +* **data_masking:** add from __future__ import annotations ([#4945](https://github.com/aws-powertools/powertools-lambda-python/issues/4945)) +* **event_handler:** add from __future__ import annotations ([#4992](https://github.com/aws-powertools/powertools-lambda-python/issues/4992)) +* **event_handler:** add from __future__ import annotations in the Middlewares ([#4975](https://github.com/aws-powertools/powertools-lambda-python/issues/4975)) +* **feature_flags:** add from __future__ import annotations ([#4960](https://github.com/aws-powertools/powertools-lambda-python/issues/4960)) +* **general:** drop pydantic v1 ([#4305](https://github.com/aws-powertools/powertools-lambda-python/issues/4305)) +* **idempotency:** add from __future__ import annotations ([#4961](https://github.com/aws-powertools/powertools-lambda-python/issues/4961)) +* **jmespath_utils:** deprecate extract_data_from_envelope in favor of query ([#4907](https://github.com/aws-powertools/powertools-lambda-python/issues/4907)) +* **jmespath_utils:** add from __future__ import annotations ([#4962](https://github.com/aws-powertools/powertools-lambda-python/issues/4962)) +* **logging:** add from __future__ import annotations ([#4940](https://github.com/aws-powertools/powertools-lambda-python/issues/4940)) +* **metrics:** add from __future__ import annotations ([#4944](https://github.com/aws-powertools/powertools-lambda-python/issues/4944)) +* **middleware_factory:** add from __future__ import annotations ([#4941](https://github.com/aws-powertools/powertools-lambda-python/issues/4941)) +* **openapi:** add from __future__ import annotations ([#4990](https://github.com/aws-powertools/powertools-lambda-python/issues/4990)) +* **parameters:** deprecate the config parameter in favor of boto_config ([#4893](https://github.com/aws-powertools/powertools-lambda-python/issues/4893)) +* **parameters:** add top-level get_multiple method in SSMProvider class ([#4785](https://github.com/aws-powertools/powertools-lambda-python/issues/4785)) +* **parameters:** add from __future__ import annotations ([#4976](https://github.com/aws-powertools/powertools-lambda-python/issues/4976)) +* **parameters:** increase default max_age (cache) to 5 minutes ([#4279](https://github.com/aws-powertools/powertools-lambda-python/issues/4279)) +* **parser:** add from __future__ import annotations ([#4977](https://github.com/aws-powertools/powertools-lambda-python/issues/4977)) +* **parser:** add from __future__ import annotations ([#4983](https://github.com/aws-powertools/powertools-lambda-python/issues/4983)) +* **shared:** add from __future__ import annotations ([#4942](https://github.com/aws-powertools/powertools-lambda-python/issues/4942)) +* **streaming:** add from __future__ import annotations ([#4987](https://github.com/aws-powertools/powertools-lambda-python/issues/4987)) +* **tracing:** add from __future__ import annotations ([#4943](https://github.com/aws-powertools/powertools-lambda-python/issues/4943)) +* **typing:** add from __future__ import annotations ([#4985](https://github.com/aws-powertools/powertools-lambda-python/issues/4985)) +* **typing:** enable TCH, UP and FA100 ruff rules ([#5017](https://github.com/aws-powertools/powertools-lambda-python/issues/5017)) +* **typing:** reduce aws_lambda_powertools.shared.types usage ([#4896](https://github.com/aws-powertools/powertools-lambda-python/issues/4896)) +* **typing:** enable boto3 implicit type annotations ([#4692](https://github.com/aws-powertools/powertools-lambda-python/issues/4692)) +* **typing:** move more types into TYPE_CHECKING ([#5088](https://github.com/aws-powertools/powertools-lambda-python/issues/5088)) +* **validation:** add from __future__ import annotations ([#4984](https://github.com/aws-powertools/powertools-lambda-python/issues/4984)) + +## Documentation + +* **upgrade_guide:** create upgrade guide from v2 to v3 ([#5028](https://github.com/aws-powertools/powertools-lambda-python/issues/5028)) + +## Features + +* **data_classes:** return empty dict or list instead of None ([#4606](https://github.com/aws-powertools/powertools-lambda-python/issues/4606)) +* **event_handler:** Ensure Bedrock Agents resolver works with Pydantic v2 ([#5156](https://github.com/aws-powertools/powertools-lambda-python/issues/5156)) +* **idempotency:** simplify access to expiration time in `DataRecord` class ([#5082](https://github.com/aws-powertools/powertools-lambda-python/issues/5082)) +* **lambda-layer:** add pipeline to build Lambda layer in v3 ([#4826](https://github.com/aws-powertools/powertools-lambda-python/issues/4826)) +* **parser:** Adds DDB deserialization to DynamoDBStreamChangedRecordModel ([#4401](https://github.com/aws-powertools/powertools-lambda-python/issues/4401)) +* **parser:** Allow primitive data types to be parsed using TypeAdapter ([#4502](https://github.com/aws-powertools/powertools-lambda-python/issues/4502)) +* **v3:** merging develop into v3 ([#5160](https://github.com/aws-powertools/powertools-lambda-python/issues/5160)) + +## Maintenance + +* version bump +* **ci:** fix bump poetry version ([#5211](https://github.com/aws-powertools/powertools-lambda-python/issues/5211)) +* **ci:** fix working-directory in v3 layer pipeline ([#5199](https://github.com/aws-powertools/powertools-lambda-python/issues/5199)) +* **ci:** fix Redis e2e tests in v3 branch ([#4852](https://github.com/aws-powertools/powertools-lambda-python/issues/4852)) +* **ci:** fix e2e tests in v3 branch ([#4848](https://github.com/aws-powertools/powertools-lambda-python/issues/4848)) +* **ci:** add the aws-encryption-sdk dependency in the Lambda layer ([#4630](https://github.com/aws-powertools/powertools-lambda-python/issues/4630)) +* **ci:** bump pydantic library to 2.0+ and boto3 to 1.34.32 ([#4235](https://github.com/aws-powertools/powertools-lambda-python/issues/4235)) +* **v3:** merging develop into v3 - 15/05/2024 ([#4335](https://github.com/aws-powertools/powertools-lambda-python/issues/4335)) +* **v3:** merging develop into v3 ([#4267](https://github.com/aws-powertools/powertools-lambda-python/issues/4267)) + + +<a name="v2.43.1"></a> +## [v2.43.1] - 2024-08-12 +## Bug Fixes + +* **event_source:** fix regression when working with zero numbers in DynamoDBStreamEvent ([#4932](https://github.com/aws-powertools/powertools-lambda-python/issues/4932)) + +## Maintenance + +* version bump +* **ci:** new pre-release 2.43.1a0 ([#4920](https://github.com/aws-powertools/powertools-lambda-python/issues/4920)) +* **ci:** new pre-release 2.43.1a1 ([#4926](https://github.com/aws-powertools/powertools-lambda-python/issues/4926)) +* **ci:** new pre-release 2.42.1a9 ([#4912](https://github.com/aws-powertools/powertools-lambda-python/issues/4912)) +* **deps-dev:** bump ruff from 0.5.6 to 0.5.7 ([#4918](https://github.com/aws-powertools/powertools-lambda-python/issues/4918)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.234 to 0.1.238 ([#4917](https://github.com/aws-powertools/powertools-lambda-python/issues/4917)) +* **deps-dev:** bump mypy-boto3-ssm from 1.34.132 to 1.34.158 in the boto-typing group ([#4921](https://github.com/aws-powertools/powertools-lambda-python/issues/4921)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.238 to 0.1.242 ([#4922](https://github.com/aws-powertools/powertools-lambda-python/issues/4922)) +* **deps-dev:** bump cfn-lint from 1.9.6 to 1.9.7 ([#4923](https://github.com/aws-powertools/powertools-lambda-python/issues/4923)) +* **deps-dev:** bump cfn-lint from 1.9.5 to 1.9.6 ([#4916](https://github.com/aws-powertools/powertools-lambda-python/issues/4916)) + + +<a name="v2.43.0"></a> +## [v2.43.0] - 2024-08-08 +## Bug Fixes + +* **data_class:** ensure DynamoDBStreamEvent conforms to decimal limits ([#4863](https://github.com/aws-powertools/powertools-lambda-python/issues/4863)) + +## Code Refactoring + +* **test:** make CORS test consistent with expected behavior ([#4882](https://github.com/aws-powertools/powertools-lambda-python/issues/4882)) +* **tracer:** make capture_lambda_handler type more generic ([#4796](https://github.com/aws-powertools/powertools-lambda-python/issues/4796)) + +## Documentation + +* fix type vs. field in comment ([#4832](https://github.com/aws-powertools/powertools-lambda-python/issues/4832)) +* **public_reference:** add CHS Inc. as a public reference ([#4885](https://github.com/aws-powertools/powertools-lambda-python/issues/4885)) +* **public_reference:** add LocalStack as a public reference ([#4858](https://github.com/aws-powertools/powertools-lambda-python/issues/4858)) +* **public_reference:** add Caylent as a public reference ([#4822](https://github.com/aws-powertools/powertools-lambda-python/issues/4822)) + +## Features + +* **metrics:** add unit None for CloudWatch EMF Metrics ([#4904](https://github.com/aws-powertools/powertools-lambda-python/issues/4904)) +* **validation:** returns output from validate function ([#4839](https://github.com/aws-powertools/powertools-lambda-python/issues/4839)) + +## Maintenance + +* version bump +* **ci:** new pre-release 2.42.1a5 ([#4868](https://github.com/aws-powertools/powertools-lambda-python/issues/4868)) +* **ci:** new pre-release 2.42.1a8 ([#4903](https://github.com/aws-powertools/powertools-lambda-python/issues/4903)) +* **ci:** new pre-release 2.42.1a0 ([#4827](https://github.com/aws-powertools/powertools-lambda-python/issues/4827)) +* **ci:** new pre-release 2.42.1a7 ([#4894](https://github.com/aws-powertools/powertools-lambda-python/issues/4894)) +* **ci:** new pre-release 2.42.1a1 ([#4837](https://github.com/aws-powertools/powertools-lambda-python/issues/4837)) +* **ci:** new pre-release 2.42.1a3 ([#4856](https://github.com/aws-powertools/powertools-lambda-python/issues/4856)) +* **ci:** new pre-release 2.42.1a4 ([#4864](https://github.com/aws-powertools/powertools-lambda-python/issues/4864)) +* **ci:** new pre-release 2.42.1a6 ([#4884](https://github.com/aws-powertools/powertools-lambda-python/issues/4884)) +* **ci:** new pre-release 2.42.1a2 ([#4847](https://github.com/aws-powertools/powertools-lambda-python/issues/4847)) +* **deps:** bump golang.org/x/sync from 0.7.0 to 0.8.0 in /layer/scripts/layer-balancer in the layer-balancer group ([#4892](https://github.com/aws-powertools/powertools-lambda-python/issues/4892)) +* **deps:** bump actions/upload-artifact from 4.3.5 to 4.3.6 ([#4901](https://github.com/aws-powertools/powertools-lambda-python/issues/4901)) +* **deps:** bump actions/upload-artifact from 4.3.4 to 4.3.5 ([#4871](https://github.com/aws-powertools/powertools-lambda-python/issues/4871)) +* **deps:** bump ossf/scorecard-action from 2.3.3 to 2.4.0 ([#4829](https://github.com/aws-powertools/powertools-lambda-python/issues/4829)) +* **deps:** bump squidfunk/mkdocs-material from `257eca8` to `9919d6e` in /docs ([#4878](https://github.com/aws-powertools/powertools-lambda-python/issues/4878)) +* **deps:** bump docker/setup-buildx-action from 3.5.0 to 3.6.1 ([#4844](https://github.com/aws-powertools/powertools-lambda-python/issues/4844)) +* **deps:** bump redis from 5.0.7 to 5.0.8 ([#4854](https://github.com/aws-powertools/powertools-lambda-python/issues/4854)) +* **deps-dev:** bump ruff from 0.5.5 to 0.5.6 ([#4874](https://github.com/aws-powertools/powertools-lambda-python/issues/4874)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.34.83 to 1.34.153 in the boto-typing group ([#4887](https://github.com/aws-powertools/powertools-lambda-python/issues/4887)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.224 to 0.1.228 ([#4867](https://github.com/aws-powertools/powertools-lambda-python/issues/4867)) +* **deps-dev:** bump cfn-lint from 1.9.1 to 1.9.3 ([#4866](https://github.com/aws-powertools/powertools-lambda-python/issues/4866)) +* **deps-dev:** bump sentry-sdk from 2.11.0 to 2.12.0 ([#4861](https://github.com/aws-powertools/powertools-lambda-python/issues/4861)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.228 to 0.1.230 ([#4876](https://github.com/aws-powertools/powertools-lambda-python/issues/4876)) +* **deps-dev:** bump black from 24.4.2 to 24.8.0 ([#4873](https://github.com/aws-powertools/powertools-lambda-python/issues/4873)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.223 to 0.1.224 ([#4855](https://github.com/aws-powertools/powertools-lambda-python/issues/4855)) +* **deps-dev:** bump mypy-boto3-logs from 1.34.66 to 1.34.151 in the boto-typing group ([#4853](https://github.com/aws-powertools/powertools-lambda-python/issues/4853)) +* **deps-dev:** bump coverage from 7.6.0 to 7.6.1 ([#4888](https://github.com/aws-powertools/powertools-lambda-python/issues/4888)) +* **deps-dev:** bump cfn-lint from 1.8.2 to 1.9.1 ([#4851](https://github.com/aws-powertools/powertools-lambda-python/issues/4851)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.150.0a0 to 2.151.0a0 ([#4889](https://github.com/aws-powertools/powertools-lambda-python/issues/4889)) +* **deps-dev:** bump aws-cdk from 2.150.0 to 2.151.0 ([#4872](https://github.com/aws-powertools/powertools-lambda-python/issues/4872)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.219 to 0.1.222 ([#4836](https://github.com/aws-powertools/powertools-lambda-python/issues/4836)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.222 to 0.1.223 ([#4843](https://github.com/aws-powertools/powertools-lambda-python/issues/4843)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.233 to 0.1.234 ([#4909](https://github.com/aws-powertools/powertools-lambda-python/issues/4909)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.230 to 0.1.231 ([#4891](https://github.com/aws-powertools/powertools-lambda-python/issues/4891)) +* **deps-dev:** bump cfn-lint from 1.9.3 to 1.9.5 ([#4890](https://github.com/aws-powertools/powertools-lambda-python/issues/4890)) +* **deps-dev:** bump pytest from 8.3.1 to 8.3.2 ([#4824](https://github.com/aws-powertools/powertools-lambda-python/issues/4824)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.231 to 0.1.233 ([#4900](https://github.com/aws-powertools/powertools-lambda-python/issues/4900)) +* **deps-dev:** bump mkdocs-material from 9.5.30 to 9.5.31 ([#4877](https://github.com/aws-powertools/powertools-lambda-python/issues/4877)) +* **deps-dev:** bump types-redis from 4.6.0.20240425 to 4.6.0.20240726 ([#4831](https://github.com/aws-powertools/powertools-lambda-python/issues/4831)) +* **deps-dev:** bump ruff from 0.5.4 to 0.5.5 ([#4823](https://github.com/aws-powertools/powertools-lambda-python/issues/4823)) +* **deps-dev:** bump aws-cdk-lib from 2.150.0 to 2.151.0 ([#4875](https://github.com/aws-powertools/powertools-lambda-python/issues/4875)) +* **deps-dev:** bump types-redis from 4.6.0.20240726 to 4.6.0.20240806 ([#4899](https://github.com/aws-powertools/powertools-lambda-python/issues/4899)) +* **maintenance:** add Banxware customer refernece ([#4841](https://github.com/aws-powertools/powertools-lambda-python/issues/4841)) + + +<a name="v2.42.0"></a> +## [v2.42.0] - 2024-07-25 +## Bug Fixes + +* **idempotency:** ensure in_progress_expiration field is set on Lambda timeout. ([#4773](https://github.com/aws-powertools/powertools-lambda-python/issues/4773)) + +## Documentation + +* **idempotency:** improve navigation, wording, and new section on guarantees ([#4613](https://github.com/aws-powertools/powertools-lambda-python/issues/4613)) + +## Features + +* **event_handler:** add OpenAPI extensions ([#4703](https://github.com/aws-powertools/powertools-lambda-python/issues/4703)) + +## Maintenance + +* version bump +* **ci:** new pre-release 2.41.1a4 ([#4772](https://github.com/aws-powertools/powertools-lambda-python/issues/4772)) +* **ci:** new pre-release 2.41.1a0 ([#4749](https://github.com/aws-powertools/powertools-lambda-python/issues/4749)) +* **ci:** new pre-release 2.41.1a1 ([#4756](https://github.com/aws-powertools/powertools-lambda-python/issues/4756)) +* **ci:** new pre-release 2.41.1a2 ([#4758](https://github.com/aws-powertools/powertools-lambda-python/issues/4758)) +* **ci:** new pre-release 2.41.1a9 ([#4808](https://github.com/aws-powertools/powertools-lambda-python/issues/4808)) +* **ci:** new pre-release 2.41.1a3 ([#4766](https://github.com/aws-powertools/powertools-lambda-python/issues/4766)) +* **ci:** new pre-release 2.41.1a8 ([#4802](https://github.com/aws-powertools/powertools-lambda-python/issues/4802)) +* **ci:** new pre-release 2.41.1a5 ([#4777](https://github.com/aws-powertools/powertools-lambda-python/issues/4777)) +* **ci:** new pre-release 2.41.1a6 ([#4783](https://github.com/aws-powertools/powertools-lambda-python/issues/4783)) +* **ci:** new pre-release 2.41.1a7 ([#4792](https://github.com/aws-powertools/powertools-lambda-python/issues/4792)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.27.26 to 1.27.27 in /layer/scripts/layer-balancer in the layer-balancer group ([#4779](https://github.com/aws-powertools/powertools-lambda-python/issues/4779)) +* **deps:** bump aws-actions/closed-issue-message from 8b6324312193476beecf11f8e8539d73a3553bf4 to 80edfc24bdf1283400eb04d20a8a605ae8bf7d48 ([#4786](https://github.com/aws-powertools/powertools-lambda-python/issues/4786)) +* **deps:** bump actions/dependency-review-action from 4.3.3 to 4.3.4 ([#4753](https://github.com/aws-powertools/powertools-lambda-python/issues/4753)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4745](https://github.com/aws-powertools/powertools-lambda-python/issues/4745)) +* **deps:** bump datadog-lambda from 6.96.0 to 6.97.0 ([#4770](https://github.com/aws-powertools/powertools-lambda-python/issues/4770)) +* **deps:** bump docker/setup-buildx-action from 3.4.0 to 3.5.0 ([#4801](https://github.com/aws-powertools/powertools-lambda-python/issues/4801)) +* **deps:** bump docker/setup-qemu-action from 3.1.0 to 3.2.0 ([#4800](https://github.com/aws-powertools/powertools-lambda-python/issues/4800)) +* **deps-dev:** bump cfn-lint from 1.8.1 to 1.8.2 ([#4788](https://github.com/aws-powertools/powertools-lambda-python/issues/4788)) +* **deps-dev:** bump pytest-asyncio from 0.23.7 to 0.23.8 ([#4776](https://github.com/aws-powertools/powertools-lambda-python/issues/4776)) +* **deps-dev:** bump pytest from 8.2.2 to 8.3.1 ([#4799](https://github.com/aws-powertools/powertools-lambda-python/issues/4799)) +* **deps-dev:** bump aws-cdk-lib from 2.148.1 to 2.150.0 ([#4806](https://github.com/aws-powertools/powertools-lambda-python/issues/4806)) +* **deps-dev:** bump ruff from 0.5.3 to 0.5.4 ([#4798](https://github.com/aws-powertools/powertools-lambda-python/issues/4798)) +* **deps-dev:** bump cfn-lint from 1.6.1 to 1.8.1 ([#4780](https://github.com/aws-powertools/powertools-lambda-python/issues/4780)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.211 to 0.1.212 ([#4769](https://github.com/aws-powertools/powertools-lambda-python/issues/4769)) +* **deps-dev:** bump ruff from 0.5.2 to 0.5.3 ([#4781](https://github.com/aws-powertools/powertools-lambda-python/issues/4781)) +* **deps-dev:** bump mkdocs-material from 9.5.28 to 9.5.29 ([#4764](https://github.com/aws-powertools/powertools-lambda-python/issues/4764)) +* **deps-dev:** bump aws-cdk from 2.148.0 to 2.149.0 ([#4765](https://github.com/aws-powertools/powertools-lambda-python/issues/4765)) +* **deps-dev:** bump ruff from 0.5.1 to 0.5.2 ([#4762](https://github.com/aws-powertools/powertools-lambda-python/issues/4762)) +* **deps-dev:** bump sentry-sdk from 2.9.0 to 2.10.0 ([#4763](https://github.com/aws-powertools/powertools-lambda-python/issues/4763)) +* **deps-dev:** bump aws-cdk from 2.149.0 to 2.150.0 ([#4805](https://github.com/aws-powertools/powertools-lambda-python/issues/4805)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.207 to 0.1.211 ([#4760](https://github.com/aws-powertools/powertools-lambda-python/issues/4760)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.34.131 to 1.34.148 in the boto-typing group ([#4812](https://github.com/aws-powertools/powertools-lambda-python/issues/4812)) +* **deps-dev:** bump sentry-sdk from 2.10.0 to 2.11.0 ([#4815](https://github.com/aws-powertools/powertools-lambda-python/issues/4815)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.212 to 0.1.219 ([#4817](https://github.com/aws-powertools/powertools-lambda-python/issues/4817)) +* **deps-dev:** bump cfn-lint from 1.6.0 to 1.6.1 ([#4751](https://github.com/aws-powertools/powertools-lambda-python/issues/4751)) +* **deps-dev:** bump mkdocs-material from 9.5.29 to 9.5.30 ([#4807](https://github.com/aws-powertools/powertools-lambda-python/issues/4807)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.148.1a0 to 2.150.0a0 ([#4813](https://github.com/aws-powertools/powertools-lambda-python/issues/4813)) +* **deps-dev:** bump cfn-lint from 1.5.3 to 1.6.0 ([#4747](https://github.com/aws-powertools/powertools-lambda-python/issues/4747)) +* **deps-dev:** bump coverage from 7.5.4 to 7.6.0 ([#4746](https://github.com/aws-powertools/powertools-lambda-python/issues/4746)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.206 to 0.1.207 ([#4748](https://github.com/aws-powertools/powertools-lambda-python/issues/4748)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.34.128 to 1.34.145 in the boto-typing group ([#4787](https://github.com/aws-powertools/powertools-lambda-python/issues/4787)) +* **docs:** Add lambda layer policy to versioning docs ([#4811](https://github.com/aws-powertools/powertools-lambda-python/issues/4811)) +* **logger:** use package logger over source logger to reduce noise ([#4793](https://github.com/aws-powertools/powertools-lambda-python/issues/4793)) + + +<a name="v2.41.0"></a> +## [v2.41.0] - 2024-07-11 +## Bug Fixes + +* **event_handler:** make the max_age attribute comply with RFC specification ([#4731](https://github.com/aws-powertools/powertools-lambda-python/issues/4731)) +* **event_handler:** disable allow-credentials header when origin allow_origin is * ([#4638](https://github.com/aws-powertools/powertools-lambda-python/issues/4638)) +* **event_handler:** convert null body to empty string in ALBResolver to avoid HTTP 502 ([#4683](https://github.com/aws-powertools/powertools-lambda-python/issues/4683)) +* **event_handler:** custom serializer recursive values when using data validation ([#4664](https://github.com/aws-powertools/powertools-lambda-python/issues/4664)) + +## Documentation + +* **i-made-this:** Bedrock agents with Powertools for AWS Lambda ([#4705](https://github.com/aws-powertools/powertools-lambda-python/issues/4705)) +* **public_reference:** add BusPatrol as a public reference ([#4713](https://github.com/aws-powertools/powertools-lambda-python/issues/4713)) + +## Features + +* **batch:** add option to not raise `BatchProcessingError` exception when the entire batch fails ([#4719](https://github.com/aws-powertools/powertools-lambda-python/issues/4719)) +* **feature_flags:** allow customers to bring their own boto3 client and session ([#4717](https://github.com/aws-powertools/powertools-lambda-python/issues/4717)) +* **parser:** add support for API Gateway Lambda authorizer events ([#4718](https://github.com/aws-powertools/powertools-lambda-python/issues/4718)) + +## Maintenance + +* version bump +* Add token to codecov action ([#4682](https://github.com/aws-powertools/powertools-lambda-python/issues/4682)) +* **ci:** new pre-release 2.40.2a5 ([#4706](https://github.com/aws-powertools/powertools-lambda-python/issues/4706)) +* **ci:** new pre-release 2.40.2a0 ([#4665](https://github.com/aws-powertools/powertools-lambda-python/issues/4665)) +* **ci:** new pre-release 2.40.2a8 ([#4737](https://github.com/aws-powertools/powertools-lambda-python/issues/4737)) +* **ci:** new pre-release 2.40.2a7 ([#4726](https://github.com/aws-powertools/powertools-lambda-python/issues/4726)) +* **ci:** new pre-release 2.40.2a1 ([#4669](https://github.com/aws-powertools/powertools-lambda-python/issues/4669)) +* **ci:** new pre-release 2.40.2a2 ([#4679](https://github.com/aws-powertools/powertools-lambda-python/issues/4679)) +* **ci:** new pre-release 2.40.2a3 ([#4688](https://github.com/aws-powertools/powertools-lambda-python/issues/4688)) +* **ci:** new pre-release 2.40.2a6 ([#4715](https://github.com/aws-powertools/powertools-lambda-python/issues/4715)) +* **ci:** new pre-release 2.40.2a4 ([#4694](https://github.com/aws-powertools/powertools-lambda-python/issues/4694)) +* **deps:** bump docker/setup-qemu-action from 3.0.0 to 3.1.0 ([#4685](https://github.com/aws-powertools/powertools-lambda-python/issues/4685)) +* **deps:** bump actions/setup-python from 5.1.0 to 5.1.1 ([#4732](https://github.com/aws-powertools/powertools-lambda-python/issues/4732)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4733](https://github.com/aws-powertools/powertools-lambda-python/issues/4733)) +* **deps:** bump actions/upload-artifact from 4.3.3 to 4.3.4 ([#4698](https://github.com/aws-powertools/powertools-lambda-python/issues/4698)) +* **deps:** bump actions/download-artifact from 4.1.7 to 4.1.8 ([#4699](https://github.com/aws-powertools/powertools-lambda-python/issues/4699)) +* **deps:** bump actions/setup-node from 4.0.2 to 4.0.3 ([#4725](https://github.com/aws-powertools/powertools-lambda-python/issues/4725)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.9 to 3.0.10 ([#4678](https://github.com/aws-powertools/powertools-lambda-python/issues/4678)) +* **deps:** bump docker/setup-buildx-action from 3.3.0 to 3.4.0 ([#4693](https://github.com/aws-powertools/powertools-lambda-python/issues/4693)) +* **deps:** bump zipp from 3.17.0 to 3.19.1 in /docs ([#4720](https://github.com/aws-powertools/powertools-lambda-python/issues/4720)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4659](https://github.com/aws-powertools/powertools-lambda-python/issues/4659)) +* **deps:** bump certifi from 2024.6.2 to 2024.7.4 ([#4700](https://github.com/aws-powertools/powertools-lambda-python/issues/4700)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.27.23 to 1.27.24 in /layer/scripts/layer-balancer in the layer-balancer group ([#4684](https://github.com/aws-powertools/powertools-lambda-python/issues/4684)) +* **deps-dev:** bump mkdocs-material from 9.5.27 to 9.5.28 ([#4676](https://github.com/aws-powertools/powertools-lambda-python/issues/4676)) +* **deps-dev:** bump cfn-lint from 1.4.2 to 1.5.0 ([#4675](https://github.com/aws-powertools/powertools-lambda-python/issues/4675)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.147.3a0 to 2.148.0a0 ([#4722](https://github.com/aws-powertools/powertools-lambda-python/issues/4722)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.200 to 0.1.201 ([#4687](https://github.com/aws-powertools/powertools-lambda-python/issues/4687)) +* **deps-dev:** bump aws-cdk-lib from 2.147.2 to 2.147.3 ([#4674](https://github.com/aws-powertools/powertools-lambda-python/issues/4674)) +* **deps-dev:** bump zipp from 3.17.0 to 3.19.1 in /layer ([#4721](https://github.com/aws-powertools/powertools-lambda-python/issues/4721)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.202 to 0.1.205 ([#4723](https://github.com/aws-powertools/powertools-lambda-python/issues/4723)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.147.2a0 to 2.147.3a0 ([#4686](https://github.com/aws-powertools/powertools-lambda-python/issues/4686)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.199 to 0.1.200 ([#4677](https://github.com/aws-powertools/powertools-lambda-python/issues/4677)) +* **deps-dev:** bump aws-cdk-lib from 2.147.3 to 2.148.0 ([#4710](https://github.com/aws-powertools/powertools-lambda-python/issues/4710)) +* **deps-dev:** bump aws-cdk from 2.147.2 to 2.147.3 ([#4672](https://github.com/aws-powertools/powertools-lambda-python/issues/4672)) +* **deps-dev:** bump mypy-boto3-s3 from 1.34.120 to 1.34.138 in the boto-typing group ([#4673](https://github.com/aws-powertools/powertools-lambda-python/issues/4673)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.201 to 0.1.202 ([#4696](https://github.com/aws-powertools/powertools-lambda-python/issues/4696)) +* **deps-dev:** bump cfn-lint from 1.5.1 to 1.5.2 ([#4724](https://github.com/aws-powertools/powertools-lambda-python/issues/4724)) +* **deps-dev:** bump ruff from 0.5.0 to 0.5.1 ([#4697](https://github.com/aws-powertools/powertools-lambda-python/issues/4697)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.198 to 0.1.199 ([#4668](https://github.com/aws-powertools/powertools-lambda-python/issues/4668)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.147.1a0 to 2.147.2a0 ([#4667](https://github.com/aws-powertools/powertools-lambda-python/issues/4667)) +* **deps-dev:** bump aws-cdk from 2.147.3 to 2.148.0 ([#4708](https://github.com/aws-powertools/powertools-lambda-python/issues/4708)) +* **deps-dev:** bump cfn-lint from 1.5.2 to 1.5.3 ([#4734](https://github.com/aws-powertools/powertools-lambda-python/issues/4734)) +* **deps-dev:** bump sentry-sdk from 2.8.0 to 2.9.0 ([#4735](https://github.com/aws-powertools/powertools-lambda-python/issues/4735)) +* **deps-dev:** bump cfn-lint from 1.4.1 to 1.4.2 ([#4660](https://github.com/aws-powertools/powertools-lambda-python/issues/4660)) +* **deps-dev:** bump aws-cdk-lib from 2.147.1 to 2.147.2 ([#4661](https://github.com/aws-powertools/powertools-lambda-python/issues/4661)) +* **deps-dev:** bump cfn-lint from 1.5.0 to 1.5.1 ([#4711](https://github.com/aws-powertools/powertools-lambda-python/issues/4711)) +* **deps-dev:** bump aws-cdk from 2.147.1 to 2.147.2 ([#4657](https://github.com/aws-powertools/powertools-lambda-python/issues/4657)) +* **deps-dev:** bump ruff from 0.4.10 to 0.5.0 ([#4644](https://github.com/aws-powertools/powertools-lambda-python/issues/4644)) +* **deps-dev:** bump sentry-sdk from 2.7.1 to 2.8.0 ([#4712](https://github.com/aws-powertools/powertools-lambda-python/issues/4712)) +* **layers:** downgrade aws cdk to 2.145.0 ([#4739](https://github.com/aws-powertools/powertools-lambda-python/issues/4739)) + + +<a name="v2.40.1"></a> +## [v2.40.1] - 2024-06-28 +## Bug Fixes + +* **event_handler:** current_event regression AppSyncResolver Router ([#4652](https://github.com/aws-powertools/powertools-lambda-python/issues/4652)) + +## Maintenance + +* version bump +* **ci:** new pre-release 2.40.1a1 ([#4653](https://github.com/aws-powertools/powertools-lambda-python/issues/4653)) +* **ci:** new pre-release 2.40.1a0 ([#4648](https://github.com/aws-powertools/powertools-lambda-python/issues/4648)) +* **deps-dev:** bump cfn-lint from 1.3.7 to 1.4.1 ([#4646](https://github.com/aws-powertools/powertools-lambda-python/issues/4646)) +* **deps-dev:** bump sentry-sdk from 2.7.0 to 2.7.1 ([#4645](https://github.com/aws-powertools/powertools-lambda-python/issues/4645)) + + +<a name="v2.40.0"></a> +## [v2.40.0] - 2024-06-27 +## Bug Fixes + +* **event_sources:** change partition and offset field types in KafkaEventRecord ([#4515](https://github.com/aws-powertools/powertools-lambda-python/issues/4515)) + +## Documentation + +* **homepage:** Fix homepage link ([#4587](https://github.com/aws-powertools/powertools-lambda-python/issues/4587)) +* **i-made-this:** add new article about best practices for accelerating serverless development ([#4518](https://github.com/aws-powertools/powertools-lambda-python/issues/4518)) +* **public reference:** add Brsk as a public reference ([#4597](https://github.com/aws-powertools/powertools-lambda-python/issues/4597)) + +## Features + +* **event-handler:** add appsync batch resolvers ([#1998](https://github.com/aws-powertools/powertools-lambda-python/issues/1998)) +* **validation:** support JSON Schema referencing in validation utils ([#4508](https://github.com/aws-powertools/powertools-lambda-python/issues/4508)) + +## Maintenance + +* version bump +* **ci:** add the Metrics feature to nox tests ([#4552](https://github.com/aws-powertools/powertools-lambda-python/issues/4552)) +* **ci:** new pre-release 2.39.2a5 ([#4636](https://github.com/aws-powertools/powertools-lambda-python/issues/4636)) +* **ci:** add the Streaming feature to nox tests ([#4575](https://github.com/aws-powertools/powertools-lambda-python/issues/4575)) +* **ci:** new pre-release 2.39.2a4 ([#4629](https://github.com/aws-powertools/powertools-lambda-python/issues/4629)) +* **ci:** new pre-release 2.39.2a3 ([#4620](https://github.com/aws-powertools/powertools-lambda-python/issues/4620)) +* **ci:** add the Event Handler feature to nox tests ([#4581](https://github.com/aws-powertools/powertools-lambda-python/issues/4581)) +* **ci:** add the Data Class feature to nox tests ([#4583](https://github.com/aws-powertools/powertools-lambda-python/issues/4583)) +* **ci:** add the Parser feature to nox tests ([#4584](https://github.com/aws-powertools/powertools-lambda-python/issues/4584)) +* **ci:** add the Idempotency feature to nox tests ([#4585](https://github.com/aws-powertools/powertools-lambda-python/issues/4585)) +* **ci:** new pre-release 2.39.2a2 ([#4610](https://github.com/aws-powertools/powertools-lambda-python/issues/4610)) +* **ci:** introduce tests with Nox ([#4537](https://github.com/aws-powertools/powertools-lambda-python/issues/4537)) +* **ci:** new pre-release 2.39.2a1 ([#4598](https://github.com/aws-powertools/powertools-lambda-python/issues/4598)) +* **ci:** add the Tracer feature to nox tests ([#4567](https://github.com/aws-powertools/powertools-lambda-python/issues/4567)) +* **ci:** add the Middleware Factory feature to nox tests ([#4568](https://github.com/aws-powertools/powertools-lambda-python/issues/4568)) +* **ci:** add the Parameters feature to nox tests ([#4569](https://github.com/aws-powertools/powertools-lambda-python/issues/4569)) +* **ci:** add the Batch Processor feature to nox tests ([#4586](https://github.com/aws-powertools/powertools-lambda-python/issues/4586)) +* **ci:** add the Feature Flags feature to nox tests ([#4570](https://github.com/aws-powertools/powertools-lambda-python/issues/4570)) +* **ci:** add the Validation feature to nox tests ([#4571](https://github.com/aws-powertools/powertools-lambda-python/issues/4571)) +* **ci:** introduce daily pre-releases ([#4535](https://github.com/aws-powertools/powertools-lambda-python/issues/4535)) +* **ci:** new pre-release 2.39.2a0 ([#4590](https://github.com/aws-powertools/powertools-lambda-python/issues/4590)) +* **ci:** add the Data Masking feature to nox tests ([#4574](https://github.com/aws-powertools/powertools-lambda-python/issues/4574)) +* **ci:** add the Typing feature to nox tests ([#4572](https://github.com/aws-powertools/powertools-lambda-python/issues/4572)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.14 to 1.9.0 ([#4592](https://github.com/aws-powertools/powertools-lambda-python/issues/4592)) +* **deps:** bump pydantic from 1.10.16 to 1.10.17 ([#4595](https://github.com/aws-powertools/powertools-lambda-python/issues/4595)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4565](https://github.com/aws-powertools/powertools-lambda-python/issues/4565)) +* **deps:** bump squidfunk/mkdocs-material from `96abcbb` to `257eca8` in /docs ([#4540](https://github.com/aws-powertools/powertools-lambda-python/issues/4540)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.7 to 3.0.9 ([#4539](https://github.com/aws-powertools/powertools-lambda-python/issues/4539)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4546](https://github.com/aws-powertools/powertools-lambda-python/issues/4546)) +* **deps:** bump redis from 5.0.5 to 5.0.6 ([#4527](https://github.com/aws-powertools/powertools-lambda-python/issues/4527)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4580](https://github.com/aws-powertools/powertools-lambda-python/issues/4580)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#4635](https://github.com/aws-powertools/powertools-lambda-python/issues/4635)) +* **deps:** bump codecov/codecov-action from 4.4.1 to 4.5.0 ([#4514](https://github.com/aws-powertools/powertools-lambda-python/issues/4514)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.14 to 1.9.0 ([#4538](https://github.com/aws-powertools/powertools-lambda-python/issues/4538)) +* **deps:** bump fastjsonschema from 2.19.1 to 2.20.0 ([#4543](https://github.com/aws-powertools/powertools-lambda-python/issues/4543)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.189 to 0.1.192 ([#4578](https://github.com/aws-powertools/powertools-lambda-python/issues/4578)) +* **deps-dev:** bump sentry-sdk from 2.5.1 to 2.6.0 ([#4579](https://github.com/aws-powertools/powertools-lambda-python/issues/4579)) +* **deps-dev:** bump cfn-lint from 0.87.7 to 1.3.0 ([#4577](https://github.com/aws-powertools/powertools-lambda-python/issues/4577)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.192 to 0.1.193 ([#4596](https://github.com/aws-powertools/powertools-lambda-python/issues/4596)) +* **deps-dev:** bump ruff from 0.4.9 to 0.4.10 ([#4594](https://github.com/aws-powertools/powertools-lambda-python/issues/4594)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.193 to 0.1.194 ([#4601](https://github.com/aws-powertools/powertools-lambda-python/issues/4601)) +* **deps-dev:** bump aws-cdk from 2.146.0 to 2.147.0 ([#4604](https://github.com/aws-powertools/powertools-lambda-python/issues/4604)) +* **deps-dev:** bump aws-cdk-lib from 2.146.0 to 2.147.0 ([#4603](https://github.com/aws-powertools/powertools-lambda-python/issues/4603)) +* **deps-dev:** bump filelock from 3.15.1 to 3.15.3 ([#4576](https://github.com/aws-powertools/powertools-lambda-python/issues/4576)) +* **deps-dev:** bump hvac from 2.2.0 to 2.3.0 ([#4563](https://github.com/aws-powertools/powertools-lambda-python/issues/4563)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.188 to 0.1.189 ([#4564](https://github.com/aws-powertools/powertools-lambda-python/issues/4564)) +* **deps-dev:** bump cfn-lint from 1.3.0 to 1.3.3 ([#4602](https://github.com/aws-powertools/powertools-lambda-python/issues/4602)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.194 to 0.1.198 ([#4627](https://github.com/aws-powertools/powertools-lambda-python/issues/4627)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.146.0a0 to 2.147.0a0 ([#4619](https://github.com/aws-powertools/powertools-lambda-python/issues/4619)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.184 to 0.1.188 ([#4550](https://github.com/aws-powertools/powertools-lambda-python/issues/4550)) +* **deps-dev:** bump mkdocs-material from 9.5.26 to 9.5.27 ([#4544](https://github.com/aws-powertools/powertools-lambda-python/issues/4544)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.145.0a0 to 2.146.0a0 ([#4542](https://github.com/aws-powertools/powertools-lambda-python/issues/4542)) +* **deps-dev:** bump urllib3 from 1.26.18 to 1.26.19 in /layer ([#4547](https://github.com/aws-powertools/powertools-lambda-python/issues/4547)) +* **deps-dev:** bump aws-cdk-lib from 2.145.0 to 2.146.0 ([#4526](https://github.com/aws-powertools/powertools-lambda-python/issues/4526)) +* **deps-dev:** bump aws-cdk from 2.147.0 to 2.147.1 ([#4614](https://github.com/aws-powertools/powertools-lambda-python/issues/4614)) +* **deps-dev:** bump coverage from 7.5.3 to 7.5.4 ([#4617](https://github.com/aws-powertools/powertools-lambda-python/issues/4617)) +* **deps-dev:** bump aws-cdk-lib from 2.147.0 to 2.147.1 ([#4615](https://github.com/aws-powertools/powertools-lambda-python/issues/4615)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.34.125 to 1.34.128 in the boto-typing group ([#4541](https://github.com/aws-powertools/powertools-lambda-python/issues/4541)) +* **deps-dev:** bump pdoc3 from 0.10.0 to 0.11.0 ([#4618](https://github.com/aws-powertools/powertools-lambda-python/issues/4618)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.34.109 to 1.34.125 in the boto-typing group ([#4509](https://github.com/aws-powertools/powertools-lambda-python/issues/4509)) +* **deps-dev:** bump mike from 2.1.1 to 2.1.2 ([#4616](https://github.com/aws-powertools/powertools-lambda-python/issues/4616)) +* **deps-dev:** bump mypy from 1.10.0 to 1.10.1 ([#4624](https://github.com/aws-powertools/powertools-lambda-python/issues/4624)) +* **deps-dev:** bump filelock from 3.15.3 to 3.15.4 ([#4626](https://github.com/aws-powertools/powertools-lambda-python/issues/4626)) +* **deps-dev:** bump ruff from 0.4.8 to 0.4.9 ([#4528](https://github.com/aws-powertools/powertools-lambda-python/issues/4528)) +* **deps-dev:** bump cfn-lint from 1.3.3 to 1.3.5 ([#4628](https://github.com/aws-powertools/powertools-lambda-python/issues/4628)) +* **deps-dev:** bump mypy-boto3-ssm from 1.34.91 to 1.34.132 in the boto-typing group ([#4623](https://github.com/aws-powertools/powertools-lambda-python/issues/4623)) +* **deps-dev:** bump aws-cdk from 2.145.0 to 2.146.0 ([#4525](https://github.com/aws-powertools/powertools-lambda-python/issues/4525)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.182 to 0.1.184 ([#4529](https://github.com/aws-powertools/powertools-lambda-python/issues/4529)) +* **deps-dev:** bump bandit from 1.7.8 to 1.7.9 ([#4511](https://github.com/aws-powertools/powertools-lambda-python/issues/4511)) +* **deps-dev:** bump cfn-lint from 0.87.6 to 0.87.7 ([#4513](https://github.com/aws-powertools/powertools-lambda-python/issues/4513)) +* **deps-dev:** bump filelock from 3.14.0 to 3.15.1 ([#4512](https://github.com/aws-powertools/powertools-lambda-python/issues/4512)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.179 to 0.1.182 ([#4510](https://github.com/aws-powertools/powertools-lambda-python/issues/4510)) +* **deps-dev:** bump cfn-lint from 1.3.5 to 1.3.7 ([#4634](https://github.com/aws-powertools/powertools-lambda-python/issues/4634)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.34.114 to 1.34.131 in the boto-typing group ([#4593](https://github.com/aws-powertools/powertools-lambda-python/issues/4593)) +* **governance:** fix errors when creating Gitpod environment ([#4532](https://github.com/aws-powertools/powertools-lambda-python/issues/4532)) +* **layers:** downgrade aws cdk to 2.145.0 ([#4640](https://github.com/aws-powertools/powertools-lambda-python/issues/4640)) + + +<a name="v2.39.1"></a> +## [v2.39.1] - 2024-06-13 +## Bug Fixes + +* **event_handler:** regression making pydantic required (it should not) ([#4500](https://github.com/aws-powertools/powertools-lambda-python/issues/4500)) + +## Maintenance + +* version bump + + +<a name="v2.39.0"></a> +## [v2.39.0] - 2024-06-13 +## Bug Fixes + +* **event_handler:** do not skip middleware and exception handlers on 404 error ([#4492](https://github.com/aws-powertools/powertools-lambda-python/issues/4492)) +* **event_handler:** raise more specific SerializationError exception for unsupported types in data validation ([#4415](https://github.com/aws-powertools/powertools-lambda-python/issues/4415)) +* **event_handler:** security scheme unhashable list when working with router ([#4421](https://github.com/aws-powertools/powertools-lambda-python/issues/4421)) +* **event_handler:** CORS Origin for ALBResolver multi-headers ([#4385](https://github.com/aws-powertools/powertools-lambda-python/issues/4385)) +* **idempotency:** POWERTOOLS_IDEMPOTENCY_DISABLED should respect truthy values ([#4391](https://github.com/aws-powertools/powertools-lambda-python/issues/4391)) + +## Documentation + +* **homepage:** Change installation to CDK v2 ([#4351](https://github.com/aws-powertools/powertools-lambda-python/issues/4351)) +* **public reference:** add Recast as a public reference ([#4491](https://github.com/aws-powertools/powertools-lambda-python/issues/4491)) + +## Features + +* **event_source:** add CloudFormationCustomResourceEvent data class. ([#4342](https://github.com/aws-powertools/powertools-lambda-python/issues/4342)) +* **events:** Update and Add Cognito User Pool Events ([#4423](https://github.com/aws-powertools/powertools-lambda-python/issues/4423)) + +## Maintenance + +* version bump +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4369](https://github.com/aws-powertools/powertools-lambda-python/issues/4369)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4468](https://github.com/aws-powertools/powertools-lambda-python/issues/4468)) +* **deps:** bump datadog-lambda from 5.94.0 to 6.95.0 ([#4471](https://github.com/aws-powertools/powertools-lambda-python/issues/4471)) +* **deps:** bump redis from 5.0.4 to 5.0.5 ([#4464](https://github.com/aws-powertools/powertools-lambda-python/issues/4464)) +* **deps:** bump aws-encryption-sdk from 3.2.0 to 3.3.0 ([#4393](https://github.com/aws-powertools/powertools-lambda-python/issues/4393)) +* **deps:** bump codecov/codecov-action from 4.4.0 to 4.4.1 ([#4376](https://github.com/aws-powertools/powertools-lambda-python/issues/4376)) +* **deps:** bump squidfunk/mkdocs-material from `8a87f05` to `96abcbb` in /docs ([#4461](https://github.com/aws-powertools/powertools-lambda-python/issues/4461)) +* **deps:** bump typing-extensions from 4.12.1 to 4.12.2 ([#4470](https://github.com/aws-powertools/powertools-lambda-python/issues/4470)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#4396](https://github.com/aws-powertools/powertools-lambda-python/issues/4396)) +* **deps:** bump aws-xray-sdk from 2.13.0 to 2.13.1 ([#4379](https://github.com/aws-powertools/powertools-lambda-python/issues/4379)) +* **deps:** bump actions/dependency-review-action from 4.3.2 to 4.3.3 ([#4456](https://github.com/aws-powertools/powertools-lambda-python/issues/4456)) +* **deps:** bump aws-xray-sdk from 2.13.1 to 2.14.0 ([#4453](https://github.com/aws-powertools/powertools-lambda-python/issues/4453)) +* **deps:** bump typing-extensions from 4.11.0 to 4.12.0 ([#4404](https://github.com/aws-powertools/powertools-lambda-python/issues/4404)) +* **deps:** bump squidfunk/mkdocs-material from `5358893` to `8a87f05` in /docs ([#4408](https://github.com/aws-powertools/powertools-lambda-python/issues/4408)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.6 to 3.0.7 ([#4478](https://github.com/aws-powertools/powertools-lambda-python/issues/4478)) +* **deps:** bump squidfunk/mkdocs-material from `48d1914` to `5358893` in /docs ([#4377](https://github.com/aws-powertools/powertools-lambda-python/issues/4377)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4444](https://github.com/aws-powertools/powertools-lambda-python/issues/4444)) +* **deps:** bump pydantic from 1.10.15 to 1.10.16 ([#4485](https://github.com/aws-powertools/powertools-lambda-python/issues/4485)) +* **deps:** bump datadog-lambda from 6.95.0 to 6.96.0 ([#4489](https://github.com/aws-powertools/powertools-lambda-python/issues/4489)) +* **deps:** bump actions/checkout from 4.1.6 to 4.1.7 ([#4493](https://github.com/aws-powertools/powertools-lambda-python/issues/4493)) +* **deps:** bump typing-extensions from 4.12.0 to 4.12.1 ([#4440](https://github.com/aws-powertools/powertools-lambda-python/issues/4440)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.5 to 3.0.6 ([#4445](https://github.com/aws-powertools/powertools-lambda-python/issues/4445)) +* **deps:** bump requests from 2.31.0 to 2.32.0 ([#4383](https://github.com/aws-powertools/powertools-lambda-python/issues/4383)) +* **deps-dev:** bump aws-cdk from 2.143.1 to 2.144.0 ([#4443](https://github.com/aws-powertools/powertools-lambda-python/issues/4443)) +* **deps-dev:** bump aws-cdk-lib from 2.143.1 to 2.144.0 ([#4441](https://github.com/aws-powertools/powertools-lambda-python/issues/4441)) +* **deps-dev:** bump ruff from 0.4.6 to 0.4.7 ([#4435](https://github.com/aws-powertools/powertools-lambda-python/issues/4435)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.143.0a0 to 2.143.1a0 ([#4433](https://github.com/aws-powertools/powertools-lambda-python/issues/4433)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.164 to 0.1.169 ([#4442](https://github.com/aws-powertools/powertools-lambda-python/issues/4442)) +* **deps-dev:** bump pytest from 8.2.1 to 8.2.2 ([#4450](https://github.com/aws-powertools/powertools-lambda-python/issues/4450)) +* **deps-dev:** bump aws-cdk from 2.143.0 to 2.143.1 ([#4430](https://github.com/aws-powertools/powertools-lambda-python/issues/4430)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.163 to 0.1.164 ([#4428](https://github.com/aws-powertools/powertools-lambda-python/issues/4428)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.161 to 0.1.163 ([#4425](https://github.com/aws-powertools/powertools-lambda-python/issues/4425)) +* **deps-dev:** bump cfn-lint from 0.87.5 to 0.87.6 ([#4486](https://github.com/aws-powertools/powertools-lambda-python/issues/4486)) +* **deps-dev:** bump sentry-sdk from 2.3.1 to 2.4.0 ([#4449](https://github.com/aws-powertools/powertools-lambda-python/issues/4449)) +* **deps-dev:** bump ruff from 0.4.5 to 0.4.6 ([#4417](https://github.com/aws-powertools/powertools-lambda-python/issues/4417)) +* **deps-dev:** bump cfn-lint from 0.87.3 to 0.87.4 ([#4419](https://github.com/aws-powertools/powertools-lambda-python/issues/4419)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.159 to 0.1.161 ([#4420](https://github.com/aws-powertools/powertools-lambda-python/issues/4420)) +* **deps-dev:** bump coverage from 7.5.2 to 7.5.3 ([#4418](https://github.com/aws-powertools/powertools-lambda-python/issues/4418)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.34.113 to 1.34.114 in the boto-typing group ([#4416](https://github.com/aws-powertools/powertools-lambda-python/issues/4416)) +* **deps-dev:** bump mkdocs-material from 9.5.24 to 9.5.25 ([#4411](https://github.com/aws-powertools/powertools-lambda-python/issues/4411)) +* **deps-dev:** bump aws-cdk-lib from 2.143.0 to 2.143.1 ([#4429](https://github.com/aws-powertools/powertools-lambda-python/issues/4429)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.142.1a0 to 2.143.0a0 ([#4410](https://github.com/aws-powertools/powertools-lambda-python/issues/4410)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.34.97 to 1.34.113 in the boto-typing group ([#4409](https://github.com/aws-powertools/powertools-lambda-python/issues/4409)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.158 to 0.1.159 ([#4412](https://github.com/aws-powertools/powertools-lambda-python/issues/4412)) +* **deps-dev:** bump coverage from 7.5.1 to 7.5.2 ([#4413](https://github.com/aws-powertools/powertools-lambda-python/issues/4413)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.143.1a0 to 2.144.0a0 ([#4448](https://github.com/aws-powertools/powertools-lambda-python/issues/4448)) +* **deps-dev:** bump mypy-boto3-s3 from 1.34.105 to 1.34.120 in the boto-typing group ([#4452](https://github.com/aws-powertools/powertools-lambda-python/issues/4452)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.169 to 0.1.173 ([#4459](https://github.com/aws-powertools/powertools-lambda-python/issues/4459)) +* **deps-dev:** bump aws-cdk-lib from 2.142.1 to 2.143.0 ([#4403](https://github.com/aws-powertools/powertools-lambda-python/issues/4403)) +* **deps-dev:** bump aws-cdk from 2.142.1 to 2.143.0 ([#4402](https://github.com/aws-powertools/powertools-lambda-python/issues/4402)) +* **deps-dev:** bump ruff from 0.4.4 to 0.4.5 ([#4399](https://github.com/aws-powertools/powertools-lambda-python/issues/4399)) +* **deps-dev:** bump sentry-sdk from 2.2.1 to 2.3.1 ([#4398](https://github.com/aws-powertools/powertools-lambda-python/issues/4398)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.157 to 0.1.158 ([#4397](https://github.com/aws-powertools/powertools-lambda-python/issues/4397)) +* **deps-dev:** bump ruff from 0.4.7 to 0.4.8 ([#4455](https://github.com/aws-powertools/powertools-lambda-python/issues/4455)) +* **deps-dev:** bump sentry-sdk from 2.4.0 to 2.5.0 ([#4462](https://github.com/aws-powertools/powertools-lambda-python/issues/4462)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.155 to 0.1.157 ([#4394](https://github.com/aws-powertools/powertools-lambda-python/issues/4394)) +* **deps-dev:** bump mkdocs-material from 9.5.25 to 9.5.26 ([#4463](https://github.com/aws-powertools/powertools-lambda-python/issues/4463)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.34.84 to 1.34.111 in the boto-typing group ([#4392](https://github.com/aws-powertools/powertools-lambda-python/issues/4392)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.154 to 0.1.155 ([#4386](https://github.com/aws-powertools/powertools-lambda-python/issues/4386)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.173 to 0.1.174 ([#4466](https://github.com/aws-powertools/powertools-lambda-python/issues/4466)) +* **deps-dev:** bump pytest-asyncio from 0.23.6 to 0.23.7 ([#4387](https://github.com/aws-powertools/powertools-lambda-python/issues/4387)) +* **deps-dev:** bump sentry-sdk from 2.2.0 to 2.2.1 ([#4388](https://github.com/aws-powertools/powertools-lambda-python/issues/4388)) +* **deps-dev:** bump ijson from 3.2.3 to 3.3.0 ([#4465](https://github.com/aws-powertools/powertools-lambda-python/issues/4465)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.152 to 0.1.154 ([#4382](https://github.com/aws-powertools/powertools-lambda-python/issues/4382)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.174 to 0.1.175 ([#4472](https://github.com/aws-powertools/powertools-lambda-python/issues/4472)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.34.107 to 1.34.109 in the boto-typing group ([#4378](https://github.com/aws-powertools/powertools-lambda-python/issues/4378)) +* **deps-dev:** bump sentry-sdk from 2.5.0 to 2.5.1 ([#4469](https://github.com/aws-powertools/powertools-lambda-python/issues/4469)) +* **deps-dev:** bump cfn-lint from 0.87.4 to 0.87.5 ([#4479](https://github.com/aws-powertools/powertools-lambda-python/issues/4479)) +* **deps-dev:** bump mkdocs-material from 9.5.23 to 9.5.24 ([#4380](https://github.com/aws-powertools/powertools-lambda-python/issues/4380)) +* **deps-dev:** bump pytest from 8.2.0 to 8.2.1 ([#4381](https://github.com/aws-powertools/powertools-lambda-python/issues/4381)) +* **deps-dev:** bump aws-cdk from 2.144.0 to 2.145.0 ([#4482](https://github.com/aws-powertools/powertools-lambda-python/issues/4482)) +* **deps-dev:** bump aws-cdk-lib from 2.144.0 to 2.145.0 ([#4481](https://github.com/aws-powertools/powertools-lambda-python/issues/4481)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.141.0a0 to 2.142.1a0 ([#4367](https://github.com/aws-powertools/powertools-lambda-python/issues/4367)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.144.0a0 to 2.145.0a0 ([#4487](https://github.com/aws-powertools/powertools-lambda-python/issues/4487)) +* **deps-dev:** bump aws-cdk from 2.142.0 to 2.142.1 ([#4366](https://github.com/aws-powertools/powertools-lambda-python/issues/4366)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.150 to 0.1.152 ([#4368](https://github.com/aws-powertools/powertools-lambda-python/issues/4368)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.176 to 0.1.179 ([#4488](https://github.com/aws-powertools/powertools-lambda-python/issues/4488)) +* **deps-dev:** bump cfn-lint from 0.87.2 to 0.87.3 ([#4370](https://github.com/aws-powertools/powertools-lambda-python/issues/4370)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.175 to 0.1.176 ([#4480](https://github.com/aws-powertools/powertools-lambda-python/issues/4480)) +* **libraries:** add jmespath as a required dependency ([#4422](https://github.com/aws-powertools/powertools-lambda-python/issues/4422)) + + +<a name="v2.38.1"></a> +## [v2.38.1] - 2024-05-17 +## Bug Fixes + +* **logger:** reverting logger child modification ([#4363](https://github.com/aws-powertools/powertools-lambda-python/issues/4363)) + +## Maintenance + +* version bump + + +<a name="v2.38.0"></a> +## [v2.38.0] - 2024-05-17 +## Bug Fixes + +* **ci:** apply lessons learned to monthly roadmap reminder cross-repo ([#4078](https://github.com/aws-powertools/powertools-lambda-python/issues/4078)) +* **event-sources:** sane defaults for authorizer v1 and v2 ([#4298](https://github.com/aws-powertools/powertools-lambda-python/issues/4298)) +* **logger:** correctly pick powertools or custom handler in custom environments ([#4295](https://github.com/aws-powertools/powertools-lambda-python/issues/4295)) +* **parser:** make etag optional field on S3 notification events ([#4173](https://github.com/aws-powertools/powertools-lambda-python/issues/4173)) +* **typing:** resolved_headers_field is not Optional ([#4148](https://github.com/aws-powertools/powertools-lambda-python/issues/4148)) + +## Code Refactoring + +* **data-masking:** remove Non-GA comments ([#4334](https://github.com/aws-powertools/powertools-lambda-python/issues/4334)) +* **parser:** only infer type hints when necessary ([#4183](https://github.com/aws-powertools/powertools-lambda-python/issues/4183)) + +## Documentation + +* **general:** update documentation to add info about v3 ([#4234](https://github.com/aws-powertools/powertools-lambda-python/issues/4234)) +* **homepage:** add link to new and official workshop ([#4292](https://github.com/aws-powertools/powertools-lambda-python/issues/4292)) +* **idempotency:** fix highlight and import path ([#4154](https://github.com/aws-powertools/powertools-lambda-python/issues/4154)) +* **roadmap:** april updates ([#4181](https://github.com/aws-powertools/powertools-lambda-python/issues/4181)) + +## Features + +* **event_handler:** add support for persisting authorization session in OpenAPI ([#4312](https://github.com/aws-powertools/powertools-lambda-python/issues/4312)) +* **event_handler:** add decorator for HTTP HEAD verb ([#4275](https://github.com/aws-powertools/powertools-lambda-python/issues/4275)) +* **logger-utils:** preserve log level for discovered third-party top-level loggers ([#4299](https://github.com/aws-powertools/powertools-lambda-python/issues/4299)) + +## Maintenance + +* version bump +* **ci:** bump upload artifact action to v4 ([#4355](https://github.com/aws-powertools/powertools-lambda-python/issues/4355)) +* **ci:** add branch v3 to quality check and e2e actions ([#4232](https://github.com/aws-powertools/powertools-lambda-python/issues/4232)) +* **ci:** bump download artifact action to v4 ([#4358](https://github.com/aws-powertools/powertools-lambda-python/issues/4358)) +* **deps:** bump actions/download-artifact from 4.1.4 to 4.1.5 ([#4161](https://github.com/aws-powertools/powertools-lambda-python/issues/4161)) +* **deps:** bump actions/checkout from 4.1.3 to 4.1.4 ([#4206](https://github.com/aws-powertools/powertools-lambda-python/issues/4206)) +* **deps:** bump ossf/scorecard-action from 2.3.1 to 2.3.3 ([#4315](https://github.com/aws-powertools/powertools-lambda-python/issues/4315)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.27.12 to 1.27.13 in /layer/scripts/layer-balancer in the layer-balancer group ([#4319](https://github.com/aws-powertools/powertools-lambda-python/issues/4319)) +* **deps:** bump actions/download-artifact from 4.1.6 to 4.1.7 ([#4205](https://github.com/aws-powertools/powertools-lambda-python/issues/4205)) +* **deps:** bump squidfunk/mkdocs-material from `521644b` to `e309089` in /docs ([#4216](https://github.com/aws-powertools/powertools-lambda-python/issues/4216)) +* **deps:** bump squidfunk/mkdocs-material from `11d7ec0` to `8ef47d7` in /docs ([#4323](https://github.com/aws-powertools/powertools-lambda-python/issues/4323)) +* **deps:** bump datadog-lambda from 5.92.0 to 5.93.0 ([#4211](https://github.com/aws-powertools/powertools-lambda-python/issues/4211)) +* **deps:** bump redis from 5.0.3 to 5.0.4 ([#4187](https://github.com/aws-powertools/powertools-lambda-python/issues/4187)) +* **deps:** bump actions/upload-artifact from 4.3.2 to 4.3.3 ([#4177](https://github.com/aws-powertools/powertools-lambda-python/issues/4177)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#4302](https://github.com/aws-powertools/powertools-lambda-python/issues/4302)) +* **deps:** bump squidfunk/mkdocs-material from `8ef47d7` to `48d1914` in /docs ([#4336](https://github.com/aws-powertools/powertools-lambda-python/issues/4336)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4337](https://github.com/aws-powertools/powertools-lambda-python/issues/4337)) +* **deps:** bump squidfunk/mkdocs-material from `e309089` to `98c9809` in /docs ([#4236](https://github.com/aws-powertools/powertools-lambda-python/issues/4236)) +* **deps:** bump actions/dependency-review-action from 4.3.1 to 4.3.2 ([#4244](https://github.com/aws-powertools/powertools-lambda-python/issues/4244)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.4 to 3.0.5 ([#4281](https://github.com/aws-powertools/powertools-lambda-python/issues/4281)) +* **deps:** bump actions/checkout from 4.1.4 to 4.1.5 ([#4282](https://github.com/aws-powertools/powertools-lambda-python/issues/4282)) +* **deps:** bump jinja2 from 3.1.3 to 3.1.4 in /docs ([#4284](https://github.com/aws-powertools/powertools-lambda-python/issues/4284)) +* **deps:** bump codecov/codecov-action from 4.3.0 to 4.3.1 ([#4252](https://github.com/aws-powertools/powertools-lambda-python/issues/4252)) +* **deps:** bump datadog-lambda from 5.93.0 to 5.94.0 ([#4253](https://github.com/aws-powertools/powertools-lambda-python/issues/4253)) +* **deps:** bump actions/checkout from 4.1.2 to 4.1.3 ([#4168](https://github.com/aws-powertools/powertools-lambda-python/issues/4168)) +* **deps:** bump actions/dependency-review-action from 4.2.5 to 4.3.1 ([#4240](https://github.com/aws-powertools/powertools-lambda-python/issues/4240)) +* **deps:** bump actions/checkout from 4.1.5 to 4.1.6 ([#4344](https://github.com/aws-powertools/powertools-lambda-python/issues/4344)) +* **deps:** bump squidfunk/mkdocs-material from `98c9809` to `11d7ec0` in /docs ([#4269](https://github.com/aws-powertools/powertools-lambda-python/issues/4269)) +* **deps:** bump actions/upload-artifact from 4.3.1 to 4.3.2 ([#4162](https://github.com/aws-powertools/powertools-lambda-python/issues/4162)) +* **deps:** bump codecov/codecov-action from 4.3.1 to 4.4.0 ([#4328](https://github.com/aws-powertools/powertools-lambda-python/issues/4328)) +* **deps:** bump slsa-framework/slsa-github-generator from 1.10.0 to 2.0.0 ([#4179](https://github.com/aws-powertools/powertools-lambda-python/issues/4179)) +* **deps:** bump actions/download-artifact from 4.1.5 to 4.1.6 ([#4178](https://github.com/aws-powertools/powertools-lambda-python/issues/4178)) +* **deps-dev:** bump mkdocs-material from 9.5.20 to 9.5.21 ([#4271](https://github.com/aws-powertools/powertools-lambda-python/issues/4271)) +* **deps-dev:** bump mike from 2.1.0 to 2.1.1 ([#4268](https://github.com/aws-powertools/powertools-lambda-python/issues/4268)) +* **deps-dev:** bump cfn-lint from 0.87.0 to 0.87.1 ([#4272](https://github.com/aws-powertools/powertools-lambda-python/issues/4272)) +* **deps-dev:** bump mike from 1.1.2 to 2.1.0 ([#4258](https://github.com/aws-powertools/powertools-lambda-python/issues/4258)) +* **deps-dev:** bump aws-cdk-lib from 2.139.1 to 2.140.0 ([#4259](https://github.com/aws-powertools/powertools-lambda-python/issues/4259)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.139.1a0 to 2.140.0a0 ([#4270](https://github.com/aws-powertools/powertools-lambda-python/issues/4270)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.139.0a0 to 2.139.1a0 ([#4261](https://github.com/aws-powertools/powertools-lambda-python/issues/4261)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.133 to 0.1.134 ([#4260](https://github.com/aws-powertools/powertools-lambda-python/issues/4260)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.134 to 0.1.135 ([#4273](https://github.com/aws-powertools/powertools-lambda-python/issues/4273)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.34.91 to 1.34.97 in the boto-typing group ([#4257](https://github.com/aws-powertools/powertools-lambda-python/issues/4257)) +* **deps-dev:** bump sentry-sdk from 2.0.1 to 2.1.1 ([#4287](https://github.com/aws-powertools/powertools-lambda-python/issues/4287)) +* **deps-dev:** bump aws-cdk-lib from 2.139.0 to 2.139.1 ([#4248](https://github.com/aws-powertools/powertools-lambda-python/issues/4248)) +* **deps-dev:** bump cfn-lint from 0.86.4 to 0.87.0 ([#4249](https://github.com/aws-powertools/powertools-lambda-python/issues/4249)) +* **deps-dev:** bump pytest-xdist from 3.5.0 to 3.6.1 ([#4247](https://github.com/aws-powertools/powertools-lambda-python/issues/4247)) +* **deps-dev:** bump ruff from 0.4.2 to 0.4.3 ([#4286](https://github.com/aws-powertools/powertools-lambda-python/issues/4286)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.132 to 0.1.133 ([#4246](https://github.com/aws-powertools/powertools-lambda-python/issues/4246)) +* **deps-dev:** bump jinja2 from 3.1.3 to 3.1.4 ([#4283](https://github.com/aws-powertools/powertools-lambda-python/issues/4283)) +* **deps-dev:** bump aws-cdk from 2.139.0 to 2.139.1 ([#4245](https://github.com/aws-powertools/powertools-lambda-python/issues/4245)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.135 to 0.1.136 ([#4285](https://github.com/aws-powertools/powertools-lambda-python/issues/4285)) +* **deps-dev:** bump filelock from 3.13.4 to 3.14.0 ([#4241](https://github.com/aws-powertools/powertools-lambda-python/issues/4241)) +* **deps-dev:** bump hvac from 2.1.0 to 2.2.0 ([#4238](https://github.com/aws-powertools/powertools-lambda-python/issues/4238)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.131 to 0.1.132 ([#4239](https://github.com/aws-powertools/powertools-lambda-python/issues/4239)) +* **deps-dev:** bump mkdocs-material from 9.5.19 to 9.5.20 ([#4242](https://github.com/aws-powertools/powertools-lambda-python/issues/4242)) +* **deps-dev:** bump aws-cdk from 2.139.1 to 2.140.0 ([#4256](https://github.com/aws-powertools/powertools-lambda-python/issues/4256)) +* **deps-dev:** bump pytest from 8.1.1 to 8.2.0 ([#4237](https://github.com/aws-powertools/powertools-lambda-python/issues/4237)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.136 to 0.1.139 ([#4293](https://github.com/aws-powertools/powertools-lambda-python/issues/4293)) +* **deps-dev:** bump aws-cdk-lib from 2.141.0 to 2.142.1 ([#4352](https://github.com/aws-powertools/powertools-lambda-python/issues/4352)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.139 to 0.1.140 ([#4301](https://github.com/aws-powertools/powertools-lambda-python/issues/4301)) +* **deps-dev:** bump sentry-sdk from 1.45.0 to 2.0.1 ([#4223](https://github.com/aws-powertools/powertools-lambda-python/issues/4223)) +* **deps-dev:** bump mkdocs-material from 9.5.18 to 9.5.19 ([#4224](https://github.com/aws-powertools/powertools-lambda-python/issues/4224)) +* **deps-dev:** bump black from 24.4.1 to 24.4.2 ([#4222](https://github.com/aws-powertools/powertools-lambda-python/issues/4222)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.138.0a0 to 2.139.0a0 ([#4225](https://github.com/aws-powertools/powertools-lambda-python/issues/4225)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.130 to 0.1.131 ([#4221](https://github.com/aws-powertools/powertools-lambda-python/issues/4221)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.140 to 0.1.142 ([#4307](https://github.com/aws-powertools/powertools-lambda-python/issues/4307)) +* **deps-dev:** bump ruff from 0.4.1 to 0.4.2 ([#4212](https://github.com/aws-powertools/powertools-lambda-python/issues/4212)) +* **deps-dev:** bump aws-cdk-lib from 2.138.0 to 2.139.0 ([#4213](https://github.com/aws-powertools/powertools-lambda-python/issues/4213)) +* **deps-dev:** bump aws-cdk from 2.138.0 to 2.139.0 ([#4215](https://github.com/aws-powertools/powertools-lambda-python/issues/4215)) +* **deps-dev:** bump aws-cdk from 2.140.0 to 2.141.0 ([#4306](https://github.com/aws-powertools/powertools-lambda-python/issues/4306)) +* **deps-dev:** bump types-redis from 4.6.0.20240423 to 4.6.0.20240425 ([#4214](https://github.com/aws-powertools/powertools-lambda-python/issues/4214)) +* **deps-dev:** bump aws-cdk-lib from 2.140.0 to 2.141.0 ([#4308](https://github.com/aws-powertools/powertools-lambda-python/issues/4308)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#4210](https://github.com/aws-powertools/powertools-lambda-python/issues/4210)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.126 to 0.1.130 ([#4209](https://github.com/aws-powertools/powertools-lambda-python/issues/4209)) +* **deps-dev:** bump ruff from 0.4.3 to 0.4.4 ([#4309](https://github.com/aws-powertools/powertools-lambda-python/issues/4309)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.140.0a0 to 2.141.0a0 ([#4318](https://github.com/aws-powertools/powertools-lambda-python/issues/4318)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.142 to 0.1.144 ([#4316](https://github.com/aws-powertools/powertools-lambda-python/issues/4316)) +* **deps-dev:** bump black from 24.4.0 to 24.4.1 ([#4203](https://github.com/aws-powertools/powertools-lambda-python/issues/4203)) +* **deps-dev:** bump mypy from 1.9.0 to 1.10.0 ([#4202](https://github.com/aws-powertools/powertools-lambda-python/issues/4202)) +* **deps-dev:** bump mypy-boto3-ssm from 1.34.61 to 1.34.91 in the boto-typing group ([#4201](https://github.com/aws-powertools/powertools-lambda-python/issues/4201)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.123 to 0.1.126 ([#4188](https://github.com/aws-powertools/powertools-lambda-python/issues/4188)) +* **deps-dev:** bump cfn-lint from 0.87.1 to 0.87.2 ([#4317](https://github.com/aws-powertools/powertools-lambda-python/issues/4317)) +* **deps-dev:** bump coverage from 7.4.4 to 7.5.0 ([#4186](https://github.com/aws-powertools/powertools-lambda-python/issues/4186)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.144 to 0.1.145 ([#4325](https://github.com/aws-powertools/powertools-lambda-python/issues/4325)) +* **deps-dev:** bump types-redis from 4.6.0.20240417 to 4.6.0.20240423 ([#4185](https://github.com/aws-powertools/powertools-lambda-python/issues/4185)) +* **deps-dev:** bump mkdocs-material from 9.5.21 to 9.5.22 ([#4324](https://github.com/aws-powertools/powertools-lambda-python/issues/4324)) +* **deps-dev:** bump mypy-boto3-s3 from 1.34.91 to 1.34.105 in the boto-typing group ([#4329](https://github.com/aws-powertools/powertools-lambda-python/issues/4329)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.145 to 0.1.146 ([#4330](https://github.com/aws-powertools/powertools-lambda-python/issues/4330)) +* **deps-dev:** bump cfn-lint from 0.86.3 to 0.86.4 ([#4180](https://github.com/aws-powertools/powertools-lambda-python/issues/4180)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.121 to 0.1.123 ([#4176](https://github.com/aws-powertools/powertools-lambda-python/issues/4176)) +* **deps-dev:** bump mkdocs-material from 9.5.22 to 9.5.23 ([#4338](https://github.com/aws-powertools/powertools-lambda-python/issues/4338)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.137.0a0 to 2.138.0a0 ([#4169](https://github.com/aws-powertools/powertools-lambda-python/issues/4169)) +* **deps-dev:** bump aws-cdk from 2.141.0 to 2.142.0 ([#4343](https://github.com/aws-powertools/powertools-lambda-python/issues/4343)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.119 to 0.1.121 ([#4167](https://github.com/aws-powertools/powertools-lambda-python/issues/4167)) +* **deps-dev:** bump ruff from 0.3.7 to 0.4.1 ([#4166](https://github.com/aws-powertools/powertools-lambda-python/issues/4166)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.34.72 to 1.34.107 in the boto-typing group ([#4345](https://github.com/aws-powertools/powertools-lambda-python/issues/4345)) +* **deps-dev:** bump aws-cdk from 2.137.0 to 2.138.0 ([#4157](https://github.com/aws-powertools/powertools-lambda-python/issues/4157)) +* **deps-dev:** bump aws-cdk-lib from 2.137.0 to 2.138.0 ([#4160](https://github.com/aws-powertools/powertools-lambda-python/issues/4160)) +* **deps-dev:** bump sentry-sdk from 2.1.1 to 2.2.0 ([#4348](https://github.com/aws-powertools/powertools-lambda-python/issues/4348)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.146 to 0.1.150 ([#4346](https://github.com/aws-powertools/powertools-lambda-python/issues/4346)) +* **deps-dev:** bump coverage from 7.5.0 to 7.5.1 ([#4288](https://github.com/aws-powertools/powertools-lambda-python/issues/4288)) +* **governance:** add FastAPI third party license attribution ([#4297](https://github.com/aws-powertools/powertools-lambda-python/issues/4297)) + + +<a name="v2.37.0"></a> +## [v2.37.0] - 2024-04-18 +## Bug Fixes + +* **docs:** clarified usage of validation with fine grained responses ([#4101](https://github.com/aws-powertools/powertools-lambda-python/issues/4101)) +* **event_source:** fix typo in physicalname attribute for AmazonMQ events ([#4053](https://github.com/aws-powertools/powertools-lambda-python/issues/4053)) +* **typing:** make the case_sensitive field a boolean only ([#4128](https://github.com/aws-powertools/powertools-lambda-python/issues/4128)) +* **typing:** improve overloads to ensure the return type follows the default_value type ([#4114](https://github.com/aws-powertools/powertools-lambda-python/issues/4114)) + +## Documentation + +* **we-made-this:** new article on how to stream data with AWS Lambda & Powertools for AWS Lambda ([#4068](https://github.com/aws-powertools/powertools-lambda-python/issues/4068)) + +## Features + +* **Idempotency:** add feature for manipulating idempotent responses ([#4037](https://github.com/aws-powertools/powertools-lambda-python/issues/4037)) +* **event_handler:** add support for OpenAPI security schemes ([#4103](https://github.com/aws-powertools/powertools-lambda-python/issues/4103)) +* **logger:** add method to return currently configured keys ([#4033](https://github.com/aws-powertools/powertools-lambda-python/issues/4033)) + +## Maintenance + +* version bump +* **ci:** add monthly roadmap reminder workflow ([#4075](https://github.com/aws-powertools/powertools-lambda-python/issues/4075)) +* **ci:** prevent deprecated custom runner from being used ([#4061](https://github.com/aws-powertools/powertools-lambda-python/issues/4061)) +* **deps:** bump squidfunk/mkdocs-material from `065f3af` to `6b124e1` in /docs ([#4055](https://github.com/aws-powertools/powertools-lambda-python/issues/4055)) +* **deps:** bump squidfunk/mkdocs-material from `3307665` to `065f3af` in /docs ([#4052](https://github.com/aws-powertools/powertools-lambda-python/issues/4052)) +* **deps:** bump idna from 3.6 to 3.7 ([#4121](https://github.com/aws-powertools/powertools-lambda-python/issues/4121)) +* **deps:** bump sqlparse from 0.4.4 to 0.5.0 ([#4138](https://github.com/aws-powertools/powertools-lambda-python/issues/4138)) +* **deps:** bump squidfunk/mkdocs-material from `6b124e1` to `521644b` in /docs ([#4141](https://github.com/aws-powertools/powertools-lambda-python/issues/4141)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#4066](https://github.com/aws-powertools/powertools-lambda-python/issues/4066)) +* **deps:** bump pydantic from 1.10.14 to 1.10.15 ([#4064](https://github.com/aws-powertools/powertools-lambda-python/issues/4064)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#4042](https://github.com/aws-powertools/powertools-lambda-python/issues/4042)) +* **deps:** bump golang.org/x/sync from 0.6.0 to 0.7.0 in /layer/scripts/layer-balancer in the layer-balancer group ([#4071](https://github.com/aws-powertools/powertools-lambda-python/issues/4071)) +* **deps:** bump codecov/codecov-action from 4.1.1 to 4.2.0 ([#4072](https://github.com/aws-powertools/powertools-lambda-python/issues/4072)) +* **deps:** bump datadog-lambda from 5.91.0 to 5.92.0 ([#4038](https://github.com/aws-powertools/powertools-lambda-python/issues/4038)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.27.10 to 1.27.11 in /layer/scripts/layer-balancer in the layer-balancer group ([#4079](https://github.com/aws-powertools/powertools-lambda-python/issues/4079)) +* **deps:** bump typing-extensions from 4.10.0 to 4.11.0 ([#4080](https://github.com/aws-powertools/powertools-lambda-python/issues/4080)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.3 to 3.0.4 ([#4099](https://github.com/aws-powertools/powertools-lambda-python/issues/4099)) +* **deps:** bump codecov/codecov-action from 4.2.0 to 4.3.0 ([#4098](https://github.com/aws-powertools/powertools-lambda-python/issues/4098)) +* **deps:** bump docker/setup-buildx-action from 3.2.0 to 3.3.0 ([#4091](https://github.com/aws-powertools/powertools-lambda-python/issues/4091)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.112 to 0.1.113 ([#4136](https://github.com/aws-powertools/powertools-lambda-python/issues/4136)) +* **deps-dev:** bump aws-cdk from 2.135.0 to 2.136.0 ([#4090](https://github.com/aws-powertools/powertools-lambda-python/issues/4090)) +* **deps-dev:** bump types-redis from 4.6.0.20240311 to 4.6.0.20240409 ([#4094](https://github.com/aws-powertools/powertools-lambda-python/issues/4094)) +* **deps-dev:** bump aws-cdk-lib from 2.135.0 to 2.136.0 ([#4092](https://github.com/aws-powertools/powertools-lambda-python/issues/4092)) +* **deps-dev:** bump cfn-lint from 0.86.1 to 0.86.2 ([#4081](https://github.com/aws-powertools/powertools-lambda-python/issues/4081)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.135.0a0 to 2.136.0a0 ([#4095](https://github.com/aws-powertools/powertools-lambda-python/issues/4095)) +* **deps-dev:** bump filelock from 3.13.3 to 3.13.4 ([#4096](https://github.com/aws-powertools/powertools-lambda-python/issues/4096)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.106 to 0.1.107 ([#4082](https://github.com/aws-powertools/powertools-lambda-python/issues/4082)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.107 to 0.1.110 ([#4097](https://github.com/aws-powertools/powertools-lambda-python/issues/4097)) +* **deps-dev:** bump aws-cdk from 2.136.0 to 2.136.1 ([#4106](https://github.com/aws-powertools/powertools-lambda-python/issues/4106)) +* **deps-dev:** bump aws-cdk-lib from 2.136.0 to 2.136.1 ([#4107](https://github.com/aws-powertools/powertools-lambda-python/issues/4107)) +* **deps-dev:** bump sentry-sdk from 1.44.1 to 1.45.0 ([#4108](https://github.com/aws-powertools/powertools-lambda-python/issues/4108)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.110 to 0.1.112 ([#4109](https://github.com/aws-powertools/powertools-lambda-python/issues/4109)) +* **deps-dev:** bump sentry-sdk from 1.44.0 to 1.44.1 ([#4065](https://github.com/aws-powertools/powertools-lambda-python/issues/4065)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.134.0a0 to 2.135.0a0 ([#4063](https://github.com/aws-powertools/powertools-lambda-python/issues/4063)) +* **deps-dev:** bump aws-cdk from 2.136.1 to 2.137.0 ([#4115](https://github.com/aws-powertools/powertools-lambda-python/issues/4115)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#4062](https://github.com/aws-powertools/powertools-lambda-python/issues/4062)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.34.75 to 1.34.83 in the boto-typing group ([#4116](https://github.com/aws-powertools/powertools-lambda-python/issues/4116)) +* **deps-dev:** bump ruff from 0.3.5 to 0.3.7 ([#4123](https://github.com/aws-powertools/powertools-lambda-python/issues/4123)) +* **deps-dev:** bump aws-cdk-lib from 2.136.1 to 2.137.0 ([#4119](https://github.com/aws-powertools/powertools-lambda-python/issues/4119)) +* **deps-dev:** bump aws-cdk from 2.134.0 to 2.135.0 ([#4058](https://github.com/aws-powertools/powertools-lambda-python/issues/4058)) +* **deps-dev:** bump aws-cdk-lib from 2.134.0 to 2.135.0 ([#4057](https://github.com/aws-powertools/powertools-lambda-python/issues/4057)) +* **deps-dev:** bump mkdocs-material from 9.5.16 to 9.5.17 ([#4056](https://github.com/aws-powertools/powertools-lambda-python/issues/4056)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.136.0a0 to 2.137.0a0 ([#4124](https://github.com/aws-powertools/powertools-lambda-python/issues/4124)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.34.77 to 1.34.84 in the boto-typing group ([#4126](https://github.com/aws-powertools/powertools-lambda-python/issues/4126)) +* **deps-dev:** bump ruff from 0.3.4 to 0.3.5 ([#4049](https://github.com/aws-powertools/powertools-lambda-python/issues/4049)) +* **deps-dev:** bump mkdocs-material from 9.5.15 to 9.5.16 ([#4050](https://github.com/aws-powertools/powertools-lambda-python/issues/4050)) +* **deps-dev:** bump the boto-typing group with 1 update ([#4047](https://github.com/aws-powertools/powertools-lambda-python/issues/4047)) +* **deps-dev:** bump black from 24.3.0 to 24.4.0 ([#4135](https://github.com/aws-powertools/powertools-lambda-python/issues/4135)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.105 to 0.1.106 ([#4048](https://github.com/aws-powertools/powertools-lambda-python/issues/4048)) +* **deps-dev:** bump cfn-lint from 0.86.2 to 0.86.3 ([#4137](https://github.com/aws-powertools/powertools-lambda-python/issues/4137)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.113 to 0.1.115 ([#4142](https://github.com/aws-powertools/powertools-lambda-python/issues/4142)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.133.0a0 to 2.134.0a0 ([#4039](https://github.com/aws-powertools/powertools-lambda-python/issues/4039)) +* **deps-dev:** bump mkdocs-material from 9.5.17 to 9.5.18 ([#4143](https://github.com/aws-powertools/powertools-lambda-python/issues/4143)) +* **deps-dev:** bump types-redis from 4.6.0.20240409 to 4.6.0.20240417 ([#4145](https://github.com/aws-powertools/powertools-lambda-python/issues/4145)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.115 to 0.1.119 ([#4150](https://github.com/aws-powertools/powertools-lambda-python/issues/4150)) +* **deps-dev:** bump aws-cdk-lib from 2.133.0 to 2.134.0 ([#4031](https://github.com/aws-powertools/powertools-lambda-python/issues/4031)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.104 to 0.1.105 ([#4030](https://github.com/aws-powertools/powertools-lambda-python/issues/4030)) +* **deps-dev:** bump aws-cdk from 2.133.0 to 2.134.0 ([#4032](https://github.com/aws-powertools/powertools-lambda-python/issues/4032)) +* **deps-dev:** bump the boto-typing group with 1 update ([#4029](https://github.com/aws-powertools/powertools-lambda-python/issues/4029)) +* **deps-dev:** bump sentry-sdk from 1.43.0 to 1.44.0 ([#4040](https://github.com/aws-powertools/powertools-lambda-python/issues/4040)) +* **docs:** update highlighted lines on the Typing examples ([#4131](https://github.com/aws-powertools/powertools-lambda-python/issues/4131)) + + +<a name="v2.36.0"></a> +## [v2.36.0] - 2024-03-27 +## Bug Fixes + +* **event_handler:** always add 422 response to the schema ([#3995](https://github.com/aws-powertools/powertools-lambda-python/issues/3995)) +* **event_handler:** make decoded_body field optional in ApiGateway resolver ([#3937](https://github.com/aws-powertools/powertools-lambda-python/issues/3937)) +* **tracer:** add name sanitization for X-Ray subsegments ([#4005](https://github.com/aws-powertools/powertools-lambda-python/issues/4005)) + +## Code Refactoring + +* **logger:** add type annotation for append_keys method ([#3988](https://github.com/aws-powertools/powertools-lambda-python/issues/3988)) +* **parameters:** improve typing for get_secret method ([#3910](https://github.com/aws-powertools/powertools-lambda-python/issues/3910)) + +## Documentation + +* **batch:** improved the example demonstrating how to create a custom partial processor. ([#4024](https://github.com/aws-powertools/powertools-lambda-python/issues/4024)) +* **bedrock-agents:** fix type in Bedrock operation example ([#3948](https://github.com/aws-powertools/powertools-lambda-python/issues/3948)) +* **tutorial:** fix "Simplifying with Tracer" section in the tutorial ([#3962](https://github.com/aws-powertools/powertools-lambda-python/issues/3962)) + +## Features + +* **batch:** add flag in SqsFifoProcessor to enable continuous message processing ([#3954](https://github.com/aws-powertools/powertools-lambda-python/issues/3954)) +* **data_classes:** Add CloudWatchAlarmEvent data class ([#3868](https://github.com/aws-powertools/powertools-lambda-python/issues/3868)) +* **event-handler:** add compress option when serving Swagger HTML ([#3946](https://github.com/aws-powertools/powertools-lambda-python/issues/3946)) +* **event_handler:** define exception_handler directly from the router ([#3979](https://github.com/aws-powertools/powertools-lambda-python/issues/3979)) +* **metrics:** allow custom timestamps for metrics ([#4006](https://github.com/aws-powertools/powertools-lambda-python/issues/4006)) +* **parameters:** add feature for creating and updating Parameters and Secrets ([#2858](https://github.com/aws-powertools/powertools-lambda-python/issues/2858)) +* **tracer:** auto-disable tracer when for AWS SAM and Chalice environments ([#3949](https://github.com/aws-powertools/powertools-lambda-python/issues/3949)) + +## Maintenance + +* version bump +* **deps:** bump squidfunk/mkdocs-material from `3678304` to `6c81a89` in /docs ([#3973](https://github.com/aws-powertools/powertools-lambda-python/issues/3973)) +* **deps:** bump datadog-lambda from 5.89.0 to 5.90.0 ([#3941](https://github.com/aws-powertools/powertools-lambda-python/issues/3941)) +* **deps:** bump actions/checkout from 4.1.1 to 4.1.2 ([#3939](https://github.com/aws-powertools/powertools-lambda-python/issues/3939)) +* **deps:** bump redis from 5.0.2 to 5.0.3 ([#3929](https://github.com/aws-powertools/powertools-lambda-python/issues/3929)) +* **deps:** bump slsa-framework/slsa-github-generator from 1.9.0 to 1.10.0 ([#3997](https://github.com/aws-powertools/powertools-lambda-python/issues/3997)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#4001](https://github.com/aws-powertools/powertools-lambda-python/issues/4001)) +* **deps:** bump actions/dependency-review-action from 4.2.3 to 4.2.4 ([#4012](https://github.com/aws-powertools/powertools-lambda-python/issues/4012)) +* **deps:** bump docker/setup-buildx-action from 3.1.0 to 3.2.0 ([#3955](https://github.com/aws-powertools/powertools-lambda-python/issues/3955)) +* **deps:** bump actions/dependency-review-action from 4.1.3 to 4.2.3 ([#3993](https://github.com/aws-powertools/powertools-lambda-python/issues/3993)) +* **deps:** bump datadog-lambda from 5.90.0 to 5.91.0 ([#3958](https://github.com/aws-powertools/powertools-lambda-python/issues/3958)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.12 to 1.8.14 ([#3918](https://github.com/aws-powertools/powertools-lambda-python/issues/3918)) +* **deps:** bump squidfunk/mkdocs-material from `6c81a89` to `3307665` in /docs ([#4017](https://github.com/aws-powertools/powertools-lambda-python/issues/4017)) +* **deps:** bump actions/dependency-review-action from 4.2.4 to 4.2.5 ([#4023](https://github.com/aws-powertools/powertools-lambda-python/issues/4023)) +* **deps:** bump aws-encryption-sdk from 3.1.1 to 3.2.0 ([#3983](https://github.com/aws-powertools/powertools-lambda-python/issues/3983)) +* **deps:** bump actions/setup-python from 5.0.0 to 5.1.0 ([#4022](https://github.com/aws-powertools/powertools-lambda-python/issues/4022)) +* **deps:** bump codecov/codecov-action from 4.1.0 to 4.1.1 ([#4021](https://github.com/aws-powertools/powertools-lambda-python/issues/4021)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3972](https://github.com/aws-powertools/powertools-lambda-python/issues/3972)) +* **deps-dev:** bump filelock from 3.13.1 to 3.13.3 ([#4014](https://github.com/aws-powertools/powertools-lambda-python/issues/4014)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.90 to 0.1.91 ([#3975](https://github.com/aws-powertools/powertools-lambda-python/issues/3975)) +* **deps-dev:** bump types-python-dateutil from 2.9.0.20240315 to 2.9.0.20240316 ([#3977](https://github.com/aws-powertools/powertools-lambda-python/issues/3977)) +* **deps-dev:** bump mkdocs-material from 9.5.13 to 9.5.14 ([#3978](https://github.com/aws-powertools/powertools-lambda-python/issues/3978)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.132.1a0 to 2.133.0a0 ([#3976](https://github.com/aws-powertools/powertools-lambda-python/issues/3976)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3974](https://github.com/aws-powertools/powertools-lambda-python/issues/3974)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3982](https://github.com/aws-powertools/powertools-lambda-python/issues/3982)) +* **deps-dev:** bump ruff from 0.3.2 to 0.3.3 ([#3967](https://github.com/aws-powertools/powertools-lambda-python/issues/3967)) +* **deps-dev:** bump aws-cdk-lib from 2.132.1 to 2.133.0 ([#3965](https://github.com/aws-powertools/powertools-lambda-python/issues/3965)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.91 to 0.1.94 ([#3985](https://github.com/aws-powertools/powertools-lambda-python/issues/3985)) +* **deps-dev:** bump black from 24.2.0 to 24.3.0 ([#3968](https://github.com/aws-powertools/powertools-lambda-python/issues/3968)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.20240311 to 2.9.0.20240315 ([#3966](https://github.com/aws-powertools/powertools-lambda-python/issues/3966)) +* **deps-dev:** bump aws-cdk from 2.132.1 to 2.133.0 ([#3963](https://github.com/aws-powertools/powertools-lambda-python/issues/3963)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3964](https://github.com/aws-powertools/powertools-lambda-python/issues/3964)) +* **deps-dev:** bump pytest-asyncio from 0.23.5.post1 to 0.23.6 ([#3984](https://github.com/aws-powertools/powertools-lambda-python/issues/3984)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3991](https://github.com/aws-powertools/powertools-lambda-python/issues/3991)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.89 to 0.1.90 ([#3957](https://github.com/aws-powertools/powertools-lambda-python/issues/3957)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3956](https://github.com/aws-powertools/powertools-lambda-python/issues/3956)) +* **deps-dev:** bump sentry-sdk from 1.42.0 to 1.43.0 ([#3992](https://github.com/aws-powertools/powertools-lambda-python/issues/3992)) +* **deps-dev:** bump coverage from 7.4.3 to 7.4.4 ([#3959](https://github.com/aws-powertools/powertools-lambda-python/issues/3959)) +* **deps-dev:** bump ruff from 0.3.3 to 0.3.4 ([#3996](https://github.com/aws-powertools/powertools-lambda-python/issues/3996)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.88 to 0.1.89 ([#3952](https://github.com/aws-powertools/powertools-lambda-python/issues/3952)) +* **deps-dev:** bump sentry-sdk from 1.41.0 to 1.42.0 ([#3951](https://github.com/aws-powertools/powertools-lambda-python/issues/3951)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3950](https://github.com/aws-powertools/powertools-lambda-python/issues/3950)) +* **deps-dev:** bump pytest-mock from 3.12.0 to 3.13.0 ([#3999](https://github.com/aws-powertools/powertools-lambda-python/issues/3999)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.94 to 0.1.96 ([#4002](https://github.com/aws-powertools/powertools-lambda-python/issues/4002)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3940](https://github.com/aws-powertools/powertools-lambda-python/issues/3940)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.87 to 0.1.88 ([#3942](https://github.com/aws-powertools/powertools-lambda-python/issues/3942)) +* **deps-dev:** bump pytest from 8.0.2 to 8.1.1 ([#3943](https://github.com/aws-powertools/powertools-lambda-python/issues/3943)) +* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.131.0a0 to 2.132.1a0 ([#3944](https://github.com/aws-powertools/powertools-lambda-python/issues/3944)) +* **deps-dev:** bump cfn-lint from 0.86.0 to 0.86.1 ([#3998](https://github.com/aws-powertools/powertools-lambda-python/issues/3998)) +* **deps-dev:** bump aws-cdk from 2.132.0 to 2.132.1 ([#3938](https://github.com/aws-powertools/powertools-lambda-python/issues/3938)) +* **deps-dev:** bump aws-cdk-lib from 2.131.0 to 2.132.1 ([#3936](https://github.com/aws-powertools/powertools-lambda-python/issues/3936)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.96 to 0.1.99 ([#4008](https://github.com/aws-powertools/powertools-lambda-python/issues/4008)) +* **deps-dev:** bump aws-cdk from 2.131.0 to 2.132.0 ([#3928](https://github.com/aws-powertools/powertools-lambda-python/issues/3928)) +* **deps-dev:** bump types-redis from 4.6.0.20240218 to 4.6.0.20240311 ([#3931](https://github.com/aws-powertools/powertools-lambda-python/issues/3931)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.20240106 to 2.8.19.20240311 ([#3932](https://github.com/aws-powertools/powertools-lambda-python/issues/3932)) +* **deps-dev:** bump pytest-mock from 3.13.0 to 3.14.0 ([#4007](https://github.com/aws-powertools/powertools-lambda-python/issues/4007)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.99 to 0.1.101 ([#4015](https://github.com/aws-powertools/powertools-lambda-python/issues/4015)) +* **deps-dev:** bump ruff from 0.3.0 to 0.3.2 ([#3925](https://github.com/aws-powertools/powertools-lambda-python/issues/3925)) +* **deps-dev:** bump mypy from 1.8.0 to 1.9.0 ([#3921](https://github.com/aws-powertools/powertools-lambda-python/issues/3921)) +* **deps-dev:** bump bandit from 1.7.7 to 1.7.8 ([#3920](https://github.com/aws-powertools/powertools-lambda-python/issues/3920)) +* **deps-dev:** bump pytest-cov from 4.1.0 to 5.0.0 ([#4013](https://github.com/aws-powertools/powertools-lambda-python/issues/4013)) +* **deps-dev:** bump pytest-asyncio from 0.23.5 to 0.23.5.post1 ([#3923](https://github.com/aws-powertools/powertools-lambda-python/issues/3923)) +* **deps-dev:** bump mkdocs-material from 9.5.14 to 9.5.15 ([#4016](https://github.com/aws-powertools/powertools-lambda-python/issues/4016)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3919](https://github.com/aws-powertools/powertools-lambda-python/issues/3919)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.101 to 0.1.104 ([#4020](https://github.com/aws-powertools/powertools-lambda-python/issues/4020)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.83 to 0.1.87 ([#3930](https://github.com/aws-powertools/powertools-lambda-python/issues/3930)) + + +<a name="v2.35.1"></a> +## [v2.35.1] - 2024-03-08 +## Bug Fixes + +* **data_sources:** ensure correct types on SQSMessageAttributes ([#3898](https://github.com/aws-powertools/powertools-lambda-python/issues/3898)) +* **event_handler:** validate POST bodies on BedrockAgentResolver ([#3903](https://github.com/aws-powertools/powertools-lambda-python/issues/3903)) +* **internal:** call ruff with correct args ([#3901](https://github.com/aws-powertools/powertools-lambda-python/issues/3901)) + +## Features + +* **event_handler:** use custom serializer during openapi serialization ([#3900](https://github.com/aws-powertools/powertools-lambda-python/issues/3900)) + +## Maintenance + +* version bump +* **deps:** bump aws-xray-sdk from 2.12.1 to 2.13.0 ([#3906](https://github.com/aws-powertools/powertools-lambda-python/issues/3906)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3911](https://github.com/aws-powertools/powertools-lambda-python/issues/3911)) +* **deps:** bump squidfunk/mkdocs-material from `7be068b` to `3678304` in /docs ([#3894](https://github.com/aws-powertools/powertools-lambda-python/issues/3894)) +* **deps:** bump datadog-lambda from 5.88.0 to 5.89.0 ([#3907](https://github.com/aws-powertools/powertools-lambda-python/issues/3907)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.81 to 0.1.82 ([#3896](https://github.com/aws-powertools/powertools-lambda-python/issues/3896)) +* **deps-dev:** bump sentry-sdk from 1.40.6 to 1.41.0 ([#3905](https://github.com/aws-powertools/powertools-lambda-python/issues/3905)) +* **deps-dev:** bump mkdocs-material from 9.5.12 to 9.5.13 ([#3895](https://github.com/aws-powertools/powertools-lambda-python/issues/3895)) +* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.82 to 0.1.83 ([#3908](https://github.com/aws-powertools/powertools-lambda-python/issues/3908)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3904](https://github.com/aws-powertools/powertools-lambda-python/issues/3904)) + + +<a name="v2.35.0"></a> +## [v2.35.0] - 2024-03-06 +## Bug Fixes + +* **event_handler:** OpenAPI schema version respects Pydantic version ([#3860](https://github.com/aws-powertools/powertools-lambda-python/issues/3860)) + +## Code Refactoring + +* **logger:** improve typing ([#3869](https://github.com/aws-powertools/powertools-lambda-python/issues/3869)) + +## Documentation + +* **event_handler:** add bedrock agent resolver documentation ([#3602](https://github.com/aws-powertools/powertools-lambda-python/issues/3602)) + +## Maintenance + +* version bump +* **deps:** bump docker/setup-buildx-action from 3.0.0 to 3.1.0 ([#3864](https://github.com/aws-powertools/powertools-lambda-python/issues/3864)) +* **deps:** bump actions/download-artifact from 4.1.3 to 4.1.4 ([#3875](https://github.com/aws-powertools/powertools-lambda-python/issues/3875)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3884](https://github.com/aws-powertools/powertools-lambda-python/issues/3884)) +* **deps:** bump squidfunk/mkdocs-material from `49d1bfd` to `7be068b` in /docs ([#3872](https://github.com/aws-powertools/powertools-lambda-python/issues/3872)) +* **deps:** bump squidfunk/mkdocs-material from `43b898a` to `49d1bfd` in /docs ([#3857](https://github.com/aws-powertools/powertools-lambda-python/issues/3857)) +* **deps:** bump codecov/codecov-action from 4.0.2 to 4.1.0 ([#3856](https://github.com/aws-powertools/powertools-lambda-python/issues/3856)) +* **deps:** bump redis from 5.0.1 to 5.0.2 ([#3867](https://github.com/aws-powertools/powertools-lambda-python/issues/3867)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3887](https://github.com/aws-powertools/powertools-lambda-python/issues/3887)) +* **deps:** bump actions/download-artifact from 4.1.2 to 4.1.3 ([#3862](https://github.com/aws-powertools/powertools-lambda-python/issues/3862)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.11 to 1.8.12 ([#3863](https://github.com/aws-powertools/powertools-lambda-python/issues/3863)) +* **deps-dev:** bump aws-cdk-lib from 2.130.0 to 2.131.0 ([#3881](https://github.com/aws-powertools/powertools-lambda-python/issues/3881)) +* **deps-dev:** bump cfn-lint from 0.85.3 to 0.86.0 ([#3882](https://github.com/aws-powertools/powertools-lambda-python/issues/3882)) +* **deps-dev:** bump black from 24.1.1 to 24.2.0 ([#3760](https://github.com/aws-powertools/powertools-lambda-python/issues/3760)) +* **deps-dev:** bump cfn-lint from 0.85.2 to 0.85.3 ([#3861](https://github.com/aws-powertools/powertools-lambda-python/issues/3861)) +* **deps-dev:** bump aws-cdk from 2.130.0 to 2.131.0 ([#3883](https://github.com/aws-powertools/powertools-lambda-python/issues/3883)) +* **deps-dev:** bump mkdocs-material from 9.5.11 to 9.5.12 ([#3870](https://github.com/aws-powertools/powertools-lambda-python/issues/3870)) +* **deps-dev:** bump ruff from 0.2.2 to 0.3.0 ([#3871](https://github.com/aws-powertools/powertools-lambda-python/issues/3871)) +* **docs:** add Bedrock Agents to feature list ([#3889](https://github.com/aws-powertools/powertools-lambda-python/issues/3889)) + + +<a name="v2.34.2"></a> +## [v2.34.2] - 2024-02-26 +## Bug Fixes + +* **typing:** ensure return type is a str when default_value is set ([#3840](https://github.com/aws-powertools/powertools-lambda-python/issues/3840)) + +## Documentation + +* **install:** make minimum install the default option then extra ([#3834](https://github.com/aws-powertools/powertools-lambda-python/issues/3834)) + +## Features + +* **event-source:** add function to get multi-value query string params by name ([#3846](https://github.com/aws-powertools/powertools-lambda-python/issues/3846)) + +## Maintenance + +* version bump +* **ci:** remove aws-encryption-sdk from Lambda layer due to cffi being tied to python version ([#3853](https://github.com/aws-powertools/powertools-lambda-python/issues/3853)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3844](https://github.com/aws-powertools/powertools-lambda-python/issues/3844)) +* **deps:** bump cryptography from 42.0.2 to 42.0.4 ([#3827](https://github.com/aws-powertools/powertools-lambda-python/issues/3827)) +* **deps:** bump codecov/codecov-action from 4.0.1 to 4.0.2 ([#3842](https://github.com/aws-powertools/powertools-lambda-python/issues/3842)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3835](https://github.com/aws-powertools/powertools-lambda-python/issues/3835)) +* **deps-dev:** bump httpx from 0.26.0 to 0.27.0 ([#3828](https://github.com/aws-powertools/powertools-lambda-python/issues/3828)) +* **deps-dev:** bump aws-cdk from 2.128.0 to 2.129.0 ([#3831](https://github.com/aws-powertools/powertools-lambda-python/issues/3831)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3836](https://github.com/aws-powertools/powertools-lambda-python/issues/3836)) +* **deps-dev:** bump aws-cdk from 2.129.0 to 2.130.0 ([#3843](https://github.com/aws-powertools/powertools-lambda-python/issues/3843)) +* **deps-dev:** bump aws-cdk-lib from 2.128.0 to 2.130.0 ([#3838](https://github.com/aws-powertools/powertools-lambda-python/issues/3838)) + + +<a name="v2.34.1"></a> +## [v2.34.1] - 2024-02-21 +## Bug Fixes + +* **ci:** inject PR_LABELS env for PR Label automation ([#3819](https://github.com/aws-powertools/powertools-lambda-python/issues/3819)) +* **ci:** revert layer version bump write-only back to append ([#3818](https://github.com/aws-powertools/powertools-lambda-python/issues/3818)) +* **event-handler:** return dict on missing multi_value_headers ([#3824](https://github.com/aws-powertools/powertools-lambda-python/issues/3824)) +* **idempotency:** validate before saving to cache ([#3822](https://github.com/aws-powertools/powertools-lambda-python/issues/3822)) + +## Maintenance + +* version bump +* **deps-dev:** bump ruff from 0.2.1 to 0.2.2 ([#3802](https://github.com/aws-powertools/powertools-lambda-python/issues/3802)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3810](https://github.com/aws-powertools/powertools-lambda-python/issues/3810)) + + +<a name="v2.34.0"></a> +## [v2.34.0] - 2024-02-21 +## Bug Fixes + +* **ci:** create one layer artifact per region & merge ([#3808](https://github.com/aws-powertools/powertools-lambda-python/issues/3808)) +* **event-handler:** multi-value query string and validation of scalar parameters ([#3795](https://github.com/aws-powertools/powertools-lambda-python/issues/3795)) +* **event-handler:** swagger schema respects api stage ([#3796](https://github.com/aws-powertools/powertools-lambda-python/issues/3796)) +* **event-handler:** handle aliased parameters e.g., Query(alias="categoryType") ([#3766](https://github.com/aws-powertools/powertools-lambda-python/issues/3766)) + +## Code Refactoring + +* **feature-flags:** add intersection tests; structure refinement ([#3775](https://github.com/aws-powertools/powertools-lambda-python/issues/3775)) + +## Documentation + +* **feature_flags:** fix incorrect line markers and envelope name ([#3792](https://github.com/aws-powertools/powertools-lambda-python/issues/3792)) +* **home:** update layer version to 62 for package version 2.33.1 ([#3778](https://github.com/aws-powertools/powertools-lambda-python/issues/3778)) +* **home:** add note about POWERTOOLS_DEV side effects in CloudWatch Logs ([#3770](https://github.com/aws-powertools/powertools-lambda-python/issues/3770)) +* **homepage:** discord flat badge style; remove former devax email ([#3768](https://github.com/aws-powertools/powertools-lambda-python/issues/3768)) +* **homepage:** remove leftover announcement banner ([#3783](https://github.com/aws-powertools/powertools-lambda-python/issues/3783)) +* **roadmap:** latest roadmap update; use new grid to de-clutter homepage ([#3755](https://github.com/aws-powertools/powertools-lambda-python/issues/3755)) +* **we-made-this:** add swagger post ([#3799](https://github.com/aws-powertools/powertools-lambda-python/issues/3799)) +* **we-made-this:** add reinvent 2023 session ([#3790](https://github.com/aws-powertools/powertools-lambda-python/issues/3790)) + +## Features + +* **feature_flags:** add intersect actions for conditions ([#3692](https://github.com/aws-powertools/powertools-lambda-python/issues/3692)) + +## Maintenance + +* version bump +* **deps:** bump actions/dependency-review-action from 4.1.2 to 4.1.3 ([#3813](https://github.com/aws-powertools/powertools-lambda-python/issues/3813)) +* **deps:** bump actions/dependency-review-action from 4.1.0 to 4.1.2 ([#3800](https://github.com/aws-powertools/powertools-lambda-python/issues/3800)) +* **deps:** bump actions/dependency-review-action from 4.0.0 to 4.1.0 ([#3771](https://github.com/aws-powertools/powertools-lambda-python/issues/3771)) +* **deps:** bump squidfunk/mkdocs-material from `62d3668` to `43b898a` in /docs ([#3801](https://github.com/aws-powertools/powertools-lambda-python/issues/3801)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3764](https://github.com/aws-powertools/powertools-lambda-python/issues/3764)) +* **deps:** bump squidfunk/mkdocs-material from `6a72238` to `62d3668` in /docs ([#3756](https://github.com/aws-powertools/powertools-lambda-python/issues/3756)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3814](https://github.com/aws-powertools/powertools-lambda-python/issues/3814)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3784](https://github.com/aws-powertools/powertools-lambda-python/issues/3784)) +* **deps-dev:** bump pytest from 8.0.0 to 8.0.1 ([#3812](https://github.com/aws-powertools/powertools-lambda-python/issues/3812)) +* **deps-dev:** bump aws-cdk from 2.127.0 to 2.128.0 ([#3776](https://github.com/aws-powertools/powertools-lambda-python/issues/3776)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3797](https://github.com/aws-powertools/powertools-lambda-python/issues/3797)) +* **deps-dev:** bump cfn-lint from 0.85.1 to 0.85.2 ([#3786](https://github.com/aws-powertools/powertools-lambda-python/issues/3786)) +* **deps-dev:** bump pytest-asyncio from 0.21.1 to 0.23.5 ([#3773](https://github.com/aws-powertools/powertools-lambda-python/issues/3773)) +* **deps-dev:** bump aws-cdk-lib from 2.127.0 to 2.128.0 ([#3777](https://github.com/aws-powertools/powertools-lambda-python/issues/3777)) +* **deps-dev:** bump sentry-sdk from 1.40.3 to 1.40.4 ([#3765](https://github.com/aws-powertools/powertools-lambda-python/issues/3765)) +* **deps-dev:** bump sentry-sdk from 1.40.4 to 1.40.5 ([#3805](https://github.com/aws-powertools/powertools-lambda-python/issues/3805)) +* **deps-dev:** bump mkdocs-material from 9.5.9 to 9.5.10 ([#3803](https://github.com/aws-powertools/powertools-lambda-python/issues/3803)) +* **deps-dev:** bump types-redis from 4.6.0.20240106 to 4.6.0.20240218 ([#3804](https://github.com/aws-powertools/powertools-lambda-python/issues/3804)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3757](https://github.com/aws-powertools/powertools-lambda-python/issues/3757)) +* **deps-dev:** bump aws-cdk-lib from 2.126.0 to 2.127.0 ([#3758](https://github.com/aws-powertools/powertools-lambda-python/issues/3758)) +* **deps-dev:** bump aws-cdk from 2.126.0 to 2.127.0 ([#3761](https://github.com/aws-powertools/powertools-lambda-python/issues/3761)) +* **deps-dev:** bump mkdocs-material from 9.5.8 to 9.5.9 ([#3759](https://github.com/aws-powertools/powertools-lambda-python/issues/3759)) +* **deps-dev:** bump sentry-sdk from 1.40.2 to 1.40.3 ([#3750](https://github.com/aws-powertools/powertools-lambda-python/issues/3750)) +* **deps-dev:** bump cfn-lint from 0.85.0 to 0.85.1 ([#3749](https://github.com/aws-powertools/powertools-lambda-python/issues/3749)) +* **deps-dev:** bump coverage from 7.4.1 to 7.4.2 ([#3811](https://github.com/aws-powertools/powertools-lambda-python/issues/3811)) +* **tests:** increase idempotency coverage with nested payload tampering tests ([#3809](https://github.com/aws-powertools/powertools-lambda-python/issues/3809)) + + +<a name="v2.33.1"></a> +## [v2.33.1] - 2024-02-09 +## Bug Fixes + +* **typing:** make Response headers covariant ([#3745](https://github.com/aws-powertools/powertools-lambda-python/issues/3745)) + +## Documentation + +* Add nathan hanks post community ([#3727](https://github.com/aws-powertools/powertools-lambda-python/issues/3727)) + +## Maintenance + +* version bump +* **ci:** drop support for Python 3.7 ([#3638](https://github.com/aws-powertools/powertools-lambda-python/issues/3638)) +* **ci:** enable Redis e2e tests ([#3718](https://github.com/aws-powertools/powertools-lambda-python/issues/3718)) +* **deps:** bump actions/setup-node from 4.0.1 to 4.0.2 ([#3737](https://github.com/aws-powertools/powertools-lambda-python/issues/3737)) +* **deps:** bump squidfunk/mkdocs-material from `e0d6c67` to `6a72238` in /docs ([#3735](https://github.com/aws-powertools/powertools-lambda-python/issues/3735)) +* **deps:** bump actions/dependency-review-action from 3.1.5 to 4.0.0 ([#3646](https://github.com/aws-powertools/powertools-lambda-python/issues/3646)) +* **deps:** bump release-drafter/release-drafter from 5.25.0 to 6.0.0 ([#3699](https://github.com/aws-powertools/powertools-lambda-python/issues/3699)) +* **deps:** bump actions/download-artifact from 4.1.1 to 4.1.2 ([#3725](https://github.com/aws-powertools/powertools-lambda-python/issues/3725)) +* **deps:** bump squidfunk/mkdocs-material from `a4a2029` to `e0d6c67` in /docs ([#3708](https://github.com/aws-powertools/powertools-lambda-python/issues/3708)) +* **deps:** bump codecov/codecov-action from 3.1.6 to 4.0.1 ([#3700](https://github.com/aws-powertools/powertools-lambda-python/issues/3700)) +* **deps:** bump actions/download-artifact from 3.0.2 to 4.1.1 ([#3612](https://github.com/aws-powertools/powertools-lambda-python/issues/3612)) +* **deps:** revert aws-cdk-lib as a runtime dep ([#3730](https://github.com/aws-powertools/powertools-lambda-python/issues/3730)) +* **deps:** bump actions/upload-artifact from 3.1.3 to 4.3.1 ([#3714](https://github.com/aws-powertools/powertools-lambda-python/issues/3714)) +* **deps-dev:** bump cfn-lint from 0.83.8 to 0.85.0 ([#3724](https://github.com/aws-powertools/powertools-lambda-python/issues/3724)) +* **deps-dev:** bump httpx from 0.24.1 to 0.26.0 ([#3712](https://github.com/aws-powertools/powertools-lambda-python/issues/3712)) +* **deps-dev:** bump pytest from 7.4.4 to 8.0.0 ([#3711](https://github.com/aws-powertools/powertools-lambda-python/issues/3711)) +* **deps-dev:** bump sentry-sdk from 1.40.1 to 1.40.2 ([#3740](https://github.com/aws-powertools/powertools-lambda-python/issues/3740)) +* **deps-dev:** bump coverage from 7.2.7 to 7.4.1 ([#3713](https://github.com/aws-powertools/powertools-lambda-python/issues/3713)) +* **deps-dev:** bump the boto-typing group with 7 updates ([#3709](https://github.com/aws-powertools/powertools-lambda-python/issues/3709)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.14 to 2.8.19.20240106 ([#3720](https://github.com/aws-powertools/powertools-lambda-python/issues/3720)) +* **deps-dev:** bump mypy from 1.4.1 to 1.8.0 ([#3710](https://github.com/aws-powertools/powertools-lambda-python/issues/3710)) +* **deps-dev:** bump ruff from 0.2.0 to 0.2.1 ([#3742](https://github.com/aws-powertools/powertools-lambda-python/issues/3742)) +* **deps-dev:** bump isort from 5.11.5 to 5.13.2 ([#3723](https://github.com/aws-powertools/powertools-lambda-python/issues/3723)) +* **deps-dev:** bump pytest-socket from 0.6.0 to 0.7.0 ([#3721](https://github.com/aws-powertools/powertools-lambda-python/issues/3721)) +* **deps-dev:** bump ruff from 0.1.15 to 0.2.0 ([#3702](https://github.com/aws-powertools/powertools-lambda-python/issues/3702)) +* **deps-dev:** bump aws-cdk from 2.125.0 to 2.126.0 ([#3701](https://github.com/aws-powertools/powertools-lambda-python/issues/3701)) +* **deps-dev:** bump hvac from 1.2.1 to 2.1.0 ([#3738](https://github.com/aws-powertools/powertools-lambda-python/issues/3738)) +* **deps-dev:** bump black from 23.12.1 to 24.1.1 ([#3739](https://github.com/aws-powertools/powertools-lambda-python/issues/3739)) + + +<a name="v2.33.0"></a> +## [v2.33.0] - 2024-02-02 +## Bug Fixes + +* **data-masking:** fix and improve e2e tests for DataMasking ([#3695](https://github.com/aws-powertools/powertools-lambda-python/issues/3695)) +* **event-handler:** strip whitespace from Content-Type headers during OpenAPI schema validation ([#3677](https://github.com/aws-powertools/powertools-lambda-python/issues/3677)) + +## Documentation + +* **data-masking:** add docs for data masking utility ([#3186](https://github.com/aws-powertools/powertools-lambda-python/issues/3186)) +* **metrics:** fix empty metric warning filter ([#3660](https://github.com/aws-powertools/powertools-lambda-python/issues/3660)) +* **proccess:** add versioning and maintenance policy ([#3682](https://github.com/aws-powertools/powertools-lambda-python/issues/3682)) + +## Features + +* **event_handler:** support Header parameter validation in OpenAPI schema ([#3687](https://github.com/aws-powertools/powertools-lambda-python/issues/3687)) +* **event_handler:** add support for multiValueQueryStringParameters in OpenAPI schema ([#3667](https://github.com/aws-powertools/powertools-lambda-python/issues/3667)) + +## Maintenance + +* version bump +* **deps:** bump codecov/codecov-action from 3.1.5 to 3.1.6 ([#3683](https://github.com/aws-powertools/powertools-lambda-python/issues/3683)) +* **deps:** bump codecov/codecov-action from 3.1.4 to 3.1.5 ([#3674](https://github.com/aws-powertools/powertools-lambda-python/issues/3674)) +* **deps:** bump pydantic from 1.10.13 to 1.10.14 ([#3655](https://github.com/aws-powertools/powertools-lambda-python/issues/3655)) +* **deps:** bump squidfunk/mkdocs-material from `58eef6c` to `9aad7af` in /docs ([#3670](https://github.com/aws-powertools/powertools-lambda-python/issues/3670)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3665](https://github.com/aws-powertools/powertools-lambda-python/issues/3665)) +* **deps:** bump squidfunk/mkdocs-material from `9aad7af` to `a4a2029` in /docs ([#3679](https://github.com/aws-powertools/powertools-lambda-python/issues/3679)) +* **deps-dev:** bump sentry-sdk from 1.39.2 to 1.40.0 ([#3684](https://github.com/aws-powertools/powertools-lambda-python/issues/3684)) +* **deps-dev:** bump ruff from 0.1.14 to 0.1.15 ([#3685](https://github.com/aws-powertools/powertools-lambda-python/issues/3685)) +* **deps-dev:** bump ruff from 0.1.13 to 0.1.14 ([#3656](https://github.com/aws-powertools/powertools-lambda-python/issues/3656)) +* **deps-dev:** bump aws-cdk from 2.122.0 to 2.123.0 ([#3673](https://github.com/aws-powertools/powertools-lambda-python/issues/3673)) +* **deps-dev:** bump aws-cdk from 2.124.0 to 2.125.0 ([#3693](https://github.com/aws-powertools/powertools-lambda-python/issues/3693)) +* **deps-dev:** bump aws-cdk from 2.123.0 to 2.124.0 ([#3678](https://github.com/aws-powertools/powertools-lambda-python/issues/3678)) + + +<a name="v2.32.0"></a> +## [v2.32.0] - 2024-01-19 +## Bug Fixes + +* **event_handler:** escape OpenAPI schema on Swagger UI ([#3606](https://github.com/aws-powertools/powertools-lambda-python/issues/3606)) + +## Code Refactoring + +* **event-handler:** Inject CSS and JS files into SwaggerUI route when no custom CDN is used. ([#3562](https://github.com/aws-powertools/powertools-lambda-python/issues/3562)) +* **event_handler:** fix BedrockAgentResolver docstring ([#3645](https://github.com/aws-powertools/powertools-lambda-python/issues/3645)) + +## Documentation + +* **homepage:** add banner about Python 3.7 deprecation ([#3618](https://github.com/aws-powertools/powertools-lambda-python/issues/3618)) +* **i-made-this:** added new article on how to create a serverless API with CDK and Powertools ([#3605](https://github.com/aws-powertools/powertools-lambda-python/issues/3605)) + +## Features + +* **event_handler:** add support for additional response models ([#3591](https://github.com/aws-powertools/powertools-lambda-python/issues/3591)) +* **event_handler:** add support to download OpenAPI spec file ([#3571](https://github.com/aws-powertools/powertools-lambda-python/issues/3571)) +* **event_source:** Add support for S3 batch operations ([#3572](https://github.com/aws-powertools/powertools-lambda-python/issues/3572)) +* **event_source:** Add support for policyLevel field in CloudWatch Logs event and parser ([#3624](https://github.com/aws-powertools/powertools-lambda-python/issues/3624)) +* **idempotency:** leverage new DynamoDB Failed conditional writes behavior with ReturnValuesOnConditionCheckFailure ([#3446](https://github.com/aws-powertools/powertools-lambda-python/issues/3446)) +* **idempotency:** adding redis as idempotency backend ([#2567](https://github.com/aws-powertools/powertools-lambda-python/issues/2567)) + +## Maintenance + +* version bump +* **ci:** Disable Redis e2e until we drop Python 3.7 ([#3652](https://github.com/aws-powertools/powertools-lambda-python/issues/3652)) +* **ci:** update boto3 library version to 1.26.164+ ([#3632](https://github.com/aws-powertools/powertools-lambda-python/issues/3632)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3649](https://github.com/aws-powertools/powertools-lambda-python/issues/3649)) +* **deps:** bump jinja2 from 3.1.2 to 3.1.3 in /docs ([#3620](https://github.com/aws-powertools/powertools-lambda-python/issues/3620)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3639](https://github.com/aws-powertools/powertools-lambda-python/issues/3639)) +* **deps:** bump gitpython from 3.1.37 to 3.1.41 in /docs ([#3610](https://github.com/aws-powertools/powertools-lambda-python/issues/3610)) +* **deps:** bump squidfunk/mkdocs-material from `2f29d71` to `58eef6c` in /docs ([#3633](https://github.com/aws-powertools/powertools-lambda-python/issues/3633)) +* **deps:** bump redis from 4.6.0 to 5.0.1 ([#3613](https://github.com/aws-powertools/powertools-lambda-python/issues/3613)) +* **deps-dev:** bump gitpython from 3.1.40 to 3.1.41 ([#3611](https://github.com/aws-powertools/powertools-lambda-python/issues/3611)) +* **deps-dev:** bump sentry-sdk from 1.39.1 to 1.39.2 ([#3614](https://github.com/aws-powertools/powertools-lambda-python/issues/3614)) +* **deps-dev:** bump aws-cdk from 2.120.0 to 2.121.1 ([#3634](https://github.com/aws-powertools/powertools-lambda-python/issues/3634)) +* **deps-dev:** bump jinja2 from 3.1.2 to 3.1.3 ([#3619](https://github.com/aws-powertools/powertools-lambda-python/issues/3619)) +* **deps-dev:** bump cfn-lint from 0.83.7 to 0.83.8 ([#3603](https://github.com/aws-powertools/powertools-lambda-python/issues/3603)) +* **deps-dev:** bump aws-cdk from 2.121.1 to 2.122.0 ([#3648](https://github.com/aws-powertools/powertools-lambda-python/issues/3648)) +* **deps-dev:** bump ruff from 0.1.11 to 0.1.13 ([#3625](https://github.com/aws-powertools/powertools-lambda-python/issues/3625)) +* **deps-dev:** bump aws-cdk from 2.118.0 to 2.120.0 ([#3627](https://github.com/aws-powertools/powertools-lambda-python/issues/3627)) + + +<a name="v2.31.0"></a> +## [v2.31.0] - 2024-01-05 +## Bug Fixes + +* **ci:** fail dispatch analytics job when Lambda call fails ([#3579](https://github.com/aws-powertools/powertools-lambda-python/issues/3579)) + +## Code Refactoring + +* **parameters:** add overload signatures for get_parameter and get_parameters ([#3534](https://github.com/aws-powertools/powertools-lambda-python/issues/3534)) +* **parser:** Improve error message when parsing models and envelopes ([#3587](https://github.com/aws-powertools/powertools-lambda-python/issues/3587)) + +## Documentation + +* **middleware-factory:** Fix and improve typing ([#3569](https://github.com/aws-powertools/powertools-lambda-python/issues/3569)) + +## Features + +* **event-handler:** add description to request body in OpenAPI schema ([#3548](https://github.com/aws-powertools/powertools-lambda-python/issues/3548)) +* **event_handler:** support richer top level Tags ([#3543](https://github.com/aws-powertools/powertools-lambda-python/issues/3543)) +* **layers:** add new comercial region Canada - ca-west-1 ([#3549](https://github.com/aws-powertools/powertools-lambda-python/issues/3549)) + +## Maintenance + +* version bump +* **ci:** Remove dev dependencies locked to Pydantic v1 within the Pydantic v2 workflow. ([#3582](https://github.com/aws-powertools/powertools-lambda-python/issues/3582)) +* **deps:** bump squidfunk/mkdocs-material from `9af3b7e` to `2f29d71` in /docs ([#3559](https://github.com/aws-powertools/powertools-lambda-python/issues/3559)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 4 updates ([#3593](https://github.com/aws-powertools/powertools-lambda-python/issues/3593)) +* **deps:** bump actions/setup-node from 4.0.0 to 4.0.1 ([#3535](https://github.com/aws-powertools/powertools-lambda-python/issues/3535)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.2 to 3.0.3 ([#3536](https://github.com/aws-powertools/powertools-lambda-python/issues/3536)) +* **deps:** bump actions/dependency-review-action from 3.1.4 to 3.1.5 ([#3592](https://github.com/aws-powertools/powertools-lambda-python/issues/3592)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3544](https://github.com/aws-powertools/powertools-lambda-python/issues/3544)) +* **deps:** bump fastjsonschema from 2.19.0 to 2.19.1 ([#3567](https://github.com/aws-powertools/powertools-lambda-python/issues/3567)) +* **deps-dev:** bump ruff from 0.1.8 to 0.1.9 ([#3550](https://github.com/aws-powertools/powertools-lambda-python/issues/3550)) +* **deps-dev:** bump aws-cdk from 2.115.0 to 2.116.1 ([#3553](https://github.com/aws-powertools/powertools-lambda-python/issues/3553)) +* **deps-dev:** bump aws-cdk from 2.117.0 to 2.118.0 ([#3589](https://github.com/aws-powertools/powertools-lambda-python/issues/3589)) +* **deps-dev:** bump cfn-lint from 0.83.6 to 0.83.7 ([#3554](https://github.com/aws-powertools/powertools-lambda-python/issues/3554)) +* **deps-dev:** bump ruff from 0.1.9 to 0.1.10 ([#3583](https://github.com/aws-powertools/powertools-lambda-python/issues/3583)) +* **deps-dev:** bump pytest from 7.4.3 to 7.4.4 ([#3576](https://github.com/aws-powertools/powertools-lambda-python/issues/3576)) +* **deps-dev:** bump aws-cdk from 2.116.1 to 2.117.0 ([#3565](https://github.com/aws-powertools/powertools-lambda-python/issues/3565)) +* **deps-dev:** bump ruff from 0.1.10 to 0.1.11 ([#3588](https://github.com/aws-powertools/powertools-lambda-python/issues/3588)) + + +<a name="v2.30.2"></a> +## [v2.30.2] - 2023-12-18 +## Bug Fixes + +* **event-handler:** fix operation tags schema generation ([#3528](https://github.com/aws-powertools/powertools-lambda-python/issues/3528)) +* **event-handler:** set default OpenAPI version to 3.0.0 ([#3527](https://github.com/aws-powertools/powertools-lambda-python/issues/3527)) +* **event-handler:** upgrade Swagger UI to fix regressions ([#3526](https://github.com/aws-powertools/powertools-lambda-python/issues/3526)) + +## Maintenance + +* version bump +* **deps-dev:** bump cfn-lint from 0.83.5 to 0.83.6 ([#3521](https://github.com/aws-powertools/powertools-lambda-python/issues/3521)) + + +<a name="v2.30.1"></a> +## [v2.30.1] - 2023-12-15 +## Bug Fixes + +* **event_handler:** allow responses and metadata when using Router ([#3514](https://github.com/aws-powertools/powertools-lambda-python/issues/3514)) + +## Maintenance + +* version bump +* **deps-dev:** bump aws-cdk from 2.114.1 to 2.115.0 ([#3508](https://github.com/aws-powertools/powertools-lambda-python/issues/3508)) +* **deps-dev:** bump the boto-typing group with 11 updates ([#3509](https://github.com/aws-powertools/powertools-lambda-python/issues/3509)) +* **deps-dev:** bump sentry-sdk from 1.39.0 to 1.39.1 ([#3512](https://github.com/aws-powertools/powertools-lambda-python/issues/3512)) + + +<a name="v2.30.0"></a> +## [v2.30.0] - 2023-12-14 +## Bug Fixes + +* **docs:** make the Lambda Layer version consistent ([#3498](https://github.com/aws-powertools/powertools-lambda-python/issues/3498)) + +## Documentation + +* **customer-reference:** add Transformity as a customer reference ([#3497](https://github.com/aws-powertools/powertools-lambda-python/issues/3497)) + +## Features + +* **general:** add support for Python 3.12 ([#3304](https://github.com/aws-powertools/powertools-lambda-python/issues/3304)) + +## Maintenance + +* version bump +* **deps:** bump squidfunk/mkdocs-material from `876b39c` to `9af3b7e` in /docs ([#3486](https://github.com/aws-powertools/powertools-lambda-python/issues/3486)) +* **deps-dev:** bump sentry-sdk from 1.38.0 to 1.39.0 ([#3495](https://github.com/aws-powertools/powertools-lambda-python/issues/3495)) +* **deps-dev:** bump cfn-lint from 0.83.4 to 0.83.5 ([#3487](https://github.com/aws-powertools/powertools-lambda-python/issues/3487)) +* **deps-dev:** bump ruff from 0.1.7 to 0.1.8 ([#3501](https://github.com/aws-powertools/powertools-lambda-python/issues/3501)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3500](https://github.com/aws-powertools/powertools-lambda-python/issues/3500)) +* **tests:** temporarily disable E2E parallelism ([#3484](https://github.com/aws-powertools/powertools-lambda-python/issues/3484)) + + +<a name="v2.29.1"></a> +## [v2.29.1] - 2023-12-11 +## Bug Fixes + +* **logger:** log non-ascii characters as is when JSON stringifying ([#3475](https://github.com/aws-powertools/powertools-lambda-python/issues/3475)) + +## Maintenance + +* version bump +* **deps:** bump squidfunk/mkdocs-material from `8c72011` to `20241c6` in /docs ([#3470](https://github.com/aws-powertools/powertools-lambda-python/issues/3470)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3476](https://github.com/aws-powertools/powertools-lambda-python/issues/3476)) +* **deps:** bump actions/setup-python from 4.8.0 to 5.0.0 ([#3465](https://github.com/aws-powertools/powertools-lambda-python/issues/3465)) +* **deps:** bump squidfunk/mkdocs-material from `20241c6` to `876b39c` in /docs ([#3477](https://github.com/aws-powertools/powertools-lambda-python/issues/3477)) +* **deps:** bump datadog-lambda from 5.84.0 to 5.85.0 ([#3466](https://github.com/aws-powertools/powertools-lambda-python/issues/3466)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3467](https://github.com/aws-powertools/powertools-lambda-python/issues/3467)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3469](https://github.com/aws-powertools/powertools-lambda-python/issues/3469)) +* **deps-dev:** bump aws-cdk from 2.113.0 to 2.114.1 ([#3464](https://github.com/aws-powertools/powertools-lambda-python/issues/3464)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3478](https://github.com/aws-powertools/powertools-lambda-python/issues/3478)) + + +<a name="v2.29.0"></a> +## [v2.29.0] - 2023-12-06 +## Bug Fixes + +* **event_handler:** serialize pydantic/dataclasses in exception handler ([#3455](https://github.com/aws-powertools/powertools-lambda-python/issues/3455)) +* **metrics:** lambda_handler typing, and **kwargs preservation all middlewares ([#3460](https://github.com/aws-powertools/powertools-lambda-python/issues/3460)) + +## Features + +* **event_sources:** add get_context() to standardize API Gateway Lambda Authorizer context in v1 and v2 ([#3454](https://github.com/aws-powertools/powertools-lambda-python/issues/3454)) + +## Maintenance + +* version bump +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3441](https://github.com/aws-powertools/powertools-lambda-python/issues/3441)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.1 to 3.0.2 ([#3449](https://github.com/aws-powertools/powertools-lambda-python/issues/3449)) +* **deps:** bump datadog-lambda from 5.83.0 to 5.84.0 ([#3438](https://github.com/aws-powertools/powertools-lambda-python/issues/3438)) +* **deps:** bump cryptography from 41.0.4 to 41.0.6 ([#3431](https://github.com/aws-powertools/powertools-lambda-python/issues/3431)) +* **deps:** bump squidfunk/mkdocs-material from `fc42bac` to `8c72011` in /docs ([#3416](https://github.com/aws-powertools/powertools-lambda-python/issues/3416)) +* **deps:** bump actions/dependency-review-action from 3.1.3 to 3.1.4 ([#3426](https://github.com/aws-powertools/powertools-lambda-python/issues/3426)) +* **deps:** bump actions/setup-python from 4.7.1 to 4.8.0 ([#3456](https://github.com/aws-powertools/powertools-lambda-python/issues/3456)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.10 to 1.8.11 ([#3433](https://github.com/aws-powertools/powertools-lambda-python/issues/3433)) +* **deps-dev:** bump cfn-lint from 0.83.3 to 0.83.4 ([#3450](https://github.com/aws-powertools/powertools-lambda-python/issues/3450)) +* **deps-dev:** bump ruff from 0.1.6 to 0.1.7 ([#3458](https://github.com/aws-powertools/powertools-lambda-python/issues/3458)) +* **deps-dev:** bump sentry-sdk from 1.36.0 to 1.38.0 ([#3435](https://github.com/aws-powertools/powertools-lambda-python/issues/3435)) +* **deps-dev:** bump aws-cdk-lib from 2.110.1 to 2.111.0 ([#3428](https://github.com/aws-powertools/powertools-lambda-python/issues/3428)) +* **deps-dev:** bump aws-cdk from 2.112.0 to 2.113.0 ([#3448](https://github.com/aws-powertools/powertools-lambda-python/issues/3448)) +* **deps-dev:** bump aws-cdk from 2.110.1 to 2.111.0 ([#3418](https://github.com/aws-powertools/powertools-lambda-python/issues/3418)) +* **deps-dev:** bump the boto-typing group with 11 updates ([#3427](https://github.com/aws-powertools/powertools-lambda-python/issues/3427)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3457](https://github.com/aws-powertools/powertools-lambda-python/issues/3457)) +* **deps-dev:** bump aws-cdk from 2.111.0 to 2.112.0 ([#3440](https://github.com/aws-powertools/powertools-lambda-python/issues/3440)) +* **layers:** Update log retention to 10 years ([#3424](https://github.com/aws-powertools/powertools-lambda-python/issues/3424)) + + +<a name="v2.28.1"></a> +## [v2.28.1] - 2023-11-28 +## Bug Fixes + +* **event_handler:** fix compress handling ([#3420](https://github.com/aws-powertools/powertools-lambda-python/issues/3420)) + +## Maintenance + +* version bump + + +<a name="v2.28.0"></a> +## [v2.28.0] - 2023-11-23 +## Bug Fixes + +* **event_handler:** hide error details by default ([#3406](https://github.com/aws-powertools/powertools-lambda-python/issues/3406)) +* **event_handler:** fix format for OpenAPI path templating ([#3399](https://github.com/aws-powertools/powertools-lambda-python/issues/3399)) +* **event_handler:** lazy load Pydantic to improve cold start ([#3397](https://github.com/aws-powertools/powertools-lambda-python/issues/3397)) +* **event_handler:** allow fine grained Response with data validation ([#3394](https://github.com/aws-powertools/powertools-lambda-python/issues/3394)) +* **event_handler:** apply serialization as the last operation for middlewares ([#3392](https://github.com/aws-powertools/powertools-lambda-python/issues/3392)) + +## Documentation + +* **event_handlers:** new data validation and OpenAPI feature ([#3386](https://github.com/aws-powertools/powertools-lambda-python/issues/3386)) + +## Features + +* **event_handler:** allow customers to catch request validation errors ([#3396](https://github.com/aws-powertools/powertools-lambda-python/issues/3396)) + +## Maintenance + +* version bump +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3389](https://github.com/aws-powertools/powertools-lambda-python/issues/3389)) +* **deps:** bump datadog-lambda from 4.82.0 to 5.83.0 ([#3401](https://github.com/aws-powertools/powertools-lambda-python/issues/3401)) +* **deps-dev:** bump aws-cdk-lib from 2.110.0 to 2.110.1 ([#3402](https://github.com/aws-powertools/powertools-lambda-python/issues/3402)) +* **deps-dev:** bump pytest-xdist from 3.4.0 to 3.5.0 ([#3387](https://github.com/aws-powertools/powertools-lambda-python/issues/3387)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3400](https://github.com/aws-powertools/powertools-lambda-python/issues/3400)) +* **deps-dev:** bump sentry-sdk from 1.35.0 to 1.36.0 ([#3388](https://github.com/aws-powertools/powertools-lambda-python/issues/3388)) +* **deps-dev:** bump aws-cdk from 2.110.0 to 2.110.1 ([#3403](https://github.com/aws-powertools/powertools-lambda-python/issues/3403)) + + +<a name="v2.27.1"></a> +## [v2.27.1] - 2023-11-21 +## Bug Fixes + +* **logger:** allow custom JMESPath functions to extract correlation ID ([#3382](https://github.com/aws-powertools/powertools-lambda-python/issues/3382)) + +## Documentation + +* **event_handlers:** note that CORS and */* binary mime type don't work in API Gateway ([#3383](https://github.com/aws-powertools/powertools-lambda-python/issues/3383)) +* **logger:** improve ALC messaging in the PT context ([#3359](https://github.com/aws-powertools/powertools-lambda-python/issues/3359)) +* **logger:** Fix ALC link ([#3352](https://github.com/aws-powertools/powertools-lambda-python/issues/3352)) + +## Features + +* **logger:** implement addFilter/removeFilter to address static typing errors ([#3380](https://github.com/aws-powertools/powertools-lambda-python/issues/3380)) + +## Maintenance + +* version bump +* **ci:** lint and type checking removal in Pydantic v2 quality check ([#3360](https://github.com/aws-powertools/powertools-lambda-python/issues/3360)) +* **deps:** bump actions/github-script from 7.0.0 to 7.0.1 ([#3377](https://github.com/aws-powertools/powertools-lambda-python/issues/3377)) +* **deps:** bump squidfunk/mkdocs-material from `2c57e4d` to `fc42bac` in /docs ([#3375](https://github.com/aws-powertools/powertools-lambda-python/issues/3375)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3353](https://github.com/aws-powertools/powertools-lambda-python/issues/3353)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3374](https://github.com/aws-powertools/powertools-lambda-python/issues/3374)) +* **deps:** bump squidfunk/mkdocs-material from `f486dc9` to `2c57e4d` in /docs ([#3366](https://github.com/aws-powertools/powertools-lambda-python/issues/3366)) +* **deps-dev:** bump cfn-lint from 0.83.2 to 0.83.3 ([#3363](https://github.com/aws-powertools/powertools-lambda-python/issues/3363)) +* **deps-dev:** bump the boto-typing group with 11 updates ([#3362](https://github.com/aws-powertools/powertools-lambda-python/issues/3362)) +* **deps-dev:** bump aws-cdk-lib from 2.108.1 to 2.110.0 ([#3365](https://github.com/aws-powertools/powertools-lambda-python/issues/3365)) +* **deps-dev:** bump aws-cdk from 2.108.1 to 2.109.0 ([#3354](https://github.com/aws-powertools/powertools-lambda-python/issues/3354)) +* **deps-dev:** bump aws-cdk from 2.109.0 to 2.110.0 ([#3361](https://github.com/aws-powertools/powertools-lambda-python/issues/3361)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3376](https://github.com/aws-powertools/powertools-lambda-python/issues/3376)) +* **deps-dev:** bump ruff from 0.1.5 to 0.1.6 ([#3364](https://github.com/aws-powertools/powertools-lambda-python/issues/3364)) + + +<a name="v2.27.0"></a> +## [v2.27.0] - 2023-11-16 +## Features + +* **logger:** Adding support to new env variables ([#3348](https://github.com/aws-powertools/powertools-lambda-python/issues/3348)) + +## Maintenance + +* version bump +* **deps:** bump actions/github-script from 6.4.1 to 7.0.0 ([#3330](https://github.com/aws-powertools/powertools-lambda-python/issues/3330)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3340](https://github.com/aws-powertools/powertools-lambda-python/issues/3340)) +* **deps:** bump fastjsonschema from 2.18.1 to 2.19.0 ([#3337](https://github.com/aws-powertools/powertools-lambda-python/issues/3337)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3345](https://github.com/aws-powertools/powertools-lambda-python/issues/3345)) +* **deps:** bump actions/dependency-review-action from 3.1.2 to 3.1.3 ([#3331](https://github.com/aws-powertools/powertools-lambda-python/issues/3331)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3329](https://github.com/aws-powertools/powertools-lambda-python/issues/3329)) +* **deps:** bump datadog-lambda from 4.81.0 to 4.82.0 ([#3338](https://github.com/aws-powertools/powertools-lambda-python/issues/3338)) +* **deps-dev:** bump cfn-lint from 0.83.1 to 0.83.2 ([#3335](https://github.com/aws-powertools/powertools-lambda-python/issues/3335)) +* **deps-dev:** bump aws-cdk from 2.108.0 to 2.108.1 ([#3344](https://github.com/aws-powertools/powertools-lambda-python/issues/3344)) +* **deps-dev:** bump sentry-sdk from 1.34.0 to 1.35.0 ([#3334](https://github.com/aws-powertools/powertools-lambda-python/issues/3334)) +* **deps-dev:** bump pytest-xdist from 3.3.1 to 3.4.0 ([#3332](https://github.com/aws-powertools/powertools-lambda-python/issues/3332)) +* **deps-dev:** bump aws-cdk-lib from 2.107.0 to 2.108.1 ([#3343](https://github.com/aws-powertools/powertools-lambda-python/issues/3343)) +* **deps-dev:** bump aws-cdk from 2.106.0 to 2.106.1 ([#3328](https://github.com/aws-powertools/powertools-lambda-python/issues/3328)) +* **deps-dev:** bump aws-cdk-lib from 2.105.0 to 2.106.0 ([#3319](https://github.com/aws-powertools/powertools-lambda-python/issues/3319)) +* **deps-dev:** bump aws-cdk from 2.105.0 to 2.106.0 ([#3320](https://github.com/aws-powertools/powertools-lambda-python/issues/3320)) +* **deps-dev:** bump aws-cdk from 2.106.1 to 2.108.0 ([#3341](https://github.com/aws-powertools/powertools-lambda-python/issues/3341)) +* **deps-dev:** bump aws-cdk-lib from 2.106.0 to 2.107.0 ([#3333](https://github.com/aws-powertools/powertools-lambda-python/issues/3333)) + + +<a name="v2.26.1"></a> +## [v2.26.1] - 2023-11-10 +## Bug Fixes + +* **event-handler:** enable path parameters on Bedrock handler ([#3312](https://github.com/aws-powertools/powertools-lambda-python/issues/3312)) +* **event_handler:** Router prefix mismatch regression after Middleware feat ([#3302](https://github.com/aws-powertools/powertools-lambda-python/issues/3302)) +* **event_source:** kinesis subsequenceNumber str type to int ([#3275](https://github.com/aws-powertools/powertools-lambda-python/issues/3275)) +* **parameters:** Respect POWERTOOLS_PARAMETERS_SSM_DECRYPT environment variable when getting multiple ssm parameters. ([#3241](https://github.com/aws-powertools/powertools-lambda-python/issues/3241)) + +## Documentation + +* **customer-reference:** add Vertex Pharmaceuticals as a customer reference ([#3210](https://github.com/aws-powertools/powertools-lambda-python/issues/3210)) +* **event-handler:** fixed SchemaValidationMiddleware link ([#3247](https://github.com/aws-powertools/powertools-lambda-python/issues/3247)) + +## Features + +* **data_classes:** add support for Bedrock Agents event ([#3262](https://github.com/aws-powertools/powertools-lambda-python/issues/3262)) +* **event_handler:** add Bedrock Agent event handler ([#3285](https://github.com/aws-powertools/powertools-lambda-python/issues/3285)) +* **event_handler:** add ability to expose a Swagger UI ([#3254](https://github.com/aws-powertools/powertools-lambda-python/issues/3254)) +* **event_handler:** generate OpenAPI specifications and validate input/output ([#3109](https://github.com/aws-powertools/powertools-lambda-python/issues/3109)) +* **parser:** add BedrockEventModel parser and envelope ([#3286](https://github.com/aws-powertools/powertools-lambda-python/issues/3286)) + +## Maintenance + +* version bump +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.1.5 to 3.0.0 ([#3289](https://github.com/aws-powertools/powertools-lambda-python/issues/3289)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3287](https://github.com/aws-powertools/powertools-lambda-python/issues/3287)) +* **deps:** bump actions/checkout from 4.1.0 to 4.1.1 ([#3220](https://github.com/aws-powertools/powertools-lambda-python/issues/3220)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3282](https://github.com/aws-powertools/powertools-lambda-python/issues/3282)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.1.4 to 2.1.5 ([#3281](https://github.com/aws-powertools/powertools-lambda-python/issues/3281)) +* **deps:** bump release-drafter/release-drafter from 5.24.0 to 5.25.0 ([#3219](https://github.com/aws-powertools/powertools-lambda-python/issues/3219)) +* **deps:** bump squidfunk/mkdocs-material from `cb38dc2` to `df9409b` in /docs ([#3216](https://github.com/aws-powertools/powertools-lambda-python/issues/3216)) +* **deps:** bump urllib3 from 1.26.17 to 1.26.18 ([#3222](https://github.com/aws-powertools/powertools-lambda-python/issues/3222)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3298](https://github.com/aws-powertools/powertools-lambda-python/issues/3298)) +* **deps:** bump squidfunk/mkdocs-material from `772e14e` to `f486dc9` in /docs ([#3299](https://github.com/aws-powertools/powertools-lambda-python/issues/3299)) +* **deps:** bump datadog-lambda from 4.80.0 to 4.81.0 ([#3228](https://github.com/aws-powertools/powertools-lambda-python/issues/3228)) +* **deps:** bump actions/setup-node from 3.8.1 to 4.0.0 ([#3244](https://github.com/aws-powertools/powertools-lambda-python/issues/3244)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 3.0.0 to 3.0.1 ([#3300](https://github.com/aws-powertools/powertools-lambda-python/issues/3300)) +* **deps:** bump actions/dependency-review-action from 3.1.0 to 3.1.1 ([#3301](https://github.com/aws-powertools/powertools-lambda-python/issues/3301)) +* **deps:** bump squidfunk/mkdocs-material from `df9409b` to `772e14e` in /docs ([#3265](https://github.com/aws-powertools/powertools-lambda-python/issues/3265)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3305](https://github.com/aws-powertools/powertools-lambda-python/issues/3305)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#3248](https://github.com/aws-powertools/powertools-lambda-python/issues/3248)) +* **deps:** bump actions/dependency-review-action from 3.1.1 to 3.1.2 ([#3308](https://github.com/aws-powertools/powertools-lambda-python/issues/3308)) +* **deps:** bump ossf/scorecard-action from 2.3.0 to 2.3.1 ([#3245](https://github.com/aws-powertools/powertools-lambda-python/issues/3245)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3310](https://github.com/aws-powertools/powertools-lambda-python/issues/3310)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3215](https://github.com/aws-powertools/powertools-lambda-python/issues/3215)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3313](https://github.com/aws-powertools/powertools-lambda-python/issues/3313)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3278](https://github.com/aws-powertools/powertools-lambda-python/issues/3278)) +* **deps-dev:** bump pytest from 7.4.2 to 7.4.3 ([#3249](https://github.com/aws-powertools/powertools-lambda-python/issues/3249)) +* **deps-dev:** bump ruff from 0.1.1 to 0.1.2 ([#3250](https://github.com/aws-powertools/powertools-lambda-python/issues/3250)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3242](https://github.com/aws-powertools/powertools-lambda-python/issues/3242)) +* **deps-dev:** bump aws-cdk-lib from 2.102.0 to 2.103.0 ([#3258](https://github.com/aws-powertools/powertools-lambda-python/issues/3258)) +* **deps-dev:** bump cfn-lint from 0.82.2 to 0.83.0 ([#3243](https://github.com/aws-powertools/powertools-lambda-python/issues/3243)) +* **deps-dev:** bump ruff from 0.1.2 to 0.1.3 ([#3257](https://github.com/aws-powertools/powertools-lambda-python/issues/3257)) +* **deps-dev:** bump aws-cdk from 2.102.0 to 2.103.0 ([#3259](https://github.com/aws-powertools/powertools-lambda-python/issues/3259)) +* **deps-dev:** bump ruff from 0.1.0 to 0.1.1 ([#3235](https://github.com/aws-powertools/powertools-lambda-python/issues/3235)) +* **deps-dev:** bump aws-cdk-lib from 2.103.0 to 2.103.1 ([#3263](https://github.com/aws-powertools/powertools-lambda-python/issues/3263)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3231](https://github.com/aws-powertools/powertools-lambda-python/issues/3231)) +* **deps-dev:** bump aws-cdk from 2.101.1 to 2.102.0 ([#3232](https://github.com/aws-powertools/powertools-lambda-python/issues/3232)) +* **deps-dev:** bump aws-cdk from 2.103.0 to 2.103.1 ([#3264](https://github.com/aws-powertools/powertools-lambda-python/issues/3264)) +* **deps-dev:** bump cfn-lint from 0.82.0 to 0.82.2 ([#3229](https://github.com/aws-powertools/powertools-lambda-python/issues/3229)) +* **deps-dev:** bump cfn-lint from 0.83.0 to 0.83.1 ([#3274](https://github.com/aws-powertools/powertools-lambda-python/issues/3274)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3273](https://github.com/aws-powertools/powertools-lambda-python/issues/3273)) +* **deps-dev:** bump cfn-lint from 0.81.0 to 0.82.0 ([#3224](https://github.com/aws-powertools/powertools-lambda-python/issues/3224)) +* **deps-dev:** bump aws-cdk from 2.101.0 to 2.101.1 ([#3223](https://github.com/aws-powertools/powertools-lambda-python/issues/3223)) +* **deps-dev:** bump sentry-sdk from 1.32.0 to 1.33.1 ([#3277](https://github.com/aws-powertools/powertools-lambda-python/issues/3277)) +* **deps-dev:** bump urllib3 from 1.26.17 to 1.26.18 in /layer ([#3221](https://github.com/aws-powertools/powertools-lambda-python/issues/3221)) +* **deps-dev:** bump aws-cdk from 2.103.1 to 2.104.0 ([#3288](https://github.com/aws-powertools/powertools-lambda-python/issues/3288)) +* **deps-dev:** bump sentry-sdk from 1.33.1 to 1.34.0 ([#3290](https://github.com/aws-powertools/powertools-lambda-python/issues/3290)) +* **deps-dev:** bump aws-cdk-lib from 2.103.1 to 2.104.0 ([#3291](https://github.com/aws-powertools/powertools-lambda-python/issues/3291)) +* **deps-dev:** bump aws-cdk-lib from 2.100.0 to 2.101.1 ([#3217](https://github.com/aws-powertools/powertools-lambda-python/issues/3217)) +* **deps-dev:** bump aws-cdk from 2.100.0 to 2.101.0 ([#3214](https://github.com/aws-powertools/powertools-lambda-python/issues/3214)) +* **deps-dev:** bump aws-cdk from 2.104.0 to 2.105.0 ([#3307](https://github.com/aws-powertools/powertools-lambda-python/issues/3307)) +* **deps-dev:** bump ruff from 0.1.3 to 0.1.4 ([#3297](https://github.com/aws-powertools/powertools-lambda-python/issues/3297)) +* **deps-dev:** bump aws-cdk-lib from 2.104.0 to 2.105.0 ([#3309](https://github.com/aws-powertools/powertools-lambda-python/issues/3309)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3211](https://github.com/aws-powertools/powertools-lambda-python/issues/3211)) +* **deps-dev:** bump the boto-typing group with 3 updates ([#3314](https://github.com/aws-powertools/powertools-lambda-python/issues/3314)) +* **deps-dev:** bump ruff from 0.1.4 to 0.1.5 ([#3315](https://github.com/aws-powertools/powertools-lambda-python/issues/3315)) +* **deps-dev:** bump ruff from 0.0.292 to 0.1.0 ([#3213](https://github.com/aws-powertools/powertools-lambda-python/issues/3213)) + + +<a name="v2.26.0"></a> +## [v2.26.0] - 2023-10-13 +## Bug Fixes + +* **logger:** force Logger to use local timezone when UTC flag is not set ([#3168](https://github.com/aws-powertools/powertools-lambda-python/issues/3168)) +* **parameter:** improve AppConfig cached configuration retrieval ([#3195](https://github.com/aws-powertools/powertools-lambda-python/issues/3195)) + +## Code Refactoring + +* **data-masking:** disable e2e tests. ([#3204](https://github.com/aws-powertools/powertools-lambda-python/issues/3204)) +* **data_masking:** move Data Masking utility to a private folder ([#3202](https://github.com/aws-powertools/powertools-lambda-python/issues/3202)) + +## Documentation + +* **contributing:** initial structure for revamped contributing guide ([#3133](https://github.com/aws-powertools/powertools-lambda-python/issues/3133)) +* **event_handler:** add information about case-insensitive header lookup function ([#3183](https://github.com/aws-powertools/powertools-lambda-python/issues/3183)) + +## Features + +* **data_masking:** add new sensitive data masking utility ([#2197](https://github.com/aws-powertools/powertools-lambda-python/issues/2197)) +* **event_handler:** add support to VPC Lattice payload v2 ([#3153](https://github.com/aws-powertools/powertools-lambda-python/issues/3153)) +* **layers:** add arm64 support in more regions ([#3151](https://github.com/aws-powertools/powertools-lambda-python/issues/3151)) +* **logger:** new stack_trace field with rich exception details ([#3147](https://github.com/aws-powertools/powertools-lambda-python/issues/3147)) +* **parser:** infer model from type hint ([#3181](https://github.com/aws-powertools/powertools-lambda-python/issues/3181)) + +## Maintenance + +* version bump +* **deps:** bump squidfunk/mkdocs-material from `cbfecae` to `a4cfa88` in /docs ([#3175](https://github.com/aws-powertools/powertools-lambda-python/issues/3175)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3174](https://github.com/aws-powertools/powertools-lambda-python/issues/3174)) +* **deps:** bump squidfunk/mkdocs-material from `b41ba6d` to `06673a1` in /docs ([#3124](https://github.com/aws-powertools/powertools-lambda-python/issues/3124)) +* **deps:** bump ossf/scorecard-action from 2.2.0 to 2.3.0 ([#3178](https://github.com/aws-powertools/powertools-lambda-python/issues/3178)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3198](https://github.com/aws-powertools/powertools-lambda-python/issues/3198)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#3177](https://github.com/aws-powertools/powertools-lambda-python/issues/3177)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3127](https://github.com/aws-powertools/powertools-lambda-python/issues/3127)) +* **deps:** bump urllib3 from 1.26.16 to 1.26.17 ([#3162](https://github.com/aws-powertools/powertools-lambda-python/issues/3162)) +* **deps:** bump aws-xray-sdk from 2.12.0 to 2.12.1 ([#3197](https://github.com/aws-powertools/powertools-lambda-python/issues/3197)) +* **deps:** bump fastjsonschema from 2.18.0 to 2.18.1 ([#3159](https://github.com/aws-powertools/powertools-lambda-python/issues/3159)) +* **deps:** bump actions/setup-python from 4.7.0 to 4.7.1 ([#3158](https://github.com/aws-powertools/powertools-lambda-python/issues/3158)) +* **deps:** bump actions/checkout from 4.0.0 to 4.1.0 ([#3128](https://github.com/aws-powertools/powertools-lambda-python/issues/3128)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3156](https://github.com/aws-powertools/powertools-lambda-python/issues/3156)) +* **deps:** bump squidfunk/mkdocs-material from `e5f28aa` to `cbfecae` in /docs ([#3157](https://github.com/aws-powertools/powertools-lambda-python/issues/3157)) +* **deps:** bump squidfunk/mkdocs-material from `06673a1` to `e5f28aa` in /docs ([#3134](https://github.com/aws-powertools/powertools-lambda-python/issues/3134)) +* **deps:** bump squidfunk/mkdocs-material from `a4cfa88` to `cb38dc2` in /docs ([#3189](https://github.com/aws-powertools/powertools-lambda-python/issues/3189)) +* **deps:** bump pydantic from 1.10.12 to 1.10.13 ([#3144](https://github.com/aws-powertools/powertools-lambda-python/issues/3144)) +* **deps:** bump gitpython from 3.1.35 to 3.1.37 in /docs ([#3188](https://github.com/aws-powertools/powertools-lambda-python/issues/3188)) +* **deps-dev:** bump types-requests from 2.31.0.5 to 2.31.0.6 ([#3145](https://github.com/aws-powertools/powertools-lambda-python/issues/3145)) +* **deps-dev:** bump aws-cdk from 2.98.0 to 2.99.0 ([#3148](https://github.com/aws-powertools/powertools-lambda-python/issues/3148)) +* **deps-dev:** bump the boto-typing group with 2 updates ([#3143](https://github.com/aws-powertools/powertools-lambda-python/issues/3143)) +* **deps-dev:** bump aws-cdk from 2.99.1 to 2.100.0 ([#3185](https://github.com/aws-powertools/powertools-lambda-python/issues/3185)) +* **deps-dev:** bump aws-cdk from 2.97.0 to 2.98.0 ([#3139](https://github.com/aws-powertools/powertools-lambda-python/issues/3139)) +* **deps-dev:** bump aws-cdk from 2.96.2 to 2.97.0 ([#3129](https://github.com/aws-powertools/powertools-lambda-python/issues/3129)) +* **deps-dev:** bump types-requests from 2.31.0.3 to 2.31.0.5 ([#3136](https://github.com/aws-powertools/powertools-lambda-python/issues/3136)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3135](https://github.com/aws-powertools/powertools-lambda-python/issues/3135)) +* **deps-dev:** bump ruff from 0.0.291 to 0.0.292 ([#3161](https://github.com/aws-powertools/powertools-lambda-python/issues/3161)) +* **deps-dev:** bump ruff from 0.0.290 to 0.0.291 ([#3126](https://github.com/aws-powertools/powertools-lambda-python/issues/3126)) +* **deps-dev:** bump aws-cdk from 2.99.0 to 2.99.1 ([#3155](https://github.com/aws-powertools/powertools-lambda-python/issues/3155)) +* **deps-dev:** bump sentry-sdk from 1.31.0 to 1.32.0 ([#3192](https://github.com/aws-powertools/powertools-lambda-python/issues/3192)) +* **deps-dev:** bump urllib3 from 1.26.16 to 1.26.17 in /layer ([#3163](https://github.com/aws-powertools/powertools-lambda-python/issues/3163)) +* **deps-dev:** bump cfn-lint from 0.80.3 to 0.80.4 ([#3166](https://github.com/aws-powertools/powertools-lambda-python/issues/3166)) +* **deps-dev:** bump cfn-lint from 0.80.2 to 0.80.3 ([#3125](https://github.com/aws-powertools/powertools-lambda-python/issues/3125)) +* **deps-dev:** bump cfn-lint from 0.80.4 to 0.81.0 ([#3179](https://github.com/aws-powertools/powertools-lambda-python/issues/3179)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3196](https://github.com/aws-powertools/powertools-lambda-python/issues/3196)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3170](https://github.com/aws-powertools/powertools-lambda-python/issues/3170)) + + +<a name="v2.25.1"></a> +## [v2.25.1] - 2023-09-22 +## Bug Fixes + +* **logger:** add explicit None return type annotations ([#3113](https://github.com/aws-powertools/powertools-lambda-python/issues/3113)) +* **metrics:** support additional arguments in functions wrapped with log_metrics decorator ([#3120](https://github.com/aws-powertools/powertools-lambda-python/issues/3120)) + +## Maintenance + +* version bump +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3108](https://github.com/aws-powertools/powertools-lambda-python/issues/3108)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3115](https://github.com/aws-powertools/powertools-lambda-python/issues/3115)) +* **deps:** bump squidfunk/mkdocs-material from `4ff781e` to `b41ba6d` in /docs ([#3117](https://github.com/aws-powertools/powertools-lambda-python/issues/3117)) +* **deps:** bump squidfunk/mkdocs-material from `c4890ab` to `4ff781e` in /docs ([#3110](https://github.com/aws-powertools/powertools-lambda-python/issues/3110)) +* **deps-dev:** bump ruff from 0.0.289 to 0.0.290 ([#3105](https://github.com/aws-powertools/powertools-lambda-python/issues/3105)) +* **deps-dev:** bump aws-cdk from 2.96.1 to 2.96.2 ([#3102](https://github.com/aws-powertools/powertools-lambda-python/issues/3102)) +* **deps-dev:** bump the boto-typing group with 3 updates ([#3118](https://github.com/aws-powertools/powertools-lambda-python/issues/3118)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3101](https://github.com/aws-powertools/powertools-lambda-python/issues/3101)) +* **deps-dev:** bump cfn-lint from 0.79.11 to 0.80.2 ([#3107](https://github.com/aws-powertools/powertools-lambda-python/issues/3107)) +* **deps-dev:** bump types-requests from 2.31.0.2 to 2.31.0.3 ([#3114](https://github.com/aws-powertools/powertools-lambda-python/issues/3114)) + + +<a name="v2.25.0"></a> +## [v2.25.0] - 2023-09-15 +## Code Refactoring + +* **parameters:** BaseProvider._get to also support Dict ([#3090](https://github.com/aws-powertools/powertools-lambda-python/issues/3090)) + +## Documentation + +* **event_handler:** fix typing in micro function example ([#3098](https://github.com/aws-powertools/powertools-lambda-python/issues/3098)) +* **event_handler:** add micro function examples ([#3056](https://github.com/aws-powertools/powertools-lambda-python/issues/3056)) +* **we-made-this:** fix broken Twitch video embeds ([#3096](https://github.com/aws-powertools/powertools-lambda-python/issues/3096)) + +## Features + +* **event_source:** add Kinesis Firehose Data Transformation data class ([#3029](https://github.com/aws-powertools/powertools-lambda-python/issues/3029)) +* **event_sources:** add Secrets Manager secret rotation event ([#3061](https://github.com/aws-powertools/powertools-lambda-python/issues/3061)) + +## Maintenance + +* version bump +* **automation:** remove previous labels when PR is updated ([#3066](https://github.com/aws-powertools/powertools-lambda-python/issues/3066)) +* **deps:** bump actions/dependency-review-action from 3.0.8 to 3.1.0 ([#3071](https://github.com/aws-powertools/powertools-lambda-python/issues/3071)) +* **deps:** bump docker/setup-qemu-action from 2.2.0 to 3.0.0 ([#3081](https://github.com/aws-powertools/powertools-lambda-python/issues/3081)) +* **deps:** bump docker/setup-buildx-action from 2.10.0 to 3.0.0 ([#3083](https://github.com/aws-powertools/powertools-lambda-python/issues/3083)) +* **deps:** bump squidfunk/mkdocs-material from `dd1770c` to `c4890ab` in /docs ([#3078](https://github.com/aws-powertools/powertools-lambda-python/issues/3078)) +* **deps-dev:** bump cfn-lint from 0.79.9 to 0.79.10 ([#3077](https://github.com/aws-powertools/powertools-lambda-python/issues/3077)) +* **deps-dev:** bump hvac from 1.2.0 to 1.2.1 ([#3075](https://github.com/aws-powertools/powertools-lambda-python/issues/3075)) +* **deps-dev:** bump ruff from 0.0.288 to 0.0.289 ([#3080](https://github.com/aws-powertools/powertools-lambda-python/issues/3080)) +* **deps-dev:** bump ruff from 0.0.287 to 0.0.288 ([#3076](https://github.com/aws-powertools/powertools-lambda-python/issues/3076)) +* **deps-dev:** bump aws-cdk from 2.95.0 to 2.95.1 ([#3074](https://github.com/aws-powertools/powertools-lambda-python/issues/3074)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3085](https://github.com/aws-powertools/powertools-lambda-python/issues/3085)) +* **deps-dev:** bump aws-cdk from 2.95.1 to 2.96.0 ([#3087](https://github.com/aws-powertools/powertools-lambda-python/issues/3087)) +* **deps-dev:** bump sentry-sdk from 1.30.0 to 1.31.0 ([#3086](https://github.com/aws-powertools/powertools-lambda-python/issues/3086)) +* **deps-dev:** bump aws-cdk from 2.94.0 to 2.95.0 ([#3070](https://github.com/aws-powertools/powertools-lambda-python/issues/3070)) +* **deps-dev:** bump cfn-lint from 0.79.10 to 0.79.11 ([#3088](https://github.com/aws-powertools/powertools-lambda-python/issues/3088)) +* **deps-dev:** bump aws-cdk from 2.96.0 to 2.96.1 ([#3093](https://github.com/aws-powertools/powertools-lambda-python/issues/3093)) +* **typing:** move backwards compat types to shared types ([#3092](https://github.com/aws-powertools/powertools-lambda-python/issues/3092)) + + +<a name="v2.24.0"></a> +## [v2.24.0] - 2023-09-08 +## Bug Fixes + +* **event_handler:** expanding safe URI characters to include +$& ([#3026](https://github.com/aws-powertools/powertools-lambda-python/issues/3026)) +* **parser:** change ApproximateCreationDateTime field to datetime in DynamoDBStreamChangedRecordModel ([#3049](https://github.com/aws-powertools/powertools-lambda-python/issues/3049)) + +## Code Refactoring + +* **batch:** type response() method ([#3023](https://github.com/aws-powertools/powertools-lambda-python/issues/3023)) + +## Documentation + +* **event_handler:** demonstrate how to combine logger correlation ID and middleware ([#3064](https://github.com/aws-powertools/powertools-lambda-python/issues/3064)) +* **event_handler:** use correct correlation_id for logger in middleware example ([#3063](https://github.com/aws-powertools/powertools-lambda-python/issues/3063)) +* **idempotency:** use tab navigation, improves custom serializer example, and additional explanations ([#3067](https://github.com/aws-powertools/powertools-lambda-python/issues/3067)) + +## Features + +* **event_handler:** add Middleware support for REST Event Handler ([#2917](https://github.com/aws-powertools/powertools-lambda-python/issues/2917)) +* **idempotency:** add support to custom serialization/deserialization on idempotency decorator ([#2951](https://github.com/aws-powertools/powertools-lambda-python/issues/2951)) + +## Maintenance + +* version bump +* **deps:** bump squidfunk/mkdocs-material from `b1f7f94` to `f4764d1` in /docs ([#3031](https://github.com/aws-powertools/powertools-lambda-python/issues/3031)) +* **deps:** bump gitpython from 3.1.32 to 3.1.35 in /docs ([#3059](https://github.com/aws-powertools/powertools-lambda-python/issues/3059)) +* **deps:** bump squidfunk/mkdocs-material from `f4764d1` to `dd1770c` in /docs ([#3044](https://github.com/aws-powertools/powertools-lambda-python/issues/3044)) +* **deps:** bump actions/checkout from 3.6.0 to 4.0.0 ([#3041](https://github.com/aws-powertools/powertools-lambda-python/issues/3041)) +* **deps:** bump squidfunk/mkdocs-material from `97da15b` to `b1f7f94` in /docs ([#3021](https://github.com/aws-powertools/powertools-lambda-python/issues/3021)) +* **deps:** bump docker/setup-buildx-action from 2.9.1 to 2.10.0 ([#3022](https://github.com/aws-powertools/powertools-lambda-python/issues/3022)) +* **deps:** bump actions/upload-artifact from 3.1.2 to 3.1.3 ([#3053](https://github.com/aws-powertools/powertools-lambda-python/issues/3053)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 1 update ([#3052](https://github.com/aws-powertools/powertools-lambda-python/issues/3052)) +* **deps-dev:** bump mkdocs-material from 9.2.6 to 9.2.7 ([#3043](https://github.com/aws-powertools/powertools-lambda-python/issues/3043)) +* **deps-dev:** bump cfn-lint from 0.79.7 to 0.79.8 ([#3033](https://github.com/aws-powertools/powertools-lambda-python/issues/3033)) +* **deps-dev:** bump mkdocs-material from 9.2.5 to 9.2.6 ([#3032](https://github.com/aws-powertools/powertools-lambda-python/issues/3032)) +* **deps-dev:** bump ruff from 0.0.286 to 0.0.287 ([#3035](https://github.com/aws-powertools/powertools-lambda-python/issues/3035)) +* **deps-dev:** bump sentry-sdk from 1.29.2 to 1.30.0 ([#3028](https://github.com/aws-powertools/powertools-lambda-python/issues/3028)) +* **deps-dev:** bump the boto-typing group with 11 updates ([#3027](https://github.com/aws-powertools/powertools-lambda-python/issues/3027)) +* **deps-dev:** bump pytest from 7.4.1 to 7.4.2 ([#3057](https://github.com/aws-powertools/powertools-lambda-python/issues/3057)) +* **deps-dev:** bump hvac from 1.1.1 to 1.2.0 ([#3054](https://github.com/aws-powertools/powertools-lambda-python/issues/3054)) +* **deps-dev:** bump cfn-lint from 0.79.8 to 0.79.9 ([#3046](https://github.com/aws-powertools/powertools-lambda-python/issues/3046)) +* **deps-dev:** bump the boto-typing group with 1 update ([#3013](https://github.com/aws-powertools/powertools-lambda-python/issues/3013)) +* **deps-dev:** bump pytest from 7.4.0 to 7.4.1 ([#3042](https://github.com/aws-powertools/powertools-lambda-python/issues/3042)) +* **deps-dev:** bump ruff from 0.0.285 to 0.0.286 ([#3014](https://github.com/aws-powertools/powertools-lambda-python/issues/3014)) +* **deps-dev:** bump gitpython from 3.1.32 to 3.1.35 ([#3060](https://github.com/aws-powertools/powertools-lambda-python/issues/3060)) +* **deps-dev:** bump aws-cdk from 2.93.0 to 2.94.0 ([#3036](https://github.com/aws-powertools/powertools-lambda-python/issues/3036)) + + +<a name="v2.23.1"></a> +## [v2.23.1] - 2023-08-25 +## Bug Fixes + +* **ci:** revert aws credentials action ([#3010](https://github.com/aws-powertools/powertools-lambda-python/issues/3010)) +* **ci:** change SAR assume role options ([#3005](https://github.com/aws-powertools/powertools-lambda-python/issues/3005)) +* **event_handler:** make invalid chars a raw str to fix DeprecationWarning ([#2982](https://github.com/aws-powertools/powertools-lambda-python/issues/2982)) +* **metrics:** preserve default_tags when metric-specific tag is set in Datadog provider ([#2997](https://github.com/aws-powertools/powertools-lambda-python/issues/2997)) + +## Maintenance + +* version bump +* **deps:** bump squidfunk/mkdocs-material from `cd3a522` to `97da15b` in /docs ([#2987](https://github.com/aws-powertools/powertools-lambda-python/issues/2987)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#2978](https://github.com/aws-powertools/powertools-lambda-python/issues/2978)) +* **deps:** bump aws-actions/configure-aws-credentials from 2.2.0 to 3.0.0 ([#3000](https://github.com/aws-powertools/powertools-lambda-python/issues/3000)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#2983](https://github.com/aws-powertools/powertools-lambda-python/issues/2983)) +* **deps:** bump slsa-framework/slsa-github-generator from 1.8.0 to 1.9.0 ([#2992](https://github.com/aws-powertools/powertools-lambda-python/issues/2992)) +* **deps:** bump actions/checkout from 3.5.3 to 3.6.0 ([#2999](https://github.com/aws-powertools/powertools-lambda-python/issues/2999)) +* **deps-dev:** bump ruff from 0.0.284 to 0.0.285 ([#2977](https://github.com/aws-powertools/powertools-lambda-python/issues/2977)) +* **deps-dev:** bump aws-cdk from 2.92.0 to 2.93.0 ([#2993](https://github.com/aws-powertools/powertools-lambda-python/issues/2993)) +* **deps-dev:** bump mkdocs-material from 9.1.21 to 9.2.0 ([#2984](https://github.com/aws-powertools/powertools-lambda-python/issues/2984)) +* **deps-dev:** bump mkdocs-material from 9.2.0 to 9.2.3 ([#2988](https://github.com/aws-powertools/powertools-lambda-python/issues/2988)) + + +<a name="v2.23.0"></a> +## [v2.23.0] - 2023-08-18 +## Bug Fixes + +* **logger:** strip xray_trace_id when explicitly disabled ([#2852](https://github.com/aws-powertools/powertools-lambda-python/issues/2852)) +* **metrics:** proxy service and namespace attrs to provider ([#2910](https://github.com/aws-powertools/powertools-lambda-python/issues/2910)) +* **parser:** API Gateway V2 request context scope field should be optional ([#2961](https://github.com/aws-powertools/powertools-lambda-python/issues/2961)) + +## Code Refactoring + +* **e2e:** support fail fast in get_lambda_response ([#2912](https://github.com/aws-powertools/powertools-lambda-python/issues/2912)) +* **metrics:** move from protocol to ABC; split provider tests ([#2934](https://github.com/aws-powertools/powertools-lambda-python/issues/2934)) + +## Documentation + +* **batch:** new visuals and error handling section ([#2857](https://github.com/aws-powertools/powertools-lambda-python/issues/2857)) +* **batch:** explain record type discrepancy in failure and success handler ([#2868](https://github.com/aws-powertools/powertools-lambda-python/issues/2868)) +* **metrics:** update Datadog integration diagram ([#2954](https://github.com/aws-powertools/powertools-lambda-python/issues/2954)) +* **navigation:** remove nofollow attribute for internal links ([#2867](https://github.com/aws-powertools/powertools-lambda-python/issues/2867)) +* **navigation:** add nofollow attribute ([#2842](https://github.com/aws-powertools/powertools-lambda-python/issues/2842)) +* **roadmap:** update roadmap themes ([#2915](https://github.com/aws-powertools/powertools-lambda-python/issues/2915)) +* **roadmap:** add GovCloud and China region item ([#2960](https://github.com/aws-powertools/powertools-lambda-python/issues/2960)) +* **tutorial:** add support for Python 3.11 ([#2860](https://github.com/aws-powertools/powertools-lambda-python/issues/2860)) + +## Features + +* **event_handler:** allow stripping route prefixes using regexes ([#2521](https://github.com/aws-powertools/powertools-lambda-python/issues/2521)) +* **layers:** add new comercial region Israel(Tel Aviv) ([#2907](https://github.com/aws-powertools/powertools-lambda-python/issues/2907)) +* **metrics:** add Datadog observability provider ([#2906](https://github.com/aws-powertools/powertools-lambda-python/issues/2906)) +* **metrics:** support to bring your own metrics provider ([#2194](https://github.com/aws-powertools/powertools-lambda-python/issues/2194)) + +## Maintenance + +* version bump +* **ci:** enable protected branch auditing ([#2913](https://github.com/aws-powertools/powertools-lambda-python/issues/2913)) +* **ci:** group dependabot updates ([#2896](https://github.com/aws-powertools/powertools-lambda-python/issues/2896)) +* **deps:** bump github.com/aws/aws-sdk-go-v2 from 1.19.0 to 1.19.1 in /layer/scripts/layer-balancer ([#2877](https://github.com/aws-powertools/powertools-lambda-python/issues/2877)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.8 to 1.8.9 ([#2943](https://github.com/aws-powertools/powertools-lambda-python/issues/2943)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/service/lambda from 1.38.0 to 1.38.1 in /layer/scripts/layer-balancer ([#2876](https://github.com/aws-powertools/powertools-lambda-python/issues/2876)) +* **deps:** bump actions/dependency-review-action from 3.0.6 to 3.0.7 ([#2941](https://github.com/aws-powertools/powertools-lambda-python/issues/2941)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.18.29 to 1.18.30 in /layer/scripts/layer-balancer ([#2875](https://github.com/aws-powertools/powertools-lambda-python/issues/2875)) +* **deps:** bump actions/dependency-review-action from 3.0.7 to 3.0.8 ([#2963](https://github.com/aws-powertools/powertools-lambda-python/issues/2963)) +* **deps:** bump gitpython from 3.1.31 to 3.1.32 in /docs ([#2948](https://github.com/aws-powertools/powertools-lambda-python/issues/2948)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 2 updates ([#2904](https://github.com/aws-powertools/powertools-lambda-python/issues/2904)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#2933](https://github.com/aws-powertools/powertools-lambda-python/issues/2933)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.9 to 1.8.10 ([#2946](https://github.com/aws-powertools/powertools-lambda-python/issues/2946)) +* **deps:** bump actions/setup-node from 3.7.0 to 3.8.0 ([#2957](https://github.com/aws-powertools/powertools-lambda-python/issues/2957)) +* **deps:** bump slsa-framework/slsa-github-generator from 1.7.0 to 1.8.0 ([#2927](https://github.com/aws-powertools/powertools-lambda-python/issues/2927)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.18.28 to 1.18.29 in /layer/scripts/layer-balancer ([#2844](https://github.com/aws-powertools/powertools-lambda-python/issues/2844)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/service/lambda from 1.37.1 to 1.38.0 in /layer/scripts/layer-balancer ([#2843](https://github.com/aws-powertools/powertools-lambda-python/issues/2843)) +* **deps:** bump pydantic from 1.10.11 to 1.10.12 ([#2846](https://github.com/aws-powertools/powertools-lambda-python/issues/2846)) +* **deps:** bump the layer-balancer group in /layer/scripts/layer-balancer with 3 updates ([#2971](https://github.com/aws-powertools/powertools-lambda-python/issues/2971)) +* **deps:** bump actions/setup-node from 3.8.0 to 3.8.1 ([#2970](https://github.com/aws-powertools/powertools-lambda-python/issues/2970)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.18.30 to 1.18.31 in /layer/scripts/layer-balancer ([#2889](https://github.com/aws-powertools/powertools-lambda-python/issues/2889)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/service/lambda from 1.38.1 to 1.39.0 in /layer/scripts/layer-balancer ([#2890](https://github.com/aws-powertools/powertools-lambda-python/issues/2890)) +* **deps:** bump squidfunk/mkdocs-material from `33e28bd` to `cd3a522` in /docs ([#2859](https://github.com/aws-powertools/powertools-lambda-python/issues/2859)) +* **deps-dev:** bump ruff from 0.0.283 to 0.0.284 ([#2940](https://github.com/aws-powertools/powertools-lambda-python/issues/2940)) +* **deps-dev:** bump cfn-lint from 0.79.5 to 0.79.6 ([#2899](https://github.com/aws-powertools/powertools-lambda-python/issues/2899)) +* **deps-dev:** bump the boto-typing group with 11 updates ([#2901](https://github.com/aws-powertools/powertools-lambda-python/issues/2901)) +* **deps-dev:** bump ruff from 0.0.281 to 0.0.282 ([#2905](https://github.com/aws-powertools/powertools-lambda-python/issues/2905)) +* **deps-dev:** bump aws-cdk from 2.88.0 to 2.89.0 ([#2887](https://github.com/aws-powertools/powertools-lambda-python/issues/2887)) +* **deps-dev:** bump aws-cdk from 2.89.0 to 2.90.0 ([#2932](https://github.com/aws-powertools/powertools-lambda-python/issues/2932)) +* **deps-dev:** bump mkdocs-material from 9.1.19 to 9.1.21 ([#2894](https://github.com/aws-powertools/powertools-lambda-python/issues/2894)) +* **deps-dev:** bump the boto-typing group with 3 updates ([#2967](https://github.com/aws-powertools/powertools-lambda-python/issues/2967)) +* **deps-dev:** bump radon from 5.1.0 to 6.0.1 ([#2964](https://github.com/aws-powertools/powertools-lambda-python/issues/2964)) +* **deps-dev:** bump the boto-typing group with 4 updates ([#2928](https://github.com/aws-powertools/powertools-lambda-python/issues/2928)) +* **deps-dev:** bump mypy-boto3-logs from 1.28.1 to 1.28.15 ([#2880](https://github.com/aws-powertools/powertools-lambda-python/issues/2880)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.28.0 to 1.28.15 ([#2879](https://github.com/aws-powertools/powertools-lambda-python/issues/2879)) +* **deps-dev:** bump mypy-boto3-lambda from 1.28.11 to 1.28.15 ([#2878](https://github.com/aws-powertools/powertools-lambda-python/issues/2878)) +* **deps-dev:** bump mypy-boto3-xray from 1.28.0 to 1.28.15 ([#2881](https://github.com/aws-powertools/powertools-lambda-python/issues/2881)) +* **deps-dev:** bump ruff from 0.0.282 to 0.0.283 ([#2937](https://github.com/aws-powertools/powertools-lambda-python/issues/2937)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.28.0 to 1.28.11 ([#2847](https://github.com/aws-powertools/powertools-lambda-python/issues/2847)) +* **deps-dev:** bump sentry-sdk from 1.28.1 to 1.29.0 ([#2900](https://github.com/aws-powertools/powertools-lambda-python/issues/2900)) +* **deps-dev:** bump cfn-lint from 0.79.4 to 0.79.5 ([#2870](https://github.com/aws-powertools/powertools-lambda-python/issues/2870)) +* **deps-dev:** bump the boto-typing group with 1 update ([#2944](https://github.com/aws-powertools/powertools-lambda-python/issues/2944)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.28.10 to 1.28.12 ([#2864](https://github.com/aws-powertools/powertools-lambda-python/issues/2864)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.28.0 to 1.28.12 ([#2865](https://github.com/aws-powertools/powertools-lambda-python/issues/2865)) +* **deps-dev:** bump cfn-lint from 0.79.3 to 0.79.4 ([#2862](https://github.com/aws-powertools/powertools-lambda-python/issues/2862)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.28.0 to 1.28.12 ([#2861](https://github.com/aws-powertools/powertools-lambda-python/issues/2861)) +* **deps-dev:** bump mypy-boto3-ssm from 1.28.0 to 1.28.12 ([#2863](https://github.com/aws-powertools/powertools-lambda-python/issues/2863)) +* **deps-dev:** bump aws-cdk from 2.90.0 to 2.91.0 ([#2947](https://github.com/aws-powertools/powertools-lambda-python/issues/2947)) +* **deps-dev:** bump xenon from 0.9.0 to 0.9.1 ([#2955](https://github.com/aws-powertools/powertools-lambda-python/issues/2955)) +* **deps-dev:** bump cfn-lint from 0.78.2 to 0.79.3 ([#2854](https://github.com/aws-powertools/powertools-lambda-python/issues/2854)) +* **deps-dev:** bump mypy-boto3-lambda from 1.28.0 to 1.28.11 ([#2845](https://github.com/aws-powertools/powertools-lambda-python/issues/2845)) +* **deps-dev:** bump cfn-lint from 0.79.6 to 0.79.7 ([#2956](https://github.com/aws-powertools/powertools-lambda-python/issues/2956)) +* **deps-dev:** bump aws-cdk from 2.91.0 to 2.92.0 ([#2965](https://github.com/aws-powertools/powertools-lambda-python/issues/2965)) +* **deps-dev:** bump ruff from 0.0.280 to 0.0.281 ([#2891](https://github.com/aws-powertools/powertools-lambda-python/issues/2891)) +* **docs:** include the environment variables section in the utilities documentation ([#2925](https://github.com/aws-powertools/powertools-lambda-python/issues/2925)) +* **docs:** disable line length rule using older syntax ([#2920](https://github.com/aws-powertools/powertools-lambda-python/issues/2920)) +* **maintenance:** enables publishing docs and changelog, running e2e tests only in the main repository ([#2924](https://github.com/aws-powertools/powertools-lambda-python/issues/2924)) + + +<a name="v2.22.0"></a> +## [v2.22.0] - 2023-07-25 +## Bug Fixes + +* **parameters:** distinct cache key for single vs path with same name ([#2839](https://github.com/aws-powertools/powertools-lambda-python/issues/2839)) + +## Documentation + +* **community:** new batch processing article ([#2828](https://github.com/aws-powertools/powertools-lambda-python/issues/2828)) +* **parameters:** improve readability on error handling get_parameter… ([#2833](https://github.com/aws-powertools/powertools-lambda-python/issues/2833)) + +## Features + +* **general:** add support for Python 3.11 ([#2820](https://github.com/aws-powertools/powertools-lambda-python/issues/2820)) + +## Maintenance + +* version bump +* **ci:** add baking time for layer build ([#2834](https://github.com/aws-powertools/powertools-lambda-python/issues/2834)) +* **ci:** build changelog on a schedule only ([#2832](https://github.com/aws-powertools/powertools-lambda-python/issues/2832)) +* **deps:** bump actions/setup-python from 4.6.1 to 4.7.0 ([#2821](https://github.com/aws-powertools/powertools-lambda-python/issues/2821)) +* **deps-dev:** bump ruff from 0.0.278 to 0.0.279 ([#2822](https://github.com/aws-powertools/powertools-lambda-python/issues/2822)) +* **deps-dev:** bump cfn-lint from 0.78.1 to 0.78.2 ([#2823](https://github.com/aws-powertools/powertools-lambda-python/issues/2823)) +* **deps-dev:** bump ruff from 0.0.279 to 0.0.280 ([#2836](https://github.com/aws-powertools/powertools-lambda-python/issues/2836)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.28.0 to 1.28.10 ([#2837](https://github.com/aws-powertools/powertools-lambda-python/issues/2837)) + + +<a name="v2.21.0"></a> +## [v2.21.0] - 2023-07-21 +## Bug Fixes + +* **docs:** remove redundant code ([#2796](https://github.com/aws-powertools/powertools-lambda-python/issues/2796)) + +## Documentation + +* **customer-reference:** add Jit Security as a customer reference ([#2801](https://github.com/aws-powertools/powertools-lambda-python/issues/2801)) + +## Features + +* **parser:** add support for Pydantic v2 ([#2733](https://github.com/aws-powertools/powertools-lambda-python/issues/2733)) + +## Maintenance + +* version bump +* **deps:** bump squidfunk/mkdocs-material from `a28ed81` to `33e28bd` in /docs ([#2797](https://github.com/aws-powertools/powertools-lambda-python/issues/2797)) +* **deps-dev:** bump mypy-boto3-s3 from 1.28.3.post2 to 1.28.8 ([#2808](https://github.com/aws-powertools/powertools-lambda-python/issues/2808)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.13 to 2.8.19.14 ([#2807](https://github.com/aws-powertools/powertools-lambda-python/issues/2807)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.28.3.post1 to 1.28.3.post2 ([#2794](https://github.com/aws-powertools/powertools-lambda-python/issues/2794)) +* **deps-dev:** bump types-requests from 2.31.0.1 to 2.31.0.2 ([#2806](https://github.com/aws-powertools/powertools-lambda-python/issues/2806)) +* **deps-dev:** bump mypy-boto3-s3 from 1.28.3.post1 to 1.28.3.post2 ([#2793](https://github.com/aws-powertools/powertools-lambda-python/issues/2793)) +* **deps-dev:** bump aws-cdk from 2.87.0 to 2.88.0 ([#2812](https://github.com/aws-powertools/powertools-lambda-python/issues/2812)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.28.3 to 1.28.3.post1 ([#2785](https://github.com/aws-powertools/powertools-lambda-python/issues/2785)) +* **deps-dev:** bump mypy-boto3-s3 from 1.28.3 to 1.28.3.post1 ([#2786](https://github.com/aws-powertools/powertools-lambda-python/issues/2786)) +* **deps-dev:** bump mkdocs-material from 9.1.18 to 9.1.19 ([#2798](https://github.com/aws-powertools/powertools-lambda-python/issues/2798)) +* **security:** improve debugging for provenance script ([#2784](https://github.com/aws-powertools/powertools-lambda-python/issues/2784)) + + +<a name="v2.20.0"></a> +## [v2.20.0] - 2023-07-14 +## Bug Fixes + +* **docs:** ensure alias is applied to versioned releases ([#2644](https://github.com/aws-powertools/powertools-lambda-python/issues/2644)) +* **docs:** ensure version alias is in an array to prevent "you're not viewing the latest version" incorrect message ([#2629](https://github.com/aws-powertools/powertools-lambda-python/issues/2629)) +* **logger:** ensure logs stream to stdout by default, not stderr ([#2736](https://github.com/aws-powertools/powertools-lambda-python/issues/2736)) + +## Code Refactoring + +* **parser:** convert functional tests to unit tests ([#2656](https://github.com/aws-powertools/powertools-lambda-python/issues/2656)) + +## Documentation + +* **batch:** fix custom batch processor example ([#2714](https://github.com/aws-powertools/powertools-lambda-python/issues/2714)) +* **contributing:** add code integration journey graph ([#2685](https://github.com/aws-powertools/powertools-lambda-python/issues/2685)) +* **maintainers:** add cicd pipeline diagram ([#2692](https://github.com/aws-powertools/powertools-lambda-python/issues/2692)) +* **process:** explain our integration automated checks; revamp navigation ([#2764](https://github.com/aws-powertools/powertools-lambda-python/issues/2764)) + +## Features + +* **metrics:** support to set default dimension in EphemeralMetrics ([#2748](https://github.com/aws-powertools/powertools-lambda-python/issues/2748)) + +## Maintenance + +* version bump +* **ci:** enforce pip --require-hashes to maybe satistify scorecard ([#2679](https://github.com/aws-powertools/powertools-lambda-python/issues/2679)) +* **ci:** prevent merging PRs that do not meet minimum requirements ([#2639](https://github.com/aws-powertools/powertools-lambda-python/issues/2639)) +* **ci:** enforce top-level permission to minimum fail-safe permission as per openssf ([#2638](https://github.com/aws-powertools/powertools-lambda-python/issues/2638)) +* **ci:** propagate checkout permission to nested workflows ([#2642](https://github.com/aws-powertools/powertools-lambda-python/issues/2642)) +* **ci:** improves dependabot based on ossf scorecard recommendations ([#2647](https://github.com/aws-powertools/powertools-lambda-python/issues/2647)) +* **ci:** use deps sha for docs and gitpod images based on ossf findings ([#2662](https://github.com/aws-powertools/powertools-lambda-python/issues/2662)) +* **ci:** use sast on every commit on any supported language ([#2646](https://github.com/aws-powertools/powertools-lambda-python/issues/2646)) +* **ci:** add gitleaks in pre-commit hooks as an extra safety measure ([#2677](https://github.com/aws-powertools/powertools-lambda-python/issues/2677)) +* **ci:** address ossf scorecard findings on npm, pip, and top-level permission leftover ([#2694](https://github.com/aws-powertools/powertools-lambda-python/issues/2694)) +* **ci:** prevent sast codeql to run in forks ([#2711](https://github.com/aws-powertools/powertools-lambda-python/issues/2711)) +* **ci:** introduce provenance and attestation in release ([#2746](https://github.com/aws-powertools/powertools-lambda-python/issues/2746)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.7 to 1.8.8 ([#2754](https://github.com/aws-powertools/powertools-lambda-python/issues/2754)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/service/lambda from 1.37.0 to 1.37.1 in /layer/scripts/layer-balancer ([#2769](https://github.com/aws-powertools/powertools-lambda-python/issues/2769)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.1.3 to 2.1.4 ([#2738](https://github.com/aws-powertools/powertools-lambda-python/issues/2738)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.18.27 to 1.18.28 in /layer/scripts/layer-balancer ([#2770](https://github.com/aws-powertools/powertools-lambda-python/issues/2770)) +* **deps:** bump actions/setup-python from 4.6.1 to 4.7.0 ([#2768](https://github.com/aws-powertools/powertools-lambda-python/issues/2768)) +* **deps:** bump github.com/aws/aws-sdk-go-v2 from 1.16.16 to 1.18.1 in /layer/scripts/layer-balancer ([#2654](https://github.com/aws-powertools/powertools-lambda-python/issues/2654)) +* **deps:** bump golang.org/x/sync from 0.1.0 to 0.3.0 in /layer/scripts/layer-balancer ([#2649](https://github.com/aws-powertools/powertools-lambda-python/issues/2649)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/service/lambda from 1.24.6 to 1.37.0 in /layer/scripts/layer-balancer ([#2653](https://github.com/aws-powertools/powertools-lambda-python/issues/2653)) +* **deps:** bump docker/setup-buildx-action from 2.8.0 to 2.9.0 ([#2718](https://github.com/aws-powertools/powertools-lambda-python/issues/2718)) +* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.17.8 to 1.18.27 in /layer/scripts/layer-balancer ([#2651](https://github.com/aws-powertools/powertools-lambda-python/issues/2651)) +* **deps:** bump github.com/aws/aws-sdk-go-v2 from 1.18.1 to 1.19.0 in /layer/scripts/layer-balancer ([#2771](https://github.com/aws-powertools/powertools-lambda-python/issues/2771)) +* **deps:** migrate from retry to retry2 to address CVE-2022-42969 ([#2665](https://github.com/aws-powertools/powertools-lambda-python/issues/2665)) +* **deps:** bump pydantic from 1.10.9 to 1.10.10 ([#2624](https://github.com/aws-powertools/powertools-lambda-python/issues/2624)) +* **deps:** bump squidfunk/mkdocs-material from `3837c0f` to `a28ed81` in /docs ([#2669](https://github.com/aws-powertools/powertools-lambda-python/issues/2669)) +* **deps:** bump pydantic from 1.10.10 to 1.10.11 ([#2671](https://github.com/aws-powertools/powertools-lambda-python/issues/2671)) +* **deps:** bump docker/setup-buildx-action from 2.9.0 to 2.9.1 ([#2755](https://github.com/aws-powertools/powertools-lambda-python/issues/2755)) +* **deps:** bump actions/dependency-review-action from 2.5.1 to 3.0.6 ([#2650](https://github.com/aws-powertools/powertools-lambda-python/issues/2650)) +* **deps:** bump actions/setup-node from 3.6.0 to 3.7.0 ([#2689](https://github.com/aws-powertools/powertools-lambda-python/issues/2689)) +* **deps-dev:** bump mypy-boto3-lambda from 1.27.0 to 1.28.0 ([#2698](https://github.com/aws-powertools/powertools-lambda-python/issues/2698)) +* **deps-dev:** bump sentry-sdk from 1.27.0 to 1.27.1 ([#2701](https://github.com/aws-powertools/powertools-lambda-python/issues/2701)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.27.0 to 1.28.0 ([#2700](https://github.com/aws-powertools/powertools-lambda-python/issues/2700)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.27.0 to 1.28.0 ([#2699](https://github.com/aws-powertools/powertools-lambda-python/issues/2699)) +* **deps-dev:** bump ruff from 0.0.276 to 0.0.277 ([#2682](https://github.com/aws-powertools/powertools-lambda-python/issues/2682)) +* **deps-dev:** bump pytest-asyncio from 0.21.0 to 0.21.1 ([#2756](https://github.com/aws-powertools/powertools-lambda-python/issues/2756)) +* **deps-dev:** bump cfn-lint from 0.77.10 to 0.78.1 ([#2757](https://github.com/aws-powertools/powertools-lambda-python/issues/2757)) +* **deps-dev:** bump aws-cdk from 2.86.0 to 2.87.0 ([#2696](https://github.com/aws-powertools/powertools-lambda-python/issues/2696)) +* **deps-dev:** bump typed-ast from 1.5.4 to 1.5.5 ([#2670](https://github.com/aws-powertools/powertools-lambda-python/issues/2670)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.27.0 to 1.28.0 ([#2697](https://github.com/aws-powertools/powertools-lambda-python/issues/2697)) +* **deps-dev:** bump ruff from 0.0.275 to 0.0.276 ([#2655](https://github.com/aws-powertools/powertools-lambda-python/issues/2655)) +* **deps-dev:** bump sentry-sdk from 1.26.0 to 1.27.0 ([#2652](https://github.com/aws-powertools/powertools-lambda-python/issues/2652)) +* **deps-dev:** bump ruff from 0.0.277 to 0.0.278 ([#2758](https://github.com/aws-powertools/powertools-lambda-python/issues/2758)) +* **deps-dev:** bump mypy-boto3-s3 from 1.28.0 to 1.28.3 ([#2774](https://github.com/aws-powertools/powertools-lambda-python/issues/2774)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.158 to 1.26.164 ([#2622](https://github.com/aws-powertools/powertools-lambda-python/issues/2622)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.28.0 to 1.28.3 ([#2773](https://github.com/aws-powertools/powertools-lambda-python/issues/2773)) +* **deps-dev:** bump sentry-sdk from 1.27.1 to 1.28.0 ([#2741](https://github.com/aws-powertools/powertools-lambda-python/issues/2741)) +* **deps-dev:** bump mypy-boto3-s3 from 1.27.0 to 1.28.0 ([#2721](https://github.com/aws-powertools/powertools-lambda-python/issues/2721)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.27.0 to 1.28.0 ([#2722](https://github.com/aws-powertools/powertools-lambda-python/issues/2722)) +* **deps-dev:** bump mypy-boto3-logs from 1.27.0 to 1.28.1 ([#2723](https://github.com/aws-powertools/powertools-lambda-python/issues/2723)) +* **deps-dev:** bump mypy-boto3-ssm from 1.27.0 to 1.28.0 ([#2724](https://github.com/aws-powertools/powertools-lambda-python/issues/2724)) +* **deps-dev:** bump mypy-boto3-xray from 1.27.0 to 1.28.0 ([#2720](https://github.com/aws-powertools/powertools-lambda-python/issues/2720)) +* **deps-dev:** bump sentry-sdk from 1.28.0 to 1.28.1 ([#2772](https://github.com/aws-powertools/powertools-lambda-python/issues/2772)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.27.0 to 1.28.0 ([#2740](https://github.com/aws-powertools/powertools-lambda-python/issues/2740)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.26.70 to 1.27.0 ([#2636](https://github.com/aws-powertools/powertools-lambda-python/issues/2636)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.27.0 to 1.28.0 ([#2739](https://github.com/aws-powertools/powertools-lambda-python/issues/2739)) +* **governance:** update active maintainers list ([#2715](https://github.com/aws-powertools/powertools-lambda-python/issues/2715)) +* **streaming:** replace deprecated Version classes from distutils ([#2752](https://github.com/aws-powertools/powertools-lambda-python/issues/2752)) +* **user-agent:** support patching botocore session ([#2614](https://github.com/aws-powertools/powertools-lambda-python/issues/2614)) + + +<a name="v2.19.0"></a> +## [v2.19.0] - 2023-06-30 +## Bug Fixes + +* **e2e:** fix idempotency tests ([#2576](https://github.com/aws-powertools/powertools-lambda-python/issues/2576)) + +## Code Refactoring + +* **event_source:** convert functional tests to unit tests ([#2506](https://github.com/aws-powertools/powertools-lambda-python/issues/2506)) + +## Documentation + +* **i-made-this:** added new article on idempotency ([#2582](https://github.com/aws-powertools/powertools-lambda-python/issues/2582)) +* **i-made-this:** article on idempotency w/ CDK and Powertools ([#2569](https://github.com/aws-powertools/powertools-lambda-python/issues/2569)) +* **idempotency:** split snippets, improve wording and lint examples ([#2492](https://github.com/aws-powertools/powertools-lambda-python/issues/2492)) + +## Features + +* **event_handler:** add VPCLatticeResolver ([#2601](https://github.com/aws-powertools/powertools-lambda-python/issues/2601)) +* **event_source:** decode nested messages on SQS events ([#2349](https://github.com/aws-powertools/powertools-lambda-python/issues/2349)) +* **parser:** add support to VpcLatticeModel ([#2584](https://github.com/aws-powertools/powertools-lambda-python/issues/2584)) + +## Maintenance + +* version bump +* **analytics:** update docs base origin url ([#2560](https://github.com/aws-powertools/powertools-lambda-python/issues/2560)) +* **ci:** replace flake8 with Ruff as a linter ([#2495](https://github.com/aws-powertools/powertools-lambda-python/issues/2495)) +* **ci:** enable Ruff rule E501 and fix errors ([#2587](https://github.com/aws-powertools/powertools-lambda-python/issues/2587)) +* **ci:** enable Ruff rule COM812 and fix the errors ([#2595](https://github.com/aws-powertools/powertools-lambda-python/issues/2595)) +* **ci:** enable Ruff rules PLW, PLR, PLC and PLE and fix the errors ([#2593](https://github.com/aws-powertools/powertools-lambda-python/issues/2593)) +* **ci:** enable Ruff autofix rules ([#2599](https://github.com/aws-powertools/powertools-lambda-python/issues/2599)) +* **ci:** enable Ruff rules ISC, I001, B018 and fix the errors ([#2597](https://github.com/aws-powertools/powertools-lambda-python/issues/2597)) +* **ci:** enable Ruff rule ERA001 and fix errors ([#2591](https://github.com/aws-powertools/powertools-lambda-python/issues/2591)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.6 to 1.8.7 ([#2573](https://github.com/aws-powertools/powertools-lambda-python/issues/2573)) +* **deps:** bump docker/setup-buildx-action from 2.7.0 to 2.8.0 ([#2604](https://github.com/aws-powertools/powertools-lambda-python/issues/2604)) +* **deps:** bump ossf/scorecard-action from 2.1.3 to 2.2.0 ([#2563](https://github.com/aws-powertools/powertools-lambda-python/issues/2563)) +* **deps:** bump release-drafter/release-drafter from 5.23.0 to 5.24.0 ([#2603](https://github.com/aws-powertools/powertools-lambda-python/issues/2603)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.155 to 1.26.163 ([#2608](https://github.com/aws-powertools/powertools-lambda-python/issues/2608)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.157 to 1.26.163 ([#2607](https://github.com/aws-powertools/powertools-lambda-python/issues/2607)) +* **deps-dev:** bump mkdocs-material from 9.1.16 to 9.1.17 ([#2564](https://github.com/aws-powertools/powertools-lambda-python/issues/2564)) +* **deps-dev:** bump ruff from 0.0.272 to 0.0.275 ([#2586](https://github.com/aws-powertools/powertools-lambda-python/issues/2586)) +* **deps-dev:** bump mypy-boto3-ssm from 1.26.97 to 1.26.162 ([#2606](https://github.com/aws-powertools/powertools-lambda-python/issues/2606)) +* **deps-dev:** bump pytest from 7.3.2 to 7.4.0 ([#2557](https://github.com/aws-powertools/powertools-lambda-python/issues/2557)) +* **deps-dev:** bump aws-cdk from 2.85.0 to 2.86.0 ([#2613](https://github.com/aws-powertools/powertools-lambda-python/issues/2613)) +* **deps-dev:** bump mypy from 1.4.0 to 1.4.1 ([#2574](https://github.com/aws-powertools/powertools-lambda-python/issues/2574)) + + +<a name="v2.18.0"></a> +## [v2.18.0] - 2023-06-23 +## Bug Fixes + +* **docs:** ensure versions.json is updated ([#2505](https://github.com/aws-powertools/powertools-lambda-python/issues/2505)) +* **event_source:** centralizing helper functions for query, header and base64 ([#2496](https://github.com/aws-powertools/powertools-lambda-python/issues/2496)) + +## Documentation + +* **homepage:** fix .NET repository link ([#2549](https://github.com/aws-powertools/powertools-lambda-python/issues/2549)) +* **homepage:** add Open Source Security Foundation badge; update links to new url ([#2545](https://github.com/aws-powertools/powertools-lambda-python/issues/2545)) +* **navigation:** make Key Feature the first section ([#2517](https://github.com/aws-powertools/powertools-lambda-python/issues/2517)) + +## Features + +* **event_handler:** support to enable or disable compression in custom responses ([#2544](https://github.com/aws-powertools/powertools-lambda-python/issues/2544)) +* **feature_flags:** add modulo range condition for segmented experimentation support ([#2331](https://github.com/aws-powertools/powertools-lambda-python/issues/2331)) + +## Maintenance + +* version bump +* **ci:** fix changelog build permissions ([#2519](https://github.com/aws-powertools/powertools-lambda-python/issues/2519)) +* **ci:** remove GH pages action ([#2501](https://github.com/aws-powertools/powertools-lambda-python/issues/2501)) +* **ci:** updates runner names in workflows ([#2510](https://github.com/aws-powertools/powertools-lambda-python/issues/2510)) +* **ci:** introduces OSSF Scorecard ([#2512](https://github.com/aws-powertools/powertools-lambda-python/issues/2512)) +* **ci:** fix codeowners team name ([#2516](https://github.com/aws-powertools/powertools-lambda-python/issues/2516)) +* **deps:** bump actions/upload-artifact from 3.1.0 to 3.1.2 ([#2522](https://github.com/aws-powertools/powertools-lambda-python/issues/2522)) +* **deps:** bump actions/checkout from 3.1.0 to 3.5.3 ([#2523](https://github.com/aws-powertools/powertools-lambda-python/issues/2523)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.153 to 1.26.155 ([#2498](https://github.com/aws-powertools/powertools-lambda-python/issues/2498)) +* **deps-dev:** bump aws-cdk from 2.84.0 to 2.85.0 ([#2524](https://github.com/aws-powertools/powertools-lambda-python/issues/2524)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.147 to 1.26.157 ([#2507](https://github.com/aws-powertools/powertools-lambda-python/issues/2507)) +* **deps-dev:** bump cfn-lint from 0.77.9 to 0.77.10 ([#2508](https://github.com/aws-powertools/powertools-lambda-python/issues/2508)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.149 to 1.26.156 ([#2503](https://github.com/aws-powertools/powertools-lambda-python/issues/2503)) +* **deps-dev:** bump sentry-sdk from 1.25.1 to 1.26.0 ([#2527](https://github.com/aws-powertools/powertools-lambda-python/issues/2527)) +* **deps-dev:** bump hvac from 1.1.0 to 1.1.1 ([#2497](https://github.com/aws-powertools/powertools-lambda-python/issues/2497)) +* **deps-dev:** bump flake8-variables-names from 0.0.5 to 0.0.6 ([#2525](https://github.com/aws-powertools/powertools-lambda-python/issues/2525)) +* **deps-dev:** bump ijson from 3.2.1 to 3.2.2 ([#2526](https://github.com/aws-powertools/powertools-lambda-python/issues/2526)) +* **deps-dev:** bump pytest-mock from 3.10.0 to 3.11.1 ([#2485](https://github.com/aws-powertools/powertools-lambda-python/issues/2485)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.152 to 1.26.158 ([#2528](https://github.com/aws-powertools/powertools-lambda-python/issues/2528)) +* **deps-dev:** bump mypy from 1.3.0 to 1.4.0 ([#2509](https://github.com/aws-powertools/powertools-lambda-python/issues/2509)) +* **documentation:** updating repository URL and name to the new location ([#2499](https://github.com/aws-powertools/powertools-lambda-python/issues/2499)) + + +<a name="v2.17.0"></a> +## [v2.17.0] - 2023-06-16 +## Bug Fixes + +* **event_handler:** prioritize static over dynamic route to prevent order of route registration mismatch ([#2458](https://github.com/aws-powertools/powertools-lambda-python/issues/2458)) +* **idempotency:** treat missing idempotency key as non-idempotent transaction (no-op) when raise_on_no_idempotency_key is False ([#2477](https://github.com/aws-powertools/powertools-lambda-python/issues/2477)) + +## Documentation + +* **event_handler:** improve compress example using Response class ([#2426](https://github.com/aws-powertools/powertools-lambda-python/issues/2426)) +* **event_sources:** fix DynamoDB stream event docstring ([#2468](https://github.com/aws-powertools/powertools-lambda-python/issues/2468)) +* **idempotency:** new sequence flow when idempotency key is optional ([#2480](https://github.com/aws-powertools/powertools-lambda-python/issues/2480)) +* **idempotency:** add CDK example ([#2434](https://github.com/aws-powertools/powertools-lambda-python/issues/2434)) +* **maintainers:** visual representation of release process ([#2399](https://github.com/aws-powertools/powertools-lambda-python/issues/2399)) +* **navigation:** standardize link targets to enhance customer experience ([#2420](https://github.com/aws-powertools/powertools-lambda-python/issues/2420)) +* **we-made-this:** new article about idempotency design ([#2425](https://github.com/aws-powertools/powertools-lambda-python/issues/2425)) + +## Features + +* **event_sources:** add AWS Config Rule event data class ([#2175](https://github.com/aws-powertools/powertools-lambda-python/issues/2175)) +* **event_sources:** add support for VPC Lattice events ([#2358](https://github.com/aws-powertools/powertools-lambda-python/issues/2358)) +* **logger:** type log record in LambdaPowertoolsFormatter with TypedDict ([#2419](https://github.com/aws-powertools/powertools-lambda-python/issues/2419)) +* **parser:** support for CloudFormation Custom Resources ([#2335](https://github.com/aws-powertools/powertools-lambda-python/issues/2335)) + +## Maintenance + +* version bump +* **ci:** document all github action workflows and enforce least-privilege ([#2395](https://github.com/aws-powertools/powertools-lambda-python/issues/2395)) +* **ci:** fix PR labeling permission scope ([#2396](https://github.com/aws-powertools/powertools-lambda-python/issues/2396)) +* **deps:** bump aws-actions/configure-aws-credentials from 2.1.0 to 2.2.0 ([#2469](https://github.com/aws-powertools/powertools-lambda-python/issues/2469)) +* **deps:** bump docker/setup-buildx-action from 2.5.0 to 2.6.0 ([#2403](https://github.com/aws-powertools/powertools-lambda-python/issues/2403)) +* **deps:** bump docker/setup-qemu-action from 2.1.0 to 2.2.0 ([#2404](https://github.com/aws-powertools/powertools-lambda-python/issues/2404)) +* **deps:** bump docker/setup-buildx-action from 2.6.0 to 2.7.0 ([#2450](https://github.com/aws-powertools/powertools-lambda-python/issues/2450)) +* **deps:** bump pydantic from 1.10.8 to 1.10.9 ([#2405](https://github.com/aws-powertools/powertools-lambda-python/issues/2405)) +* **deps:** bump actions/checkout from 3.5.2 to 3.5.3 ([#2431](https://github.com/aws-powertools/powertools-lambda-python/issues/2431)) +* **deps-dev:** bump ijson from 3.2.0.post0 to 3.2.1 ([#2441](https://github.com/aws-powertools/powertools-lambda-python/issues/2441)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.115 to 1.26.152 ([#2444](https://github.com/aws-powertools/powertools-lambda-python/issues/2444)) +* **deps-dev:** bump filelock from 3.12.0 to 3.12.2 ([#2446](https://github.com/aws-powertools/powertools-lambda-python/issues/2446)) +* **deps-dev:** bump aws-cdk from 2.83.0 to 2.83.1 ([#2432](https://github.com/aws-powertools/powertools-lambda-python/issues/2432)) +* **deps-dev:** bump cfn-lint from 0.77.6 to 0.77.7 ([#2414](https://github.com/aws-powertools/powertools-lambda-python/issues/2414)) +* **deps-dev:** bump pytest from 7.3.1 to 7.3.2 ([#2443](https://github.com/aws-powertools/powertools-lambda-python/issues/2443)) +* **deps-dev:** bump sentry-sdk from 1.25.0 to 1.25.1 ([#2408](https://github.com/aws-powertools/powertools-lambda-python/issues/2408)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.147 to 1.26.149 ([#2410](https://github.com/aws-powertools/powertools-lambda-python/issues/2410)) +* **deps-dev:** bump aws-cdk from 2.82.0 to 2.83.0 ([#2406](https://github.com/aws-powertools/powertools-lambda-python/issues/2406)) +* **deps-dev:** bump mypy-boto3-logs from 1.26.53 to 1.26.149 ([#2409](https://github.com/aws-powertools/powertools-lambda-python/issues/2409)) +* **deps-dev:** bump cfn-lint from 0.77.7 to 0.77.8 ([#2451](https://github.com/aws-powertools/powertools-lambda-python/issues/2451)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.127 to 1.26.153 ([#2452](https://github.com/aws-powertools/powertools-lambda-python/issues/2452)) +* **deps-dev:** bump cfn-lint from 0.77.8 to 0.77.9 ([#2472](https://github.com/aws-powertools/powertools-lambda-python/issues/2472)) +* **deps-dev:** bump flake8-comprehensions from 3.12.0 to 3.13.0 ([#2471](https://github.com/aws-powertools/powertools-lambda-python/issues/2471)) +* **deps-dev:** bump mkdocs-material from 9.1.15 to 9.1.16 ([#2470](https://github.com/aws-powertools/powertools-lambda-python/issues/2470)) +* **deps-dev:** bump aws-cdk from 2.83.1 to 2.84.0 ([#2460](https://github.com/aws-powertools/powertools-lambda-python/issues/2460)) + + +<a name="v2.16.2"></a> +## [v2.16.2] - 2023-06-06 +## Bug Fixes + +* **parameters:** AppConfigProvider when retrieving multiple unique configuration names ([#2378](https://github.com/aws-powertools/powertools-lambda-python/issues/2378)) +* **shared:** move to static version bumping to prevent issues with customers custom builds ([#2386](https://github.com/aws-powertools/powertools-lambda-python/issues/2386)) + +## Maintenance + +* version bump +* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.108 to 1.26.147 ([#2383](https://github.com/aws-powertools/powertools-lambda-python/issues/2383)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.122 to 1.26.147 ([#2382](https://github.com/aws-powertools/powertools-lambda-python/issues/2382)) +* **deps-dev:** bump sentry-sdk from 1.24.0 to 1.25.0 ([#2374](https://github.com/aws-powertools/powertools-lambda-python/issues/2374)) +* **deps-dev:** bump aws-cdk from 2.81.0 to 2.82.0 ([#2373](https://github.com/aws-powertools/powertools-lambda-python/issues/2373)) +* **typing:** add setLevel and addHandler to Logger for mypy/pyright ([#2388](https://github.com/aws-powertools/powertools-lambda-python/issues/2388)) + + +<a name="v2.16.1"></a> +## [v2.16.1] - 2023-06-02 +## Bug Fixes + +* **shared:** skip user agent on much older botocore versions ([#2366](https://github.com/aws-powertools/powertools-lambda-python/issues/2366)) + +## Maintenance + +* version bump + + +<a name="v2.16.0"></a> +## [v2.16.0] - 2023-06-02 +## Bug Fixes + +* **docs:** use concrete secrets from settings ([#2322](https://github.com/aws-powertools/powertools-lambda-python/issues/2322)) +* **event_source:** change the import location of boto3 in CodePipelineJobEvent data class ([#2353](https://github.com/aws-powertools/powertools-lambda-python/issues/2353)) +* **logger:** add setLevel function to set level programmatically ([#2320](https://github.com/aws-powertools/powertools-lambda-python/issues/2320)) + +## Code Refactoring + +* **logger:** remove subclassing and move unnecessary APIs ([#2334](https://github.com/aws-powertools/powertools-lambda-python/issues/2334)) + +## Documentation + +* **batch:** add encryption at rest for SQS ([#2290](https://github.com/aws-powertools/powertools-lambda-python/issues/2290)) +* **batch_processing:** snippets split, improved, and lint ([#2231](https://github.com/aws-powertools/powertools-lambda-python/issues/2231)) +* **feature_flags:** snippets split, improved, and lint ([#2222](https://github.com/aws-powertools/powertools-lambda-python/issues/2222)) +* **project:** rename project to Powertools for AWS Lambda (Python) ([#2313](https://github.com/aws-powertools/powertools-lambda-python/issues/2313)) + +## Features + +* **docs:** Move docs to S3 ([#2277](https://github.com/aws-powertools/powertools-lambda-python/issues/2277)) +* **event_source:** allow multiple CORS origins ([#2279](https://github.com/aws-powertools/powertools-lambda-python/issues/2279)) +* **parser:** add support for parsing SQS events wrapped in Kinesis Firehose ([#2294](https://github.com/aws-powertools/powertools-lambda-python/issues/2294)) +* **user-agent:** add custom header User-Agent to AWS SDK requests ([#2267](https://github.com/aws-powertools/powertools-lambda-python/issues/2267)) + +## Maintenance + +* version bump +* **ci:** remove auto-merge workflow ([#2214](https://github.com/aws-powertools/powertools-lambda-python/issues/2214)) +* **ci:** schedule changelog to rebuild daily at 8am, and on release only ([#2216](https://github.com/aws-powertools/powertools-lambda-python/issues/2216)) +* **ci:** create pull request on changelog update ([#2224](https://github.com/aws-powertools/powertools-lambda-python/issues/2224)) +* **ci:** skip analytics on forks ([#2225](https://github.com/aws-powertools/powertools-lambda-python/issues/2225)) +* **ci:** enforce zero trust for third party workflows ([#2215](https://github.com/aws-powertools/powertools-lambda-python/issues/2215)) +* **ci:** convert create-pr steps into composite action ([#2238](https://github.com/aws-powertools/powertools-lambda-python/issues/2238)) +* **ci:** bump package version after release via pull request ([#2239](https://github.com/aws-powertools/powertools-lambda-python/issues/2239)) +* **ci:** update layer ARN docs and create PR during release ([#2240](https://github.com/aws-powertools/powertools-lambda-python/issues/2240)) +* **ci:** fail create-pr when branch cannot be created or behind tip +* **ci:** filter out bot commits from CHANGELOG +* **ci:** add more permissions to analytics +* **ci:** source code tampering protection for release ([#2301](https://github.com/aws-powertools/powertools-lambda-python/issues/2301)) +* **deps:** bump fastjsonschema from 2.16.3 to 2.17.1 ([#2307](https://github.com/aws-powertools/powertools-lambda-python/issues/2307)) +* **deps:** bump aws-actions/configure-aws-credentials from 2.0.0 to 2.1.0 ([#2350](https://github.com/aws-powertools/powertools-lambda-python/issues/2350)) +* **deps:** bump typing-extensions from 4.5.0 to 4.6.2 ([#2345](https://github.com/aws-powertools/powertools-lambda-python/issues/2345)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.1.2 to 2.1.3 ([#2227](https://github.com/aws-powertools/powertools-lambda-python/issues/2227)) +* **deps:** bump actions/setup-python from 4.6.0 to 4.6.1 ([#2325](https://github.com/aws-powertools/powertools-lambda-python/issues/2325)) +* **deps:** update mkdocs configuration to support pymdown-extensions 10.0 ([#2271](https://github.com/aws-powertools/powertools-lambda-python/issues/2271)) +* **deps:** bump pymdown-extensions from 9.11 to 10.0 ([#2262](https://github.com/aws-powertools/powertools-lambda-python/issues/2262)) +* **deps:** bump pydantic from 1.10.7 to 1.10.8 ([#2316](https://github.com/aws-powertools/powertools-lambda-python/issues/2316)) +* **deps:** bump codecov/codecov-action from 3.1.3 to 3.1.4 ([#2263](https://github.com/aws-powertools/powertools-lambda-python/issues/2263)) +* **deps:** bump requests from 2.28.2 to 2.31.0 ([#2308](https://github.com/aws-powertools/powertools-lambda-python/issues/2308)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.26.116 to 1.26.135 ([#2282](https://github.com/aws-powertools/powertools-lambda-python/issues/2282)) +* **deps-dev:** bump pytest-xdist from 3.2.1 to 3.3.0 ([#2251](https://github.com/aws-powertools/powertools-lambda-python/issues/2251)) +* **deps-dev:** bump aws-cdk from 2.79.0 to 2.79.1 ([#2252](https://github.com/aws-powertools/powertools-lambda-python/issues/2252)) +* **deps-dev:** bump mkdocs-material from 9.1.11 to 9.1.12 ([#2253](https://github.com/aws-powertools/powertools-lambda-python/issues/2253)) +* **deps-dev:** bump aws-cdk from 2.79.1 to 2.80.0 ([#2305](https://github.com/aws-powertools/powertools-lambda-python/issues/2305)) +* **deps-dev:** bump mkdocs-material from 9.1.13 to 9.1.14 ([#2304](https://github.com/aws-powertools/powertools-lambda-python/issues/2304)) +* **deps-dev:** bump mkdocs-material from 9.1.12 to 9.1.13 ([#2280](https://github.com/aws-powertools/powertools-lambda-python/issues/2280)) +* **deps-dev:** bump aws-cdk from 2.80.0 to 2.81.0 ([#2332](https://github.com/aws-powertools/powertools-lambda-python/issues/2332)) +* **deps-dev:** bump sentry-sdk from 1.22.2 to 1.23.0 ([#2264](https://github.com/aws-powertools/powertools-lambda-python/issues/2264)) +* **deps-dev:** bump sentry-sdk from 1.23.1 to 1.24.0 ([#2314](https://github.com/aws-powertools/powertools-lambda-python/issues/2314)) +* **deps-dev:** bump types-requests from 2.30.0.0 to 2.31.0.0 ([#2315](https://github.com/aws-powertools/powertools-lambda-python/issues/2315)) +* **deps-dev:** bump httpx from 0.24.0 to 0.24.1 ([#2298](https://github.com/aws-powertools/powertools-lambda-python/issues/2298)) +* **deps-dev:** bump aws-cdk from 2.78.0 to 2.79.0 ([#2235](https://github.com/aws-powertools/powertools-lambda-python/issues/2235)) +* **deps-dev:** bump mypy from 1.2.0 to 1.3.0 ([#2233](https://github.com/aws-powertools/powertools-lambda-python/issues/2233)) +* **deps-dev:** bump pytest-cov from 4.0.0 to 4.1.0 ([#2327](https://github.com/aws-powertools/powertools-lambda-python/issues/2327)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.12 to 2.8.19.13 ([#2234](https://github.com/aws-powertools/powertools-lambda-python/issues/2234)) +* **deps-dev:** bump coverage from 7.2.5 to 7.2.6 ([#2326](https://github.com/aws-powertools/powertools-lambda-python/issues/2326)) +* **deps-dev:** bump mkdocs-material from 9.1.14 to 9.1.15 ([#2337](https://github.com/aws-powertools/powertools-lambda-python/issues/2337)) +* **deps-dev:** bump mkdocs-material from 9.1.9 to 9.1.11 ([#2229](https://github.com/aws-powertools/powertools-lambda-python/issues/2229)) +* **deps-dev:** bump cfn-lint from 0.77.4 to 0.77.5 ([#2228](https://github.com/aws-powertools/powertools-lambda-python/issues/2228)) +* **deps-dev:** bump cfn-lint from 0.77.5 to 0.77.6 ([#2360](https://github.com/aws-powertools/powertools-lambda-python/issues/2360)) +* **deps-dev:** bump coverage from 7.2.6 to 7.2.7 ([#2338](https://github.com/aws-powertools/powertools-lambda-python/issues/2338)) +* **deps-dev:** bump types-requests from 2.31.0.0 to 2.31.0.1 ([#2339](https://github.com/aws-powertools/powertools-lambda-python/issues/2339)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.26.99 to 1.26.127 ([#2219](https://github.com/aws-powertools/powertools-lambda-python/issues/2219)) +* **deps-dev:** bump types-requests from 2.29.0.0 to 2.30.0.0 ([#2220](https://github.com/aws-powertools/powertools-lambda-python/issues/2220)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.116 to 1.26.127 ([#2218](https://github.com/aws-powertools/powertools-lambda-python/issues/2218)) +* **deps-dev:** bump pytest-xdist from 3.3.0 to 3.3.1 ([#2297](https://github.com/aws-powertools/powertools-lambda-python/issues/2297)) +* **deps-dev:** bump sentry-sdk from 1.23.0 to 1.23.1 ([#2283](https://github.com/aws-powertools/powertools-lambda-python/issues/2283)) +* **deps-dev:** bump aws-cdk from 2.77.0 to 2.78.0 ([#2202](https://github.com/aws-powertools/powertools-lambda-python/issues/2202)) +* **governance:** Fix python version in issue templates ([#2275](https://github.com/aws-powertools/powertools-lambda-python/issues/2275)) + + +<a name="v2.15.0"></a> +## [v2.15.0] - 2023-05-04 +## Bug Fixes + +* typo +* **ci:** pypi publishing was targetting test endpoint + +## Documentation + +* **batch:** fixed typo in DynamoDB Streams section ([#2189](https://github.com/aws-powertools/powertools-lambda-python/issues/2189)) +* **examples:** standardize lambda handler function name ([#2192](https://github.com/aws-powertools/powertools-lambda-python/issues/2192)) +* **homepage:** add customer references section ([#2159](https://github.com/aws-powertools/powertools-lambda-python/issues/2159)) +* **jmespath:** fix MD037/no-space-in-emphasis +* **tutorial:** use newer sam cli template; update to py3.10 ([#2167](https://github.com/aws-powertools/powertools-lambda-python/issues/2167)) +* **we-made-this:** add serverless transactional message app ([#2182](https://github.com/aws-powertools/powertools-lambda-python/issues/2182)) + +## Features + +* **ci:** dispatch GitHub analytics action ([#2161](https://github.com/aws-powertools/powertools-lambda-python/issues/2161)) +* **event_source:** support custom json_deserializer; add json_body in SQSEvent ([#2200](https://github.com/aws-powertools/powertools-lambda-python/issues/2200)) +* **event_source:** add support for dynamic partitions in the Api Gateway Authorizer event ([#2176](https://github.com/aws-powertools/powertools-lambda-python/issues/2176)) +* **event_sources:** Add __str__ to Data Classes base DictWrapper ([#2129](https://github.com/aws-powertools/powertools-lambda-python/issues/2129)) +* **jmespath:** new built-in envelopes to unwrap S3 events ([#2169](https://github.com/aws-powertools/powertools-lambda-python/issues/2169)) +* **logger:** add DatadogLogFormatter and observability provider ([#2183](https://github.com/aws-powertools/powertools-lambda-python/issues/2183)) +* **metrics:** add flush_metrics() method to allow manual flushing of metrics ([#2171](https://github.com/aws-powertools/powertools-lambda-python/issues/2171)) +* **parser:** add support for SQS-wrapped S3 event notifications ([#2108](https://github.com/aws-powertools/powertools-lambda-python/issues/2108)) + +## Maintenance + +* update v2 layer ARN on documentation +* add dummy reusable dispatch analytics job +* **ci:** remove build step from release env; no more secrets need +* **ci:** use new pypi trusted publisher for increase security ([#2198](https://github.com/aws-powertools/powertools-lambda-python/issues/2198)) +* **deps:** bump pypa/gh-action-pypi-publish from 1.8.5 to 1.8.6 ([#2201](https://github.com/aws-powertools/powertools-lambda-python/issues/2201)) +* **deps-dev:** bump cfn-lint from 0.77.3 to 0.77.4 ([#2178](https://github.com/aws-powertools/powertools-lambda-python/issues/2178)) +* **deps-dev:** bump types-requests from 2.28.11.17 to 2.29.0.0 ([#2187](https://github.com/aws-powertools/powertools-lambda-python/issues/2187)) +* **deps-dev:** bump coverage from 7.2.4 to 7.2.5 ([#2186](https://github.com/aws-powertools/powertools-lambda-python/issues/2186)) +* **deps-dev:** bump mkdocs-material from 9.1.8 to 9.1.9 ([#2190](https://github.com/aws-powertools/powertools-lambda-python/issues/2190)) +* **deps-dev:** bump importlib-metadata from 6.5.0 to 6.6.0 ([#2163](https://github.com/aws-powertools/powertools-lambda-python/issues/2163)) +* **deps-dev:** bump mypy-boto3-xray from 1.26.11.post1 to 1.26.122 ([#2173](https://github.com/aws-powertools/powertools-lambda-python/issues/2173)) +* **deps-dev:** bump aws-cdk from 2.76.0 to 2.77.0 ([#2174](https://github.com/aws-powertools/powertools-lambda-python/issues/2174)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.115 to 1.26.122 ([#2172](https://github.com/aws-powertools/powertools-lambda-python/issues/2172)) +* **deps-dev:** bump cfn-lint from 0.77.2 to 0.77.3 ([#2165](https://github.com/aws-powertools/powertools-lambda-python/issues/2165)) +* **deps-dev:** bump mkdocs-material from 9.1.6 to 9.1.8 ([#2162](https://github.com/aws-powertools/powertools-lambda-python/issues/2162)) +* **deps-dev:** bump coverage from 7.2.3 to 7.2.4 ([#2179](https://github.com/aws-powertools/powertools-lambda-python/issues/2179)) +* **governance:** add Lambda Powertools for .NET in issue templates ([#2196](https://github.com/aws-powertools/powertools-lambda-python/issues/2196)) + + +<a name="v2.14.1"></a> +## [v2.14.1] - 2023-04-21 +## Bug Fixes + +* **batch:** resolve use of ValidationError in batch ([#2157](https://github.com/aws-powertools/powertools-lambda-python/issues/2157)) +* **e2e:** fix test brittleness ([#2152](https://github.com/aws-powertools/powertools-lambda-python/issues/2152)) + +## Documentation + +* **readme:** update python version badge to 3.10 + +## Features + +* **event_sources:** add queue_url field in SQS EventSource DataClass ([#2146](https://github.com/aws-powertools/powertools-lambda-python/issues/2146)) + +## Maintenance + +* update v2 layer ARN on documentation +* add Python 3.10 PyPi language classifier ([#2144](https://github.com/aws-powertools/powertools-lambda-python/issues/2144)) +* update v2 layer ARN on documentation +* **batch:** safeguard custom use of BatchProcessingError exception ([#2155](https://github.com/aws-powertools/powertools-lambda-python/issues/2155)) +* **deps:** bump codecov/codecov-action from 3.1.2 to 3.1.3 ([#2153](https://github.com/aws-powertools/powertools-lambda-python/issues/2153)) +* **deps:** bump dependabot/fetch-metadata from 1.3.6 to 1.4.0 ([#2140](https://github.com/aws-powertools/powertools-lambda-python/issues/2140)) +* **deps-dev:** bump aws-cdk from 2.75.0 to 2.75.1 ([#2150](https://github.com/aws-powertools/powertools-lambda-python/issues/2150)) +* **deps-dev:** bump aws-cdk from 2.75.1 to 2.76.0 ([#2154](https://github.com/aws-powertools/powertools-lambda-python/issues/2154)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.26.89 to 1.26.116 ([#2147](https://github.com/aws-powertools/powertools-lambda-python/issues/2147)) +* **deps-dev:** bump importlib-metadata from 6.4.1 to 6.5.0 ([#2141](https://github.com/aws-powertools/powertools-lambda-python/issues/2141)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.104 to 1.26.116 ([#2149](https://github.com/aws-powertools/powertools-lambda-python/issues/2149)) +* **deps-dev:** bump filelock from 3.11.0 to 3.12.0 ([#2142](https://github.com/aws-powertools/powertools-lambda-python/issues/2142)) +* **deps-dev:** bump cfn-lint from 0.77.1 to 0.77.2 ([#2148](https://github.com/aws-powertools/powertools-lambda-python/issues/2148)) + + +<a name="v2.14.0"></a> +## [v2.14.0] - 2023-04-18 +## Bug Fixes + +* enable python 3.10 on SAR template +* **ci:** fix layer version in tracer, logger and metrics +* **ci:** typo +* **docs:** add Layer ARN for new 5 regions +* **layers:** add debug to update layer arn script + +## Features + +* **runtime:** add support for python 3.10 ([#2137](https://github.com/aws-powertools/powertools-lambda-python/issues/2137)) + +## Maintenance + +* update v2 layer ARN on documentation +* update v2 layer ARN on documentation +* update v2 layer ARN on documentation +* **ci:** add support for x86-64 regions only ([#2122](https://github.com/aws-powertools/powertools-lambda-python/issues/2122)) +* **deps-dev:** bump importlib-metadata from 6.3.0 to 6.4.1 ([#2134](https://github.com/aws-powertools/powertools-lambda-python/issues/2134)) +* **deps-dev:** bump cfn-lint from 0.77.0 to 0.77.1 ([#2133](https://github.com/aws-powertools/powertools-lambda-python/issues/2133)) +* **deps-dev:** bump pytest from 7.3.0 to 7.3.1 ([#2127](https://github.com/aws-powertools/powertools-lambda-python/issues/2127)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.109 to 1.26.114 ([#2126](https://github.com/aws-powertools/powertools-lambda-python/issues/2126)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.114 to 1.26.115 ([#2135](https://github.com/aws-powertools/powertools-lambda-python/issues/2135)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.97.post1 to 1.26.115 ([#2132](https://github.com/aws-powertools/powertools-lambda-python/issues/2132)) +* **github:** new tech debt issue form ([#2131](https://github.com/aws-powertools/powertools-lambda-python/issues/2131)) +* **layer:** change layer-balance script to support new regions + +## Reverts +* chore: update v2 layer ARN on documentation + + +<a name="v2.13.0"></a> +## [v2.13.0] - 2023-04-14 +## Bug Fixes + +* **ci:** replace the correct files for Layer ARN +* **ci:** fix working directory +* **ci:** add debug log to NPM install +* **ci:** use project's CDK version when building layers +* **ci:** add the rest of the changed docs +* **ci:** update layer version on logger, tracer and metrics docs ([#2120](https://github.com/aws-powertools/powertools-lambda-python/issues/2120)) +* **event_sources:** Update CodePipeline event source to include optional encryption_key field and make user_parameters field optional ([#2113](https://github.com/aws-powertools/powertools-lambda-python/issues/2113)) + +## Features + +* **parameters:** Configure max_age and decrypt parameters via environment variables ([#2088](https://github.com/aws-powertools/powertools-lambda-python/issues/2088)) + +## Maintenance + +* update v2 layer ARN on documentation +* **ci:** bump the cdk-aws-lambda-powertools-layer version ([#2121](https://github.com/aws-powertools/powertools-lambda-python/issues/2121)) +* **deps:** bump codecov/codecov-action from 3.1.1 to 3.1.2 ([#2110](https://github.com/aws-powertools/powertools-lambda-python/issues/2110)) +* **deps-dev:** bump httpx from 0.23.3 to 0.24.0 ([#2111](https://github.com/aws-powertools/powertools-lambda-python/issues/2111)) +* **deps-dev:** bump aws-cdk-lib from 2.73.0 to 2.74.0 ([#2123](https://github.com/aws-powertools/powertools-lambda-python/issues/2123)) +* **deps-dev:** bump mkdocs-material from 9.1.5 to 9.1.6 ([#2104](https://github.com/aws-powertools/powertools-lambda-python/issues/2104)) +* **deps-dev:** bump aws-cdk from 2.73.0 to 2.74.0 ([#2125](https://github.com/aws-powertools/powertools-lambda-python/issues/2125)) +* **deps-dev:** bump flake8-comprehensions from 3.11.1 to 3.12.0 ([#2124](https://github.com/aws-powertools/powertools-lambda-python/issues/2124)) +* **deps-dev:** bump mypy from 1.1.1 to 1.2.0 ([#2096](https://github.com/aws-powertools/powertools-lambda-python/issues/2096)) +* **deps-dev:** bump cfn-lint from 0.76.2 to 0.77.0 ([#2107](https://github.com/aws-powertools/powertools-lambda-python/issues/2107)) +* **deps-dev:** bump pytest from 7.2.2 to 7.3.0 ([#2106](https://github.com/aws-powertools/powertools-lambda-python/issues/2106)) +* **deps-dev:** bump importlib-metadata from 6.1.0 to 6.3.0 ([#2105](https://github.com/aws-powertools/powertools-lambda-python/issues/2105)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.80 to 1.26.109 ([#2103](https://github.com/aws-powertools/powertools-lambda-python/issues/2103)) +* **maintenance:** validate acknowledgement section is present ([#2112](https://github.com/aws-powertools/powertools-lambda-python/issues/2112)) + + +<a name="v2.12.0"></a> +## [v2.12.0] - 2023-04-07 +## Bug Fixes + +* **batch:** handle early validation errors for pydantic models (poison pill) [#2091](https://github.com/aws-powertools/powertools-lambda-python/issues/2091) ([#2099](https://github.com/aws-powertools/powertools-lambda-python/issues/2099)) + +## Documentation + +* **batch:** use newly supported Json model ([#2100](https://github.com/aws-powertools/powertools-lambda-python/issues/2100)) +* **homepage:** remove banner for end-of-support v1 ([#2098](https://github.com/aws-powertools/powertools-lambda-python/issues/2098)) +* **idempotency:** fixes to testing your code section ([#2073](https://github.com/aws-powertools/powertools-lambda-python/issues/2073)) +* **idempotency:** new sequence diagrams, fix idempotency record vs DynamoDB TTL confusion ([#2074](https://github.com/aws-powertools/powertools-lambda-python/issues/2074)) +* **parser:** fix highlighted line ([#2064](https://github.com/aws-powertools/powertools-lambda-python/issues/2064)) + +## Features + +* **batch:** reduce boilerplate with process_partial_response ([#2090](https://github.com/aws-powertools/powertools-lambda-python/issues/2090)) +* **idempotency:** allow custom sdk clients in DynamoDBPersistenceLayer ([#2087](https://github.com/aws-powertools/powertools-lambda-python/issues/2087)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump peaceiris/actions-gh-pages from 3.9.2 to 3.9.3 ([#2069](https://github.com/aws-powertools/powertools-lambda-python/issues/2069)) +* **deps:** bump aws-xray-sdk from 2.11.0 to 2.12.0 ([#2080](https://github.com/aws-powertools/powertools-lambda-python/issues/2080)) +* **deps-dev:** bump coverage from 7.2.2 to 7.2.3 ([#2092](https://github.com/aws-powertools/powertools-lambda-python/issues/2092)) +* **deps-dev:** bump aws-cdk from 2.72.1 to 2.73.0 ([#2093](https://github.com/aws-powertools/powertools-lambda-python/issues/2093)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.60 to 1.26.108 ([#2095](https://github.com/aws-powertools/powertools-lambda-python/issues/2095)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.11 to 2.8.19.12 ([#2085](https://github.com/aws-powertools/powertools-lambda-python/issues/2085)) +* **deps-dev:** bump cfn-lint from 0.76.1 to 0.76.2 ([#2084](https://github.com/aws-powertools/powertools-lambda-python/issues/2084)) +* **deps-dev:** bump aws-cdk from 2.72.0 to 2.72.1 ([#2081](https://github.com/aws-powertools/powertools-lambda-python/issues/2081)) +* **deps-dev:** bump filelock from 3.10.7 to 3.11.0 ([#2094](https://github.com/aws-powertools/powertools-lambda-python/issues/2094)) +* **deps-dev:** bump mkdocs-material from 9.1.4 to 9.1.5 ([#2077](https://github.com/aws-powertools/powertools-lambda-python/issues/2077)) +* **deps-dev:** bump aws-cdk-lib from 2.72.0 to 2.72.1 ([#2076](https://github.com/aws-powertools/powertools-lambda-python/issues/2076)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.99 to 1.26.104 ([#2075](https://github.com/aws-powertools/powertools-lambda-python/issues/2075)) +* **deps-dev:** bump aws-cdk from 2.71.0 to 2.72.0 ([#2071](https://github.com/aws-powertools/powertools-lambda-python/issues/2071)) +* **deps-dev:** bump aws-cdk-lib from 2.72.1 to 2.73.0 ([#2097](https://github.com/aws-powertools/powertools-lambda-python/issues/2097)) +* **deps-dev:** bump aws-cdk-lib from 2.71.0 to 2.72.0 ([#2070](https://github.com/aws-powertools/powertools-lambda-python/issues/2070)) +* **deps-dev:** bump black from 23.1.0 to 23.3.0 ([#2066](https://github.com/aws-powertools/powertools-lambda-python/issues/2066)) +* **deps-dev:** bump aws-cdk from 2.70.0 to 2.71.0 ([#2067](https://github.com/aws-powertools/powertools-lambda-python/issues/2067)) +* **deps-dev:** bump aws-cdk-lib from 2.70.0 to 2.71.0 ([#2065](https://github.com/aws-powertools/powertools-lambda-python/issues/2065)) + + +<a name="v2.11.0"></a> +## [v2.11.0] - 2023-03-29 +## Bug Fixes + +* **feature_flags:** make test conditions deterministic ([#2059](https://github.com/aws-powertools/powertools-lambda-python/issues/2059)) +* **feature_flags:** handle expected falsy values in conditions ([#2052](https://github.com/aws-powertools/powertools-lambda-python/issues/2052)) + +## Documentation + +* **logger:** warn append_keys on not being thread-safe ([#2046](https://github.com/aws-powertools/powertools-lambda-python/issues/2046)) + +## Features + +* **event_sources:** support for S3 Event Notifications through EventBridge ([#2024](https://github.com/aws-powertools/powertools-lambda-python/issues/2024)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump pydantic from 1.10.6 to 1.10.7 ([#2034](https://github.com/aws-powertools/powertools-lambda-python/issues/2034)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.97 to 1.26.97.post2 ([#2043](https://github.com/aws-powertools/powertools-lambda-python/issues/2043)) +* **deps-dev:** bump cfn-lint from 0.75.1 to 0.76.1 ([#2056](https://github.com/aws-powertools/powertools-lambda-python/issues/2056)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.10 to 2.8.19.11 ([#2057](https://github.com/aws-powertools/powertools-lambda-python/issues/2057)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.97.post2 to 1.26.99 ([#2054](https://github.com/aws-powertools/powertools-lambda-python/issues/2054)) +* **deps-dev:** bump mkdocs-material from 9.1.3 to 9.1.4 ([#2050](https://github.com/aws-powertools/powertools-lambda-python/issues/2050)) +* **deps-dev:** bump filelock from 3.10.2 to 3.10.4 ([#2048](https://github.com/aws-powertools/powertools-lambda-python/issues/2048)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.26.52 to 1.26.99 ([#2049](https://github.com/aws-powertools/powertools-lambda-python/issues/2049)) +* **deps-dev:** bump filelock from 3.10.1 to 3.10.2 ([#2045](https://github.com/aws-powertools/powertools-lambda-python/issues/2045)) +* **deps-dev:** bump types-requests from 2.28.11.15 to 2.28.11.16 ([#2044](https://github.com/aws-powertools/powertools-lambda-python/issues/2044)) +* **deps-dev:** bump filelock from 3.10.4 to 3.10.7 ([#2055](https://github.com/aws-powertools/powertools-lambda-python/issues/2055)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.97 to 1.26.97.post1 ([#2042](https://github.com/aws-powertools/powertools-lambda-python/issues/2042)) +* **deps-dev:** bump filelock from 3.10.0 to 3.10.1 ([#2036](https://github.com/aws-powertools/powertools-lambda-python/issues/2036)) +* **deps-dev:** bump aws-cdk from 2.69.0 to 2.70.0 ([#2039](https://github.com/aws-powertools/powertools-lambda-python/issues/2039)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.87 to 1.26.97 ([#2035](https://github.com/aws-powertools/powertools-lambda-python/issues/2035)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.62 to 1.26.97 ([#2037](https://github.com/aws-powertools/powertools-lambda-python/issues/2037)) +* **deps-dev:** bump aws-cdk-lib from 2.69.0 to 2.70.0 ([#2038](https://github.com/aws-powertools/powertools-lambda-python/issues/2038)) +* **deps-dev:** bump types-requests from 2.28.11.16 to 2.28.11.17 ([#2061](https://github.com/aws-powertools/powertools-lambda-python/issues/2061)) +* **deps-dev:** bump mypy-boto3-ssm from 1.26.77 to 1.26.97 ([#2033](https://github.com/aws-powertools/powertools-lambda-python/issues/2033)) +* **deps-dev:** bump flake8-comprehensions from 3.11.0 to 3.11.1 ([#2029](https://github.com/aws-powertools/powertools-lambda-python/issues/2029)) +* **deps-dev:** bump cfn-lint from 0.75.0 to 0.75.1 ([#2027](https://github.com/aws-powertools/powertools-lambda-python/issues/2027)) +* **deps-dev:** bump pytest-asyncio from 0.20.3 to 0.21.0 ([#2026](https://github.com/aws-powertools/powertools-lambda-python/issues/2026)) + + +<a name="v2.10.0"></a> +## [v2.10.0] - 2023-03-17 +## Bug Fixes + +* only allow one e2e test at a time +* **build:** auto-generate setup.py for legacy build tools ([#2013](https://github.com/aws-powertools/powertools-lambda-python/issues/2013)) +* **ci:** bump CDK version +* **typing:** swap NoReturn with None for methods with no return value ([#2004](https://github.com/aws-powertools/powertools-lambda-python/issues/2004)) + +## Documentation + +* **homepage:** revamp install UX & share how we build Lambda Layer ([#1978](https://github.com/aws-powertools/powertools-lambda-python/issues/1978)) +* **metrics:** fix high-resolution metrics announcement link ([#2017](https://github.com/aws-powertools/powertools-lambda-python/issues/2017)) + +## Features + +* **event_sources:** support for custom properties in ActiveMQEvent ([#1999](https://github.com/aws-powertools/powertools-lambda-python/issues/1999)) +* **parser:** support for S3 Event Notifications via EventBridge ([#1982](https://github.com/aws-powertools/powertools-lambda-python/issues/1982)) + +## Maintenance + +* update v2 layer ARN on documentation +* **ci:** allow dependabot to upgrade CDK for JS +* **deps:** bump docker/setup-buildx-action from 2.4.1 to 2.5.0 ([#1995](https://github.com/aws-powertools/powertools-lambda-python/issues/1995)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.1.1 to 2.1.2 ([#1979](https://github.com/aws-powertools/powertools-lambda-python/issues/1979)) +* **deps:** bump aws-actions/configure-aws-credentials from 1 to 2 ([#1987](https://github.com/aws-powertools/powertools-lambda-python/issues/1987)) +* **deps:** bump pydantic from 1.10.5 to 1.10.6 ([#1991](https://github.com/aws-powertools/powertools-lambda-python/issues/1991)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.26.49 to 1.26.89 ([#1996](https://github.com/aws-powertools/powertools-lambda-python/issues/1996)) +* **deps-dev:** bump cfn-lint from 0.74.2 to 0.74.3 ([#2008](https://github.com/aws-powertools/powertools-lambda-python/issues/2008)) +* **deps-dev:** bump filelock from 3.9.0 to 3.9.1 ([#2006](https://github.com/aws-powertools/powertools-lambda-python/issues/2006)) +* **deps-dev:** bump aws-cdk-lib from 2.68.0 to 2.69.0 ([#2007](https://github.com/aws-powertools/powertools-lambda-python/issues/2007)) +* **deps-dev:** bump cfn-lint from 0.74.1 to 0.74.2 ([#2005](https://github.com/aws-powertools/powertools-lambda-python/issues/2005)) +* **deps-dev:** bump mypy from 0.982 to 1.1.1 ([#1985](https://github.com/aws-powertools/powertools-lambda-python/issues/1985)) +* **deps-dev:** bump pytest-xdist from 3.2.0 to 3.2.1 ([#2000](https://github.com/aws-powertools/powertools-lambda-python/issues/2000)) +* **deps-dev:** bump flake8-bugbear from 23.2.13 to 23.3.12 ([#2001](https://github.com/aws-powertools/powertools-lambda-python/issues/2001)) +* **deps-dev:** bump bandit from 1.7.4 to 1.7.5 ([#1997](https://github.com/aws-powertools/powertools-lambda-python/issues/1997)) +* **deps-dev:** bump mkdocs-material from 9.1.2 to 9.1.3 ([#2009](https://github.com/aws-powertools/powertools-lambda-python/issues/2009)) +* **deps-dev:** bump aws-cdk from 2.67.0 to 2.69.0 ([#2010](https://github.com/aws-powertools/powertools-lambda-python/issues/2010)) +* **deps-dev:** bump mkdocs-material from 9.1.1 to 9.1.2 ([#1994](https://github.com/aws-powertools/powertools-lambda-python/issues/1994)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.84 to 1.26.87 ([#1993](https://github.com/aws-powertools/powertools-lambda-python/issues/1993)) +* **deps-dev:** bump filelock from 3.9.1 to 3.10.0 ([#2019](https://github.com/aws-powertools/powertools-lambda-python/issues/2019)) +* **deps-dev:** bump aws-cdk-lib from 2.67.0 to 2.68.0 ([#1992](https://github.com/aws-powertools/powertools-lambda-python/issues/1992)) +* **deps-dev:** bump cfn-lint from 0.74.0 to 0.74.1 ([#1988](https://github.com/aws-powertools/powertools-lambda-python/issues/1988)) +* **deps-dev:** bump coverage from 7.2.1 to 7.2.2 ([#2021](https://github.com/aws-powertools/powertools-lambda-python/issues/2021)) +* **deps-dev:** bump pytest from 7.2.1 to 7.2.2 ([#1980](https://github.com/aws-powertools/powertools-lambda-python/issues/1980)) +* **deps-dev:** bump cfn-lint from 0.74.3 to 0.75.0 ([#2020](https://github.com/aws-powertools/powertools-lambda-python/issues/2020)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.9 to 2.8.19.10 ([#1973](https://github.com/aws-powertools/powertools-lambda-python/issues/1973)) +* **deps-dev:** bump hvac from 1.0.2 to 1.1.0 ([#1983](https://github.com/aws-powertools/powertools-lambda-python/issues/1983)) +* **deps-dev:** bump mkdocs-material from 9.1.0 to 9.1.1 ([#1984](https://github.com/aws-powertools/powertools-lambda-python/issues/1984)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.24 to 1.26.84 ([#1981](https://github.com/aws-powertools/powertools-lambda-python/issues/1981)) +* **deps-dev:** bump mkdocs-material from 9.0.15 to 9.1.0 ([#1976](https://github.com/aws-powertools/powertools-lambda-python/issues/1976)) +* **deps-dev:** bump cfn-lint from 0.67.0 to 0.74.0 ([#1974](https://github.com/aws-powertools/powertools-lambda-python/issues/1974)) +* **deps-dev:** bump aws-cdk-lib from 2.66.1 to 2.67.0 ([#1977](https://github.com/aws-powertools/powertools-lambda-python/issues/1977)) + + +<a name="v2.9.1"></a> +## [v2.9.1] - 2023-03-01 +## Bug Fixes + +* **idempotency:** revert dict mutation that impacted static_pk_value feature ([#1970](https://github.com/aws-powertools/powertools-lambda-python/issues/1970)) + +## Documentation + +* **appsync:** add mutation example and infrastructure fix ([#1964](https://github.com/aws-powertools/powertools-lambda-python/issues/1964)) +* **parameters:** fix typos and inconsistencies ([#1966](https://github.com/aws-powertools/powertools-lambda-python/issues/1966)) + +## Maintenance + +* update project description +* update v2 layer ARN on documentation +* **ci:** disable pypi test due to maintenance mode +* **ci:** replace deprecated set-output commands ([#1957](https://github.com/aws-powertools/powertools-lambda-python/issues/1957)) +* **deps:** bump fastjsonschema from 2.16.2 to 2.16.3 ([#1961](https://github.com/aws-powertools/powertools-lambda-python/issues/1961)) +* **deps:** bump release-drafter/release-drafter from 5.22.0 to 5.23.0 ([#1947](https://github.com/aws-powertools/powertools-lambda-python/issues/1947)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.1.0 to 2.1.1 ([#1958](https://github.com/aws-powertools/powertools-lambda-python/issues/1958)) +* **deps-dev:** bump coverage from 7.2.0 to 7.2.1 ([#1963](https://github.com/aws-powertools/powertools-lambda-python/issues/1963)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.8 to 2.8.19.9 ([#1960](https://github.com/aws-powertools/powertools-lambda-python/issues/1960)) +* **deps-dev:** bump mkdocs-material from 9.0.14 to 9.0.15 ([#1959](https://github.com/aws-powertools/powertools-lambda-python/issues/1959)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.55 to 1.26.80 ([#1967](https://github.com/aws-powertools/powertools-lambda-python/issues/1967)) +* **deps-dev:** bump types-requests from 2.28.11.14 to 2.28.11.15 ([#1962](https://github.com/aws-powertools/powertools-lambda-python/issues/1962)) +* **deps-dev:** bump aws-cdk-lib from 2.66.0 to 2.66.1 ([#1954](https://github.com/aws-powertools/powertools-lambda-python/issues/1954)) +* **deps-dev:** bump coverage from 7.1.0 to 7.2.0 ([#1951](https://github.com/aws-powertools/powertools-lambda-python/issues/1951)) +* **deps-dev:** bump mkdocs-material from 9.0.13 to 9.0.14 ([#1952](https://github.com/aws-powertools/powertools-lambda-python/issues/1952)) +* **deps-dev:** bump mypy-boto3-ssm from 1.26.43 to 1.26.77 ([#1949](https://github.com/aws-powertools/powertools-lambda-python/issues/1949)) +* **deps-dev:** bump types-requests from 2.28.11.13 to 2.28.11.14 ([#1946](https://github.com/aws-powertools/powertools-lambda-python/issues/1946)) +* **deps-dev:** bump aws-cdk-lib from 2.65.0 to 2.66.0 ([#1948](https://github.com/aws-powertools/powertools-lambda-python/issues/1948)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.7 to 2.8.19.8 ([#1945](https://github.com/aws-powertools/powertools-lambda-python/issues/1945)) +* **parser:** add workaround to make API GW test button work ([#1971](https://github.com/aws-powertools/powertools-lambda-python/issues/1971)) + + +<a name="v2.9.0"></a> +## [v2.9.0] - 2023-02-21 +## Bug Fixes + +* **ci:** upgraded cdk to match the version used on e2e tests +* **feature-flags:** revert RuleAction Enum inheritance on str ([#1910](https://github.com/aws-powertools/powertools-lambda-python/issues/1910)) +* **logger:** support exception and exception_name fields at any log level ([#1930](https://github.com/aws-powertools/powertools-lambda-python/issues/1930)) +* **metrics:** clarify no-metrics user warning ([#1935](https://github.com/aws-powertools/powertools-lambda-python/issues/1935)) + +## Documentation + +* **event_handlers:** Fix REST API - HTTP Methods documentation ([#1936](https://github.com/aws-powertools/powertools-lambda-python/issues/1936)) +* **home:** update powertools definition +* **we-made-this:** add CI/CD using Feature Flags video ([#1940](https://github.com/aws-powertools/powertools-lambda-python/issues/1940)) +* **we-made-this:** add Feature Flags post ([#1939](https://github.com/aws-powertools/powertools-lambda-python/issues/1939)) + +## Features + +* **batch:** add support to SQS FIFO queues (SqsFifoPartialProcessor) ([#1934](https://github.com/aws-powertools/powertools-lambda-python/issues/1934)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.0.5 to 2.1.0 ([#1943](https://github.com/aws-powertools/powertools-lambda-python/issues/1943)) +* **deps:** bump pydantic from 1.10.4 to 1.10.5 ([#1931](https://github.com/aws-powertools/powertools-lambda-python/issues/1931)) +* **deps-dev:** bump mkdocs-material from 9.0.12 to 9.0.13 ([#1944](https://github.com/aws-powertools/powertools-lambda-python/issues/1944)) +* **deps-dev:** bump aws-cdk-lib from 2.64.0 to 2.65.0 ([#1938](https://github.com/aws-powertools/powertools-lambda-python/issues/1938)) +* **deps-dev:** bump types-python-dateutil from 2.8.19.6 to 2.8.19.7 ([#1932](https://github.com/aws-powertools/powertools-lambda-python/issues/1932)) +* **deps-dev:** bump types-requests from 2.28.11.12 to 2.28.11.13 ([#1933](https://github.com/aws-powertools/powertools-lambda-python/issues/1933)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.26.63 to 1.26.71 ([#1928](https://github.com/aws-powertools/powertools-lambda-python/issues/1928)) +* **deps-dev:** bump flake8-bugbear from 23.1.20 to 23.2.13 ([#1924](https://github.com/aws-powertools/powertools-lambda-python/issues/1924)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.26.0.post1 to 1.26.70 ([#1925](https://github.com/aws-powertools/powertools-lambda-python/issues/1925)) + + +<a name="v2.8.0"></a> +## [v2.8.0] - 2023-02-10 +## Bug Fixes + +* **idempotency:** make idempotent_function decorator thread safe ([#1899](https://github.com/aws-powertools/powertools-lambda-python/issues/1899)) + +## Documentation + +* **engine:** re-enable clipboard button for code snippets +* **homepage:** Replace poetry command to add group parameter ([#1917](https://github.com/aws-powertools/powertools-lambda-python/issues/1917)) +* **homepage:** set url for end-of-support in announce block ([#1893](https://github.com/aws-powertools/powertools-lambda-python/issues/1893)) +* **idempotency:** add IAM permissions section ([#1902](https://github.com/aws-powertools/powertools-lambda-python/issues/1902)) +* **metrics:** remove reduntant wording before release +* **metrics:** fix syntax highlighting for new default_dimensions + +## Features + +* **batch:** add async_batch_processor for concurrent processing ([#1724](https://github.com/aws-powertools/powertools-lambda-python/issues/1724)) +* **metrics:** add default_dimensions to single_metric ([#1880](https://github.com/aws-powertools/powertools-lambda-python/issues/1880)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump docker/setup-buildx-action from 2.4.0 to 2.4.1 ([#1903](https://github.com/aws-powertools/powertools-lambda-python/issues/1903)) +* **deps-dev:** bump aws-cdk-lib from 2.63.0 to 2.63.2 ([#1904](https://github.com/aws-powertools/powertools-lambda-python/issues/1904)) +* **deps-dev:** bump black from 22.12.0 to 23.1.0 ([#1886](https://github.com/aws-powertools/powertools-lambda-python/issues/1886)) +* **deps-dev:** bump types-requests from 2.28.11.8 to 2.28.11.12 ([#1906](https://github.com/aws-powertools/powertools-lambda-python/issues/1906)) +* **deps-dev:** bump pytest-xdist from 3.1.0 to 3.2.0 ([#1905](https://github.com/aws-powertools/powertools-lambda-python/issues/1905)) +* **deps-dev:** bump aws-cdk-lib from 2.63.2 to 2.64.0 ([#1918](https://github.com/aws-powertools/powertools-lambda-python/issues/1918)) +* **deps-dev:** bump mkdocs-material from 9.0.11 to 9.0.12 ([#1919](https://github.com/aws-powertools/powertools-lambda-python/issues/1919)) +* **deps-dev:** bump mkdocs-material from 9.0.10 to 9.0.11 ([#1896](https://github.com/aws-powertools/powertools-lambda-python/issues/1896)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.26.0.post1 to 1.26.63 ([#1895](https://github.com/aws-powertools/powertools-lambda-python/issues/1895)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.58 to 1.26.62 ([#1889](https://github.com/aws-powertools/powertools-lambda-python/issues/1889)) +* **deps-dev:** bump mkdocs-material from 9.0.9 to 9.0.10 ([#1888](https://github.com/aws-powertools/powertools-lambda-python/issues/1888)) +* **deps-dev:** bump aws-cdk-lib from 2.62.2 to 2.63.0 ([#1887](https://github.com/aws-powertools/powertools-lambda-python/issues/1887)) +* **maintainers:** fix release workflow rename +* **pypi:** add new links to Pypi package homepage ([#1912](https://github.com/aws-powertools/powertools-lambda-python/issues/1912)) + + +<a name="v2.7.1"></a> +## [v2.7.1] - 2023-02-01 +## Bug Fixes + +* parallel_run should fail when e2e tests fail +* bump aws-cdk version +* **ci:** scope e2e tests by python version +* **ci:** add auth to API HTTP Gateway and Lambda Function Url ([#1882](https://github.com/aws-powertools/powertools-lambda-python/issues/1882)) +* **license:** correction to MIT + MIT-0 (no proprietary anymore) ([#1883](https://github.com/aws-powertools/powertools-lambda-python/issues/1883)) +* **license:** add MIT-0 license header ([#1871](https://github.com/aws-powertools/powertools-lambda-python/issues/1871)) +* **tests:** make logs fetching more robust ([#1878](https://github.com/aws-powertools/powertools-lambda-python/issues/1878)) +* **tests:** remove custom workers +* **tests:** make sure multiple e2e tests run concurrently ([#1861](https://github.com/aws-powertools/powertools-lambda-python/issues/1861)) + +## Documentation + +* **event-source:** fix incorrect method in example CloudWatch Logs ([#1857](https://github.com/aws-powertools/powertools-lambda-python/issues/1857)) +* **homepage:** add banner for end-of-support v1 ([#1879](https://github.com/aws-powertools/powertools-lambda-python/issues/1879)) +* **parameters:** snippets split, improved, and lint ([#1564](https://github.com/aws-powertools/powertools-lambda-python/issues/1564)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump docker/setup-buildx-action from 2.0.0 to 2.4.0 ([#1873](https://github.com/aws-powertools/powertools-lambda-python/issues/1873)) +* **deps:** bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 ([#1855](https://github.com/aws-powertools/powertools-lambda-python/issues/1855)) +* **deps-dev:** bump mypy-boto3-s3 from 1.26.0.post1 to 1.26.58 ([#1868](https://github.com/aws-powertools/powertools-lambda-python/issues/1868)) +* **deps-dev:** bump isort from 5.11.4 to 5.11.5 ([#1875](https://github.com/aws-powertools/powertools-lambda-python/issues/1875)) +* **deps-dev:** bump aws-cdk-lib from 2.62.1 to 2.62.2 ([#1869](https://github.com/aws-powertools/powertools-lambda-python/issues/1869)) +* **deps-dev:** bump mkdocs-material from 9.0.6 to 9.0.8 ([#1874](https://github.com/aws-powertools/powertools-lambda-python/issues/1874)) +* **deps-dev:** bump aws-cdk-lib from 2.62.0 to 2.62.1 ([#1866](https://github.com/aws-powertools/powertools-lambda-python/issues/1866)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.35.post1 to 1.26.57 ([#1865](https://github.com/aws-powertools/powertools-lambda-python/issues/1865)) +* **deps-dev:** bump coverage from 7.0.5 to 7.1.0 ([#1862](https://github.com/aws-powertools/powertools-lambda-python/issues/1862)) +* **deps-dev:** bump aws-cdk-lib from 2.61.1 to 2.62.0 ([#1863](https://github.com/aws-powertools/powertools-lambda-python/issues/1863)) +* **deps-dev:** bump flake8-bugbear from 22.12.6 to 23.1.20 ([#1854](https://github.com/aws-powertools/powertools-lambda-python/issues/1854)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.49 to 1.26.55 ([#1856](https://github.com/aws-powertools/powertools-lambda-python/issues/1856)) + +## Reverts +* fix(tests): remove custom workers + + +<a name="v2.7.0"></a> +## [v2.7.0] - 2023-01-24 +## Bug Fixes + +* git-chlg docker image is broken + +## Features + +* **feature_flags:** Add Time based feature flags actions ([#1846](https://github.com/aws-powertools/powertools-lambda-python/issues/1846)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump peaceiris/actions-gh-pages from 3.9.1 to 3.9.2 ([#1841](https://github.com/aws-powertools/powertools-lambda-python/issues/1841)) +* **deps:** bump future from 0.18.2 to 0.18.3 ([#1836](https://github.com/aws-powertools/powertools-lambda-python/issues/1836)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.0.4 to 2.0.5 ([#1837](https://github.com/aws-powertools/powertools-lambda-python/issues/1837)) +* **deps-dev:** bump mkdocs-material from 9.0.4 to 9.0.5 ([#1840](https://github.com/aws-powertools/powertools-lambda-python/issues/1840)) +* **deps-dev:** bump types-requests from 2.28.11.7 to 2.28.11.8 ([#1843](https://github.com/aws-powertools/powertools-lambda-python/issues/1843)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.26.30 to 1.26.52 ([#1847](https://github.com/aws-powertools/powertools-lambda-python/issues/1847)) +* **deps-dev:** bump pytest from 7.2.0 to 7.2.1 ([#1838](https://github.com/aws-powertools/powertools-lambda-python/issues/1838)) +* **deps-dev:** bump aws-cdk-lib from 2.60.0 to 2.61.1 ([#1849](https://github.com/aws-powertools/powertools-lambda-python/issues/1849)) +* **deps-dev:** bump mypy-boto3-logs from 1.26.49 to 1.26.53 ([#1850](https://github.com/aws-powertools/powertools-lambda-python/issues/1850)) +* **deps-dev:** bump mkdocs-material from 9.0.5 to 9.0.6 ([#1851](https://github.com/aws-powertools/powertools-lambda-python/issues/1851)) +* **deps-dev:** bump mkdocs-material from 9.0.3 to 9.0.4 ([#1833](https://github.com/aws-powertools/powertools-lambda-python/issues/1833)) +* **deps-dev:** bump mypy-boto3-logs from 1.26.43 to 1.26.49 ([#1834](https://github.com/aws-powertools/powertools-lambda-python/issues/1834)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.26.40 to 1.26.49 ([#1835](https://github.com/aws-powertools/powertools-lambda-python/issues/1835)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.18 to 1.26.49 ([#1832](https://github.com/aws-powertools/powertools-lambda-python/issues/1832)) +* **deps-dev:** bump aws-cdk-lib from 2.59.0 to 2.60.0 ([#1831](https://github.com/aws-powertools/powertools-lambda-python/issues/1831)) + + +<a name="v2.6.0"></a> +## [v2.6.0] - 2023-01-12 +## Bug Fixes + +* **api_gateway:** fixed custom metrics issue when using debug mode ([#1827](https://github.com/aws-powertools/powertools-lambda-python/issues/1827)) + +## Documentation + +* **logger:** fix incorrect field names in example structured logs ([#1830](https://github.com/aws-powertools/powertools-lambda-python/issues/1830)) +* **logger:** Add warning of uncaught exceptions ([#1826](https://github.com/aws-powertools/powertools-lambda-python/issues/1826)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump pydantic from 1.10.2 to 1.10.4 ([#1817](https://github.com/aws-powertools/powertools-lambda-python/issues/1817)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.0.1 to 2.0.3 ([#1801](https://github.com/aws-powertools/powertools-lambda-python/issues/1801)) +* **deps:** bump release-drafter/release-drafter from 5.21.1 to 5.22.0 ([#1802](https://github.com/aws-powertools/powertools-lambda-python/issues/1802)) +* **deps:** bump gitpython from 3.1.29 to 3.1.30 ([#1812](https://github.com/aws-powertools/powertools-lambda-python/issues/1812)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.0.3 to 2.0.4 ([#1821](https://github.com/aws-powertools/powertools-lambda-python/issues/1821)) +* **deps:** bump peaceiris/actions-gh-pages from 3.9.0 to 3.9.1 ([#1814](https://github.com/aws-powertools/powertools-lambda-python/issues/1814)) +* **deps-dev:** bump mkdocs-material from 8.5.11 to 9.0.2 ([#1808](https://github.com/aws-powertools/powertools-lambda-python/issues/1808)) +* **deps-dev:** bump mypy-boto3-ssm from 1.26.11.post1 to 1.26.43 ([#1819](https://github.com/aws-powertools/powertools-lambda-python/issues/1819)) +* **deps-dev:** bump mypy-boto3-logs from 1.26.27 to 1.26.43 ([#1820](https://github.com/aws-powertools/powertools-lambda-python/issues/1820)) +* **deps-dev:** bump filelock from 3.8.2 to 3.9.0 ([#1816](https://github.com/aws-powertools/powertools-lambda-python/issues/1816)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.11.post1 to 1.26.35.post1 ([#1818](https://github.com/aws-powertools/powertools-lambda-python/issues/1818)) +* **deps-dev:** bump ijson from 3.1.4 to 3.2.0.post0 ([#1815](https://github.com/aws-powertools/powertools-lambda-python/issues/1815)) +* **deps-dev:** bump coverage from 6.5.0 to 7.0.3 ([#1806](https://github.com/aws-powertools/powertools-lambda-python/issues/1806)) +* **deps-dev:** bump flake8-builtins from 2.0.1 to 2.1.0 ([#1799](https://github.com/aws-powertools/powertools-lambda-python/issues/1799)) +* **deps-dev:** bump coverage from 7.0.3 to 7.0.4 ([#1822](https://github.com/aws-powertools/powertools-lambda-python/issues/1822)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.26.12 to 1.26.40 ([#1811](https://github.com/aws-powertools/powertools-lambda-python/issues/1811)) +* **deps-dev:** bump isort from 5.11.3 to 5.11.4 ([#1809](https://github.com/aws-powertools/powertools-lambda-python/issues/1809)) +* **deps-dev:** bump aws-cdk-lib from 2.55.1 to 2.59.0 ([#1810](https://github.com/aws-powertools/powertools-lambda-python/issues/1810)) +* **deps-dev:** bump importlib-metadata from 5.1.0 to 6.0.0 ([#1804](https://github.com/aws-powertools/powertools-lambda-python/issues/1804)) +* **deps-dev:** bump mkdocs-material from 9.0.2 to 9.0.3 ([#1823](https://github.com/aws-powertools/powertools-lambda-python/issues/1823)) +* **deps-dev:** bump black from 22.10.0 to 22.12.0 ([#1770](https://github.com/aws-powertools/powertools-lambda-python/issues/1770)) +* **deps-dev:** bump flake8-black from 0.3.5 to 0.3.6 ([#1792](https://github.com/aws-powertools/powertools-lambda-python/issues/1792)) +* **deps-dev:** bump coverage from 7.0.4 to 7.0.5 ([#1829](https://github.com/aws-powertools/powertools-lambda-python/issues/1829)) +* **deps-dev:** bump types-requests from 2.28.11.5 to 2.28.11.7 ([#1795](https://github.com/aws-powertools/powertools-lambda-python/issues/1795)) + + +<a name="v2.5.0"></a> +## [v2.5.0] - 2022-12-21 +## Bug Fixes + +* **event_handlers:** omit explicit None HTTP header values ([#1793](https://github.com/aws-powertools/powertools-lambda-python/issues/1793)) + +## Documentation + +* **idempotency:** fix, improve, and increase visibility for batch integration ([#1776](https://github.com/aws-powertools/powertools-lambda-python/issues/1776)) +* **validation:** fix broken link; enrich built-in jmespath links ([#1777](https://github.com/aws-powertools/powertools-lambda-python/issues/1777)) + +## Features + +* **logger:** unwrap event from common models if asked to log ([#1778](https://github.com/aws-powertools/powertools-lambda-python/issues/1778)) + +## Maintenance + +* update v2 layer ARN on documentation +* **common:** reusable function to extract event from models +* **deps:** bump certifi from 2022.9.24 to 2022.12.7 ([#1768](https://github.com/aws-powertools/powertools-lambda-python/issues/1768)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 1.4.0 to 2.0.1 ([#1752](https://github.com/aws-powertools/powertools-lambda-python/issues/1752)) +* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 1.3.0 to 1.4.0 ([#1749](https://github.com/aws-powertools/powertools-lambda-python/issues/1749)) +* **deps-dev:** bump pytest-asyncio from 0.20.2 to 0.20.3 ([#1767](https://github.com/aws-powertools/powertools-lambda-python/issues/1767)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.26.0.post1 to 1.26.17 ([#1753](https://github.com/aws-powertools/powertools-lambda-python/issues/1753)) +* **deps-dev:** bump isort from 5.10.1 to 5.11.2 ([#1782](https://github.com/aws-powertools/powertools-lambda-python/issues/1782)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.26.17 to 1.26.30 ([#1785](https://github.com/aws-powertools/powertools-lambda-python/issues/1785)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.13.post16 to 1.26.24 ([#1765](https://github.com/aws-powertools/powertools-lambda-python/issues/1765)) +* **deps-dev:** bump aws-cdk-lib from 2.54.0 to 2.55.1 ([#1787](https://github.com/aws-powertools/powertools-lambda-python/issues/1787)) +* **deps-dev:** bump aws-cdk-lib from 2.53.0 to 2.54.0 ([#1764](https://github.com/aws-powertools/powertools-lambda-python/issues/1764)) +* **deps-dev:** bump flake8-bugbear from 22.10.27 to 22.12.6 ([#1760](https://github.com/aws-powertools/powertools-lambda-python/issues/1760)) +* **deps-dev:** bump filelock from 3.8.0 to 3.8.2 ([#1759](https://github.com/aws-powertools/powertools-lambda-python/issues/1759)) +* **deps-dev:** bump pytest-xdist from 3.0.2 to 3.1.0 ([#1758](https://github.com/aws-powertools/powertools-lambda-python/issues/1758)) +* **deps-dev:** bump mkdocs-material from 8.5.10 to 8.5.11 ([#1756](https://github.com/aws-powertools/powertools-lambda-python/issues/1756)) +* **deps-dev:** bump importlib-metadata from 4.13.0 to 5.1.0 ([#1750](https://github.com/aws-powertools/powertools-lambda-python/issues/1750)) +* **deps-dev:** bump isort from 5.11.2 to 5.11.3 ([#1788](https://github.com/aws-powertools/powertools-lambda-python/issues/1788)) +* **deps-dev:** bump flake8-black from 0.3.3 to 0.3.5 ([#1738](https://github.com/aws-powertools/powertools-lambda-python/issues/1738)) +* **deps-dev:** bump mypy-boto3-logs from 1.26.17 to 1.26.27 ([#1775](https://github.com/aws-powertools/powertools-lambda-python/issues/1775)) +* **tests:** move shared_functions to unit tests + + +<a name="v2.4.0"></a> +## [v2.4.0] - 2022-11-24 +## Bug Fixes + +* **ci:** use gh-pages env as official docs are wrong +* **ci:** api docs path + +## Documentation + +* **idempotency:** fix register_lambda_context order ([#1747](https://github.com/aws-powertools/powertools-lambda-python/issues/1747)) +* **streaming:** fix leftover newline + +## Features + +* **streaming:** add new s3 streaming utility ([#1719](https://github.com/aws-powertools/powertools-lambda-python/issues/1719)) + +## Maintenance + +* update v2 layer ARN on documentation +* **ci:** re-create versioned API docs for new pages deployment +* **ci:** re-create versioned API docs for new pages deployment +* **ci:** increase permission in parent job for docs publishing +* **ci:** attempt gh-pages deployment via beta route +* **deps:** bump aws-xray-sdk from 2.10.0 to 2.11.0 ([#1730](https://github.com/aws-powertools/powertools-lambda-python/issues/1730)) +* **deps-dev:** bump mypy-boto3-lambda from 1.26.0.post1 to 1.26.12 ([#1742](https://github.com/aws-powertools/powertools-lambda-python/issues/1742)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.0.post1 to 1.26.11.post1 ([#1746](https://github.com/aws-powertools/powertools-lambda-python/issues/1746)) +* **deps-dev:** bump aws-cdk-lib from 2.50.0 to 2.51.1 ([#1741](https://github.com/aws-powertools/powertools-lambda-python/issues/1741)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.0.post1 to 1.26.13.post16 ([#1743](https://github.com/aws-powertools/powertools-lambda-python/issues/1743)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.26.0.post1 to 1.26.12 ([#1744](https://github.com/aws-powertools/powertools-lambda-python/issues/1744)) +* **deps-dev:** bump mypy-boto3-ssm from 1.26.4 to 1.26.11.post1 ([#1740](https://github.com/aws-powertools/powertools-lambda-python/issues/1740)) +* **deps-dev:** bump types-requests from 2.28.11.4 to 2.28.11.5 ([#1729](https://github.com/aws-powertools/powertools-lambda-python/issues/1729)) +* **deps-dev:** bump mkdocs-material from 8.5.9 to 8.5.10 ([#1731](https://github.com/aws-powertools/powertools-lambda-python/issues/1731)) +* **governance:** remove markdown rendering from docs issue template + +## Regression + +* **ci:** new gh-pages beta doesn't work either; reverting as gh-pages is disrupted + + +<a name="v2.3.1"></a> +## [v2.3.1] - 2022-11-21 +## Bug Fixes + +* **apigateway:** support dynamic routes with equal sign (RFC3986) ([#1737](https://github.com/aws-powertools/powertools-lambda-python/issues/1737)) + +## Maintenance + +* update v2 layer ARN on documentation +* test build layer hardware to 8 core +* **deps-dev:** bump mypy-boto3-xray from 1.26.9 to 1.26.11.post1 ([#1734](https://github.com/aws-powertools/powertools-lambda-python/issues/1734)) + + +<a name="v2.3.0"></a> +## [v2.3.0] - 2022-11-17 +## Bug Fixes + +* **apigateway:** support nested router decorators ([#1709](https://github.com/aws-powertools/powertools-lambda-python/issues/1709)) +* **ci:** increase permission to allow version sync back to repo +* **ci:** disable pre-commit hook download from version bump +* **ci:** setup git client earlier to prevent dirty stash error +* **parameters:** get_secret correctly return SecretBinary value ([#1717](https://github.com/aws-powertools/powertools-lambda-python/issues/1717)) + +## Documentation + +* project name consistency +* **apigateway:** add all resolvers in testing your code section for accuracy ([#1688](https://github.com/aws-powertools/powertools-lambda-python/issues/1688)) +* **examples:** linting unnecessary whitespace +* **homepage:** update default value for `POWERTOOLS_DEV` ([#1695](https://github.com/aws-powertools/powertools-lambda-python/issues/1695)) +* **idempotency:** add missing Lambda Context; note on thread-safe ([#1732](https://github.com/aws-powertools/powertools-lambda-python/issues/1732)) +* **logger:** update uncaught exception message value + +## Features + +* **apigateway:** multiple exceptions in exception_handler ([#1707](https://github.com/aws-powertools/powertools-lambda-python/issues/1707)) +* **event_sources:** extract CloudWatch Logs in Kinesis streams ([#1710](https://github.com/aws-powertools/powertools-lambda-python/issues/1710)) +* **logger:** log uncaught exceptions via system's exception hook ([#1727](https://github.com/aws-powertools/powertools-lambda-python/issues/1727)) +* **parser:** export Pydantic.errors through escape hatch ([#1728](https://github.com/aws-powertools/powertools-lambda-python/issues/1728)) +* **parser:** extract CloudWatch Logs in Kinesis streams ([#1726](https://github.com/aws-powertools/powertools-lambda-python/issues/1726)) + +## Maintenance + +* apigw test event wrongly set with base64 +* update v2 layer ARN on documentation +* **ci:** revert custom hw for E2E due to lack of hw +* **ci:** try bigger hardware for e2e test +* **ci:** uncomment test pypi, fix version bump sync +* **ci:** limit to src only to prevent dependabot failures +* **ci:** use new custom hw for E2E +* **ci:** prevent dependabot updates to trigger E2E +* **ci:** bump hardware for build steps +* **deps:** bump dependabot/fetch-metadata from 1.3.4 to 1.3.5 ([#1689](https://github.com/aws-powertools/powertools-lambda-python/issues/1689)) +* **deps-dev:** bump types-requests from 2.28.11.3 to 2.28.11.4 ([#1701](https://github.com/aws-powertools/powertools-lambda-python/issues/1701)) +* **deps-dev:** bump mypy-boto3-s3 from 1.25.0 to 1.26.0.post1 ([#1716](https://github.com/aws-powertools/powertools-lambda-python/issues/1716)) +* **deps-dev:** bump mypy-boto3-appconfigdata from 1.25.0 to 1.26.0.post1 ([#1704](https://github.com/aws-powertools/powertools-lambda-python/issues/1704)) +* **deps-dev:** bump mypy-boto3-xray from 1.25.0 to 1.26.0.post1 ([#1703](https://github.com/aws-powertools/powertools-lambda-python/issues/1703)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.25.0 to 1.26.0.post1 ([#1714](https://github.com/aws-powertools/powertools-lambda-python/issues/1714)) +* **deps-dev:** bump flake8-bugbear from 22.10.25 to 22.10.27 ([#1665](https://github.com/aws-powertools/powertools-lambda-python/issues/1665)) +* **deps-dev:** bump mypy-boto3-lambda from 1.25.0 to 1.26.0.post1 ([#1705](https://github.com/aws-powertools/powertools-lambda-python/issues/1705)) +* **deps-dev:** bump mypy-boto3-xray from 1.26.0.post1 to 1.26.9 ([#1720](https://github.com/aws-powertools/powertools-lambda-python/issues/1720)) +* **deps-dev:** bump mypy-boto3-logs from 1.25.0 to 1.26.3 ([#1702](https://github.com/aws-powertools/powertools-lambda-python/issues/1702)) +* **deps-dev:** bump mypy-boto3-ssm from 1.26.0.post1 to 1.26.4 ([#1721](https://github.com/aws-powertools/powertools-lambda-python/issues/1721)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.25.0 to 1.26.0.post1 ([#1722](https://github.com/aws-powertools/powertools-lambda-python/issues/1722)) +* **deps-dev:** bump pytest-asyncio from 0.20.1 to 0.20.2 ([#1723](https://github.com/aws-powertools/powertools-lambda-python/issues/1723)) +* **deps-dev:** bump flake8-builtins from 2.0.0 to 2.0.1 ([#1715](https://github.com/aws-powertools/powertools-lambda-python/issues/1715)) +* **deps-dev:** bump pytest-xdist from 2.5.0 to 3.0.2 ([#1655](https://github.com/aws-powertools/powertools-lambda-python/issues/1655)) +* **deps-dev:** bump mkdocs-material from 8.5.7 to 8.5.9 ([#1697](https://github.com/aws-powertools/powertools-lambda-python/issues/1697)) +* **deps-dev:** bump flake8-comprehensions from 3.10.0 to 3.10.1 ([#1699](https://github.com/aws-powertools/powertools-lambda-python/issues/1699)) +* **deps-dev:** bump types-requests from 2.28.11.2 to 2.28.11.3 ([#1698](https://github.com/aws-powertools/powertools-lambda-python/issues/1698)) +* **deps-dev:** bump pytest-benchmark from 3.4.1 to 4.0.0 ([#1659](https://github.com/aws-powertools/powertools-lambda-python/issues/1659)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.25.0 to 1.26.0.post1 ([#1691](https://github.com/aws-powertools/powertools-lambda-python/issues/1691)) +* **deps-dev:** bump mypy-boto3-ssm from 1.25.0 to 1.26.0.post1 ([#1690](https://github.com/aws-powertools/powertools-lambda-python/issues/1690)) +* **logger:** uncaught exception to use exception value as message +* **logger:** overload inject_lambda_context with generics ([#1583](https://github.com/aws-powertools/powertools-lambda-python/issues/1583)) + + +<a name="v2.2.0"></a> +## [v2.2.0] - 2022-11-07 +## Documentation + +* **homepage:** remove v1 layer limitation on pydantic not being included +* **tracer:** add note on why X-Ray SDK over ADOT closes [#1675](https://github.com/aws-powertools/powertools-lambda-python/issues/1675) + +## Features + +* **metrics:** add EphemeralMetrics as a non-singleton option ([#1676](https://github.com/aws-powertools/powertools-lambda-python/issues/1676)) +* **parameters:** add get_parameters_by_name for SSM params in distinct paths ([#1678](https://github.com/aws-powertools/powertools-lambda-python/issues/1678)) + +## Maintenance + +* update v2 layer ARN on documentation +* **deps:** bump package to 2.2.0 +* **deps-dev:** bump aws-cdk-lib from 2.49.0 to 2.50.0 ([#1683](https://github.com/aws-powertools/powertools-lambda-python/issues/1683)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.25.0 to 1.26.0.post1 ([#1682](https://github.com/aws-powertools/powertools-lambda-python/issues/1682)) +* **deps-dev:** bump mypy-boto3-cloudformation from 1.25.0 to 1.26.0.post1 ([#1679](https://github.com/aws-powertools/powertools-lambda-python/issues/1679)) +* **package:** correct pyproject version manually + + +<a name="v2.1.0"></a> +## [v2.1.0] - 2022-10-31 +## Bug Fixes + +* **ci:** linting issues after flake8-blackbear,mypy upgrades +* **deps:** update build system to poetry-core ([#1651](https://github.com/aws-powertools/powertools-lambda-python/issues/1651)) +* **idempotency:** idempotent_function should support standalone falsy values ([#1669](https://github.com/aws-powertools/powertools-lambda-python/issues/1669)) +* **logger:** fix unknown attributes being ignored by mypy ([#1670](https://github.com/aws-powertools/powertools-lambda-python/issues/1670)) + +## Documentation + +* **community:** fix social handlers for Ran ([#1654](https://github.com/aws-powertools/powertools-lambda-python/issues/1654)) +* **community:** fix twitch parent domain for embedded video +* **homepage:** remove 3.6 and add hero image +* **homepage:** add Pulumi code example ([#1652](https://github.com/aws-powertools/powertools-lambda-python/issues/1652)) +* **index:** fold support us banner +* **index:** add quotes to pip for zsh customers +* **install:** address early v2 feedback on installation and project support +* **we-made-this:** new community content section ([#1650](https://github.com/aws-powertools/powertools-lambda-python/issues/1650)) + +## Features + +* **layers:** add layer balancer script ([#1643](https://github.com/aws-powertools/powertools-lambda-python/issues/1643)) +* **logger:** add use_rfc3339 and auto-complete formatter opts in Logger ([#1662](https://github.com/aws-powertools/powertools-lambda-python/issues/1662)) +* **logger:** accept arbitrary keyword=value for ephemeral metadata ([#1658](https://github.com/aws-powertools/powertools-lambda-python/issues/1658)) + +## Maintenance + +* update v2 layer ARN on documentation +* **ci:** fix typo on version description +* **deps:** bump peaceiris/actions-gh-pages from 3.8.0 to 3.9.0 ([#1649](https://github.com/aws-powertools/powertools-lambda-python/issues/1649)) +* **deps:** bump docker/setup-qemu-action from 2.0.0 to 2.1.0 ([#1627](https://github.com/aws-powertools/powertools-lambda-python/issues/1627)) +* **deps-dev:** bump aws-cdk-lib from 2.47.0 to 2.48.0 ([#1664](https://github.com/aws-powertools/powertools-lambda-python/issues/1664)) +* **deps-dev:** bump flake8-variables-names from 0.0.4 to 0.0.5 ([#1628](https://github.com/aws-powertools/powertools-lambda-python/issues/1628)) +* **deps-dev:** bump pytest-asyncio from 0.16.0 to 0.20.1 ([#1635](https://github.com/aws-powertools/powertools-lambda-python/issues/1635)) +* **deps-dev:** bump aws-cdk-lib from 2.48.0 to 2.49.0 ([#1671](https://github.com/aws-powertools/powertools-lambda-python/issues/1671)) +* **docs:** remove v2 banner on top of the docs +* **governance:** remove 'area/' from PR labels + + +<a name="v2.0.0"></a> +## [v2.0.0] - 2022-10-24 +## Bug Fixes + +* lock dependencies +* mypy errors +* lint files +* **ci:** temporarly remove pypi test deployment +* **ci:** use docker driver on buildx +* **ci:** new artifact path, sed gnu/linux syntax, and pypi test +* **ci:** secret and OIDC inheritance in nested children workflow +* **ci:** build without buildkit +* **ci:** fix arm64 layer builds +* **ci:** remove v2 suffix from SAR apps ([#1633](https://github.com/aws-powertools/powertools-lambda-python/issues/1633)) +* **ci:** workflow should use npx for CDK CLI +* **parser:** S3Model Object Deleted omits size and eTag attr ([#1638](https://github.com/aws-powertools/powertools-lambda-python/issues/1638)) + +## Code Refactoring + +* **apigateway:** remove POWERTOOLS_EVENT_HANDLER_DEBUG env var ([#1620](https://github.com/aws-powertools/powertools-lambda-python/issues/1620)) +* **batch:** remove legacy sqs_batch_processor ([#1492](https://github.com/aws-powertools/powertools-lambda-python/issues/1492)) +* **e2e:** make table name dynamic +* **e2e:** fix idempotency typing + +## Documentation + +* **batch:** remove legacy reference to sqs processor +* **homepage:** note about v2 version +* **homepage:** auto-update Layer ARN on every release ([#1610](https://github.com/aws-powertools/powertools-lambda-python/issues/1610)) +* **roadmap:** refresh roadmap post-v2 launch +* **roadmap:** include observability provider and lambda layer themes before v2 +* **upgrade_guide:** add latest changes and quick summary ([#1623](https://github.com/aws-powertools/powertools-lambda-python/issues/1623)) +* **v2:** document optional dependencies and local dev ([#1574](https://github.com/aws-powertools/powertools-lambda-python/issues/1574)) + +## Features + +* **apigateway:** ignore trailing slashes in routes (APIGatewayRestResolver) ([#1609](https://github.com/aws-powertools/powertools-lambda-python/issues/1609)) +* **ci:** release docs as alpha when doing a pre-release ([#1624](https://github.com/aws-powertools/powertools-lambda-python/issues/1624)) +* **data-classes:** replace AttributeValue in DynamoDBStreamEvent with deserialized Python values ([#1619](https://github.com/aws-powertools/powertools-lambda-python/issues/1619)) +* **data_classes:** add KinesisFirehoseEvent ([#1540](https://github.com/aws-powertools/powertools-lambda-python/issues/1540)) +* **event_handler:** improved support for headers and cookies in v2 ([#1455](https://github.com/aws-powertools/powertools-lambda-python/issues/1455)) +* **event_handler:** add cookies as 1st class citizen in v2 ([#1487](https://github.com/aws-powertools/powertools-lambda-python/issues/1487)) +* **idempotency:** support methods with the same name (ABCs) by including fully qualified name in v2 ([#1535](https://github.com/aws-powertools/powertools-lambda-python/issues/1535)) +* **layer:** publish SAR v2 via Github actions ([#1585](https://github.com/aws-powertools/powertools-lambda-python/issues/1585)) +* **layers:** add support for publishing v2 layer ([#1558](https://github.com/aws-powertools/powertools-lambda-python/issues/1558)) +* **parameters:** migrate AppConfig to new APIs due to API deprecation ([#1553](https://github.com/aws-powertools/powertools-lambda-python/issues/1553)) +* **tracer:** support methods with the same name (ABCs) by including fully qualified name in v2 ([#1486](https://github.com/aws-powertools/powertools-lambda-python/issues/1486)) + +## Maintenance + +* update v2 layer ARN on documentation +* update v2 layer ARN on documentation +* update v2 layer ARN on documentation +* update v2 layer ARN on documentation +* merge v2 branch +* bump pyproject version to 2.0 +* **ci:** make release process manual +* **ci:** migrate E2E tests to CDK CLI and off Docker ([#1501](https://github.com/aws-powertools/powertools-lambda-python/issues/1501)) +* **ci:** remove v1 workflows ([#1617](https://github.com/aws-powertools/powertools-lambda-python/issues/1617)) +* **core:** expose modules in the Top-level package ([#1517](https://github.com/aws-powertools/powertools-lambda-python/issues/1517)) +* **dep:** add cfn-lint as a dev dependency; pre-commit ([#1612](https://github.com/aws-powertools/powertools-lambda-python/issues/1612)) +* **deps:** remove email-validator; use Str over EmailStr in SES model ([#1608](https://github.com/aws-powertools/powertools-lambda-python/issues/1608)) +* **deps:** bump release-drafter/release-drafter from 5.21.0 to 5.21.1 ([#1611](https://github.com/aws-powertools/powertools-lambda-python/issues/1611)) +* **deps:** lock importlib to 4.x +* **deps-dev:** bump mypy-boto3-s3 from 1.24.76 to 1.24.94 ([#1622](https://github.com/aws-powertools/powertools-lambda-python/issues/1622)) +* **deps-dev:** bump aws-cdk-lib from 2.46.0 to 2.47.0 ([#1629](https://github.com/aws-powertools/powertools-lambda-python/issues/1629)) +* **layer:** bump to 1.31.1 (v39) + + +<a name="v1.31.1"></a> +## [v1.31.1] - 2022-10-14 +## Bug Fixes + +* **parser:** loose validation on SNS fields to support FIFO ([#1606](https://github.com/aws-powertools/powertools-lambda-python/issues/1606)) + +## Documentation + +* **governance:** allow community to suggest feature content ([#1593](https://github.com/aws-powertools/powertools-lambda-python/issues/1593)) +* **governance:** new form to allow customers self-nominate as public reference ([#1589](https://github.com/aws-powertools/powertools-lambda-python/issues/1589)) +* **homepage:** include .NET powertools +* **idempotency:** "persisntence" typo ([#1596](https://github.com/aws-powertools/powertools-lambda-python/issues/1596)) +* **logger:** fix typo. ([#1587](https://github.com/aws-powertools/powertools-lambda-python/issues/1587)) + +## Maintenance + +* add dummy v2 sar deploy job +* bump layer version to 38 +* **deps-dev:** bump mypy-boto3-ssm from 1.24.81 to 1.24.90 ([#1594](https://github.com/aws-powertools/powertools-lambda-python/issues/1594)) +* **deps-dev:** bump flake8-builtins from 1.5.3 to 2.0.0 ([#1582](https://github.com/aws-powertools/powertools-lambda-python/issues/1582)) + + +<a name="v1.31.0"></a> +## [v1.31.0] - 2022-10-10 +## Bug Fixes + +* **metrics:** ensure dimension_set is reused across instances (pointer) ([#1581](https://github.com/aws-powertools/powertools-lambda-python/issues/1581)) + +## Documentation + +* **readme:** add lambda layer latest version badge + +## Features + +* **parser:** add KinesisFirehoseModel ([#1556](https://github.com/aws-powertools/powertools-lambda-python/issues/1556)) + +## Maintenance + +* **deps-dev:** bump types-requests from 2.28.11.1 to 2.28.11.2 ([#1576](https://github.com/aws-powertools/powertools-lambda-python/issues/1576)) +* **deps-dev:** bump typing-extensions from 4.3.0 to 4.4.0 ([#1575](https://github.com/aws-powertools/powertools-lambda-python/issues/1575)) +* **layer:** remove unsused GetFunction permission for the canary +* **layer:** bump to latest version 37 + + +<a name="v1.30.0"></a> +## [v1.30.0] - 2022-10-05 +## Bug Fixes + +* **apigateway:** update Response class to require status_code only ([#1560](https://github.com/aws-powertools/powertools-lambda-python/issues/1560)) +* **ci:** integrate isort 5.0 with black to resolve conflicts +* **event_sources:** implement Mapping protocol on DictWrapper for better interop with existing middlewares ([#1516](https://github.com/aws-powertools/powertools-lambda-python/issues/1516)) +* **typing:** fix mypy error +* **typing:** level arg in copy_config_to_registered_loggers ([#1534](https://github.com/aws-powertools/powertools-lambda-python/issues/1534)) + +## Documentation + +* **batch:** document the new lambda context feature +* **homepage:** introduce POWERTOOLS_DEV env var ([#1569](https://github.com/aws-powertools/powertools-lambda-python/issues/1569)) +* **multiple:** fix highlighting after new isort/black integration +* **parser:** add JSON string field extension example ([#1526](https://github.com/aws-powertools/powertools-lambda-python/issues/1526)) + +## Features + +* **batch:** inject lambda_context if record handler signature accepts it ([#1561](https://github.com/aws-powertools/powertools-lambda-python/issues/1561)) +* **event-handler:** context support to share data between routers ([#1567](https://github.com/aws-powertools/powertools-lambda-python/issues/1567)) +* **logger:** introduce POWERTOOLS_DEBUG for internal debugging ([#1572](https://github.com/aws-powertools/powertools-lambda-python/issues/1572)) +* **logger:** include logger name attribute when copy_config_to_registered_logger is used ([#1568](https://github.com/aws-powertools/powertools-lambda-python/issues/1568)) +* **logger:** pretty-print JSON when POWERTOOLS_DEV is set ([#1548](https://github.com/aws-powertools/powertools-lambda-python/issues/1548)) + +## Maintenance + +* **dep:** bump pyproject to pypi sync +* **deps:** bump fastjsonschema from 2.16.1 to 2.16.2 ([#1530](https://github.com/aws-powertools/powertools-lambda-python/issues/1530)) +* **deps:** bump actions/setup-python from 3 to 4 ([#1528](https://github.com/aws-powertools/powertools-lambda-python/issues/1528)) +* **deps:** bump codecov/codecov-action from 3.1.0 to 3.1.1 ([#1529](https://github.com/aws-powertools/powertools-lambda-python/issues/1529)) +* **deps:** bump dependabot/fetch-metadata from 1.3.3 to 1.3.4 ([#1565](https://github.com/aws-powertools/powertools-lambda-python/issues/1565)) +* **deps:** bump email-validator from 1.2.1 to 1.3.0 ([#1533](https://github.com/aws-powertools/powertools-lambda-python/issues/1533)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.24.54 to 1.24.83 ([#1557](https://github.com/aws-powertools/powertools-lambda-python/issues/1557)) +* **deps-dev:** bump mkdocs-material from 8.5.3 to 8.5.4 ([#1563](https://github.com/aws-powertools/powertools-lambda-python/issues/1563)) +* **deps-dev:** bump pytest-cov from 3.0.0 to 4.0.0 ([#1551](https://github.com/aws-powertools/powertools-lambda-python/issues/1551)) +* **deps-dev:** bump flake8-bugbear from 22.9.11 to 22.9.23 ([#1541](https://github.com/aws-powertools/powertools-lambda-python/issues/1541)) +* **deps-dev:** bump types-requests from 2.28.11 to 2.28.11.1 ([#1571](https://github.com/aws-powertools/powertools-lambda-python/issues/1571)) +* **deps-dev:** bump mypy-boto3-ssm from 1.24.69 to 1.24.80 ([#1542](https://github.com/aws-powertools/powertools-lambda-python/issues/1542)) +* **deps-dev:** bump mako from 1.2.2 to 1.2.3 ([#1537](https://github.com/aws-powertools/powertools-lambda-python/issues/1537)) +* **deps-dev:** bump types-requests from 2.28.10 to 2.28.11 ([#1538](https://github.com/aws-powertools/powertools-lambda-python/issues/1538)) +* **deps-dev:** bump mkdocs-material from 8.5.1 to 8.5.3 ([#1532](https://github.com/aws-powertools/powertools-lambda-python/issues/1532)) +* **deps-dev:** bump mypy-boto3-ssm from 1.24.80 to 1.24.81 ([#1544](https://github.com/aws-powertools/powertools-lambda-python/issues/1544)) +* **deps-dev:** bump mypy-boto3-s3 from 1.24.36.post1 to 1.24.76 ([#1531](https://github.com/aws-powertools/powertools-lambda-python/issues/1531)) +* **docs:** bump layer version to 36 (1.29.2) +* **layers:** add dummy v2 layer automation +* **lint:** use new isort black integration +* **multiple:** localize powertools_dev env logic and warning ([#1570](https://github.com/aws-powertools/powertools-lambda-python/issues/1570)) + + +<a name="v1.29.2"></a> +## [v1.29.2] - 2022-09-19 +## Bug Fixes + +* **deps:** bump dev dep mako version to address CVE-2022-40023 ([#1524](https://github.com/aws-powertools/powertools-lambda-python/issues/1524)) + +## Maintenance + +* **deps:** bump release-drafter/release-drafter from 5.20.1 to 5.21.0 ([#1520](https://github.com/aws-powertools/powertools-lambda-python/issues/1520)) +* **deps-dev:** bump mkdocs-material from 8.5.0 to 8.5.1 ([#1521](https://github.com/aws-powertools/powertools-lambda-python/issues/1521)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.24.60 to 1.24.74 ([#1522](https://github.com/aws-powertools/powertools-lambda-python/issues/1522)) + + +<a name="v1.29.1"></a> +## [v1.29.1] - 2022-09-13 + +<a name="v1.29.0"></a> +## [v1.29.0] - 2022-09-13 +## Bug Fixes + +* **ci:** ignore v2 action for now +* **ci:** only run e2e tests on py 3.7 +* **ci:** pass core fns to large pr workflow script +* **ci:** on_label permissioning model & workflow execution +* **ci:** ensure PR_AUTHOR is present for large_pr_split workflow +* **ci:** gracefully and successful exit changelog upon no changes +* **ci:** event resolution for on_label_added workflow +* **core:** fixes leftovers from rebase + +## Documentation + +* **layer:** upgrade to 1.28.0 (v33) + +## Features + +* **ci:** add actionlint in pre-commit hook +* **data-classes:** add KafkaEvent and KafkaEventRecord ([#1485](https://github.com/aws-powertools/powertools-lambda-python/issues/1485)) +* **event_sources:** add CloudWatch dashboard custom widget event ([#1474](https://github.com/aws-powertools/powertools-lambda-python/issues/1474)) +* **parser:** add KafkaMskEventModel and KafkaSelfManagedEventModel ([#1499](https://github.com/aws-powertools/powertools-lambda-python/issues/1499)) + +## Maintenance + +* **ci:** add workflow to suggest splitting large PRs ([#1480](https://github.com/aws-powertools/powertools-lambda-python/issues/1480)) +* **ci:** remove unused and undeclared OS matrix env +* **ci:** disable v2 docs +* **ci:** limit E2E workflow run for source code change +* **ci:** add missing description fields +* **ci:** sync package version with pypi +* **ci:** fix invalid dependency leftover +* **ci:** create adhoc docs workflow for v2 +* **ci:** create adhoc docs workflow for v2 +* **ci:** remove dangling debug step +* **ci:** create docs workflow for v2 +* **ci:** create reusable docs publishing workflow ([#1482](https://github.com/aws-powertools/powertools-lambda-python/issues/1482)) +* **ci:** format comment on comment_large_pr script +* **ci:** add note for state persistence on comment_large_pr +* **ci:** destructure assignment on comment_large_pr +* **ci:** record pr details upon labeling +* **ci:** add linter for GitHub Actions as pre-commit hook ([#1479](https://github.com/aws-powertools/powertools-lambda-python/issues/1479)) +* **ci:** enable ci checks for v2 +* **deps-dev:** bump black from 21.12b0 to 22.8.0 ([#1515](https://github.com/aws-powertools/powertools-lambda-python/issues/1515)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.24.55.post1 to 1.24.60 ([#1481](https://github.com/aws-powertools/powertools-lambda-python/issues/1481)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.24.55.post1 to 1.24.60 ([#306](https://github.com/aws-powertools/powertools-lambda-python/issues/306)) +* **deps-dev:** bump mkdocs-material from 8.4.1 to 8.4.2 ([#1483](https://github.com/aws-powertools/powertools-lambda-python/issues/1483)) +* **deps-dev:** revert to v1.28.0 dependencies +* **deps-dev:** bump mkdocs-material from 8.4.4 to 8.5.0 ([#1514](https://github.com/aws-powertools/powertools-lambda-python/issues/1514)) +* **maintainers:** update release workflow link +* **maintenance:** add discord link to first PR and first issue ([#1493](https://github.com/aws-powertools/powertools-lambda-python/issues/1493)) + + +<a name="v1.28.0"></a> +## [v1.28.0] - 2022-08-25 +## Bug Fixes + +* **ci:** calculate parallel jobs based on infrastructure needs ([#1475](https://github.com/aws-powertools/powertools-lambda-python/issues/1475)) +* **ci:** del flake8 direct dep over py3.6 conflicts and docs failure +* **ci:** move from pip-tools to poetry on layers reusable workflow +* **ci:** move from pip-tools to poetry on layers to fix conflicts +* **ci:** typo and bust gh actions cache +* **ci:** use poetry to resolve layer deps; pip for CDK +* **ci:** disable poetry venv for layer workflow as cdk ignores venv +* **ci:** add cdk v2 dep for layers workflow +* **ci:** move from pip-tools to poetry on layers +* **ci:** temporarily disable changelog upon release +* **ci:** add explicit origin to fix release detached head +* **jmespath_util:** snappy as dev dep and typing example ([#1446](https://github.com/aws-powertools/powertools-lambda-python/issues/1446)) + +## Documentation + +* **apigateway:** removes duplicate admonition ([#1426](https://github.com/aws-powertools/powertools-lambda-python/issues/1426)) +* **home:** fix discord syntax and add Discord badge +* **home:** add discord invitation link ([#1471](https://github.com/aws-powertools/powertools-lambda-python/issues/1471)) +* **jmespath_util:** snippets split, improved, and lint ([#1419](https://github.com/aws-powertools/powertools-lambda-python/issues/1419)) +* **layer:** upgrade to 1.27.0 +* **layer:** upgrade to 1.27.0 +* **middleware-factory:** snippets split, improved, and lint ([#1451](https://github.com/aws-powertools/powertools-lambda-python/issues/1451)) +* **parser:** minor grammar fix ([#1427](https://github.com/aws-powertools/powertools-lambda-python/issues/1427)) +* **typing:** snippets split, improved, and lint ([#1465](https://github.com/aws-powertools/powertools-lambda-python/issues/1465)) +* **validation:** snippets split, improved, and lint ([#1449](https://github.com/aws-powertools/powertools-lambda-python/issues/1449)) + +## Features + +* **parser:** add support for Lambda Function URL ([#1442](https://github.com/aws-powertools/powertools-lambda-python/issues/1442)) + +## Maintenance + +* **batch:** deprecate sqs_batch_processor ([#1463](https://github.com/aws-powertools/powertools-lambda-python/issues/1463)) +* **ci:** prevent concurrent git update in critical workflows ([#1478](https://github.com/aws-powertools/powertools-lambda-python/issues/1478)) +* **ci:** disable e2e py version matrix due to concurrent locking +* **ci:** revert e2e py version matrix +* **ci:** temp disable e2e matrix +* **ci:** update changelog with latest changes +* **ci:** update changelog with latest changes +* **ci:** reduce payload and only send prod notification +* **ci:** remove area/utilities conflicting label +* **ci:** include py version in stack and cache lock +* **ci:** remove conventional changelog commit to reduce noise +* **ci:** update changelog with latest changes +* **deps:** bump release-drafter/release-drafter from 5.20.0 to 5.20.1 ([#1458](https://github.com/aws-powertools/powertools-lambda-python/issues/1458)) +* **deps:** bump pydantic from 1.9.1 to 1.9.2 ([#1448](https://github.com/aws-powertools/powertools-lambda-python/issues/1448)) +* **deps-dev:** bump flake8-bugbear from 22.8.22 to 22.8.23 ([#1473](https://github.com/aws-powertools/powertools-lambda-python/issues/1473)) +* **deps-dev:** bump types-requests from 2.28.7 to 2.28.8 ([#1423](https://github.com/aws-powertools/powertools-lambda-python/issues/1423)) +* **maintainer:** add Leandro as maintainer ([#1468](https://github.com/aws-powertools/powertools-lambda-python/issues/1468)) +* **tests:** build and deploy Lambda Layer stack once ([#1466](https://github.com/aws-powertools/powertools-lambda-python/issues/1466)) +* **tests:** refactor E2E test mechanics to ease maintenance, writing tests and parallelization ([#1444](https://github.com/aws-powertools/powertools-lambda-python/issues/1444)) +* **tests:** enable end-to-end test workflow ([#1470](https://github.com/aws-powertools/powertools-lambda-python/issues/1470)) +* **tests:** refactor E2E logger to ease maintenance, writing tests and parallelization ([#1460](https://github.com/aws-powertools/powertools-lambda-python/issues/1460)) +* **tests:** refactor E2E tracer to ease maintenance, writing tests and parallelization ([#1457](https://github.com/aws-powertools/powertools-lambda-python/issues/1457)) + +## Reverts +* fix(ci): add explicit origin to fix release detached head + + +<a name="v1.27.0"></a> +## [v1.27.0] - 2022-08-05 +## Bug Fixes + +* **ci:** changelog workflow must receive git tags too +* **ci:** add additional input to accurately describe intent on skip +* **ci:** job permissions +* **event_sources:** add test for Function URL AuthZ ([#1421](https://github.com/aws-powertools/powertools-lambda-python/issues/1421)) + +## Documentation + +* **layer:** upgrade to 1.26.7 + +## Features + +* **ci:** create reusable changelog generation ([#1418](https://github.com/aws-powertools/powertools-lambda-python/issues/1418)) +* **ci:** include changelog generation on docs build +* **ci:** create reusable changelog generation +* **event_handlers:** Add support for Lambda Function URLs ([#1408](https://github.com/aws-powertools/powertools-lambda-python/issues/1408)) +* **metrics:** update max user-defined dimensions from 9 to 29 ([#1417](https://github.com/aws-powertools/powertools-lambda-python/issues/1417)) + +## Maintenance + +* **ci:** sync area labels to prevent dedup +* **ci:** update changelog with latest changes +* **ci:** update changelog with latest changes +* **ci:** add manual trigger for docs +* **ci:** update changelog with latest changes +* **ci:** temporarily disable changelog push on release +* **ci:** update changelog with latest changes +* **ci:** move changelog generation to rebuild_latest_doc workflow +* **ci:** update project with version +* **ci:** update release automated activities +* **ci:** readd changelog step on release +* **ci:** move changelog generation to rebuild_latest_doc workflow +* **ci:** drop 3.6 from workflows +* **deps:** bump constructs from 10.1.1 to 10.1.60 ([#1399](https://github.com/aws-powertools/powertools-lambda-python/issues/1399)) +* **deps:** bump constructs from 10.1.1 to 10.1.66 ([#1414](https://github.com/aws-powertools/powertools-lambda-python/issues/1414)) +* **deps:** bump jsii from 1.57.0 to 1.63.2 ([#1400](https://github.com/aws-powertools/powertools-lambda-python/issues/1400)) +* **deps:** bump constructs from 10.1.1 to 10.1.64 ([#1405](https://github.com/aws-powertools/powertools-lambda-python/issues/1405)) +* **deps:** bump attrs from 21.4.0 to 22.1.0 ([#1397](https://github.com/aws-powertools/powertools-lambda-python/issues/1397)) +* **deps:** bump constructs from 10.1.1 to 10.1.63 ([#1402](https://github.com/aws-powertools/powertools-lambda-python/issues/1402)) +* **deps:** bump constructs from 10.1.1 to 10.1.65 ([#1407](https://github.com/aws-powertools/powertools-lambda-python/issues/1407)) +* **deps-dev:** bump types-requests from 2.28.5 to 2.28.6 ([#1401](https://github.com/aws-powertools/powertools-lambda-python/issues/1401)) +* **deps-dev:** bump types-requests from 2.28.6 to 2.28.7 ([#1406](https://github.com/aws-powertools/powertools-lambda-python/issues/1406)) +* **docs:** remove pause sentence from roadmap ([#1409](https://github.com/aws-powertools/powertools-lambda-python/issues/1409)) +* **docs:** update site name to test ci changelog +* **docs:** update CHANGELOG for v1.26.7 +* **docs:** update description to trigger changelog generation +* **governance:** remove devcontainer in favour of gitpod.io ([#1411](https://github.com/aws-powertools/powertools-lambda-python/issues/1411)) +* **governance:** add pre-configured dev environment with GitPod.io to ease contributions ([#1403](https://github.com/aws-powertools/powertools-lambda-python/issues/1403)) +* **layers:** upgrade cdk dep hashes to prevent ci fail + + +<a name="v1.26.7"></a> +## [v1.26.7] - 2022-07-29 +## Bug Fixes + +* **ci:** add missing oidc token generation permission +* **event_handlers:** ImportError when importing Response from top-level event_handler ([#1388](https://github.com/aws-powertools/powertools-lambda-python/issues/1388)) + +## Documentation + +* **examples:** enforce and fix all mypy errors ([#1393](https://github.com/aws-powertools/powertools-lambda-python/issues/1393)) + +## Features + +* **idempotency:** handle lambda timeout scenarios for INPROGRESS records ([#1387](https://github.com/aws-powertools/powertools-lambda-python/issues/1387)) + +## Maintenance + +* **ci:** increase skip_pypi logic to cover tests/changelog on re-run failures +* **ci:** update project with version 1.26.6 +* **ci:** drop 3.6 from workflows ([#1395](https://github.com/aws-powertools/powertools-lambda-python/issues/1395)) +* **ci:** add conditional to skip pypi release ([#1366](https://github.com/aws-powertools/powertools-lambda-python/issues/1366)) +* **ci:** remove leftover logic from on_merged_pr workflow +* **ci:** update project with version 1.26.6 +* **ci:** update project with version 1.26.6 +* **deps:** bump jsii from 1.57.0 to 1.63.1 ([#1390](https://github.com/aws-powertools/powertools-lambda-python/issues/1390)) +* **deps:** bump constructs from 10.1.1 to 10.1.59 ([#1396](https://github.com/aws-powertools/powertools-lambda-python/issues/1396)) +* **deps-dev:** bump flake8-isort from 4.1.1 to 4.1.2.post0 ([#1384](https://github.com/aws-powertools/powertools-lambda-python/issues/1384)) +* **layers:** bump to 1.26.6 using layer v26 +* **maintainers:** add Ruben as a maintainer ([#1392](https://github.com/aws-powertools/powertools-lambda-python/issues/1392)) + + +<a name="v1.26.6"></a> +## [v1.26.6] - 2022-07-25 +## Bug Fixes + +* **ci:** remove unsupported env in workflow_call +* **ci:** allow inherit secrets for reusable workflow +* **ci:** remove unused secret +* **ci:** label_related_issue unresolved var from history mixup +* **ci:** cond doesnt support two expr w/ env +* **ci:** only event is resolved in cond +* **ci:** unexpected symbol due to double quotes... +* **event_handlers:** handle lack of headers when using auto-compression feature ([#1325](https://github.com/aws-powertools/powertools-lambda-python/issues/1325)) + +## Maintenance + +* dummy for PR test +* print full event depth +* print full workflow event depth +* debug full event +* remove leftover from fork one more time +* **ci:** test env expr +* **ci:** test upstream job skip +* **ci:** lockdown workflow_run by origin ([#1350](https://github.com/aws-powertools/powertools-lambda-python/issues/1350)) +* **ci:** test default env +* **ci:** experiment hardening origin +* **ci:** experiment hardening origin +* **ci:** introduce codeowners ([#1352](https://github.com/aws-powertools/powertools-lambda-python/issues/1352)) +* **ci:** use OIDC and encrypt release secrets ([#1355](https://github.com/aws-powertools/powertools-lambda-python/issues/1355)) +* **ci:** remove core group from codeowners ([#1358](https://github.com/aws-powertools/powertools-lambda-python/issues/1358)) +* **ci:** confirm workflow_run event +* **ci:** use gh environment for beta and prod layer deploy ([#1356](https://github.com/aws-powertools/powertools-lambda-python/issues/1356)) +* **ci:** update project with version 1.26.5 +* **deps:** bump constructs from 10.1.1 to 10.1.52 ([#1343](https://github.com/aws-powertools/powertools-lambda-python/issues/1343)) +* **deps-dev:** bump mypy-boto3-cloudwatch from 1.24.0 to 1.24.35 ([#1342](https://github.com/aws-powertools/powertools-lambda-python/issues/1342)) +* **governance:** update wording tech debt to summary in maintenance template +* **governance:** add new maintenance issue template for tech debt ([#1326](https://github.com/aws-powertools/powertools-lambda-python/issues/1326)) +* **layers:** layer canary stack should not hardcode resource name +* **layers:** replace layers account secret ([#1329](https://github.com/aws-powertools/powertools-lambda-python/issues/1329)) +* **layers:** expand to all aws commercial regions ([#1324](https://github.com/aws-powertools/powertools-lambda-python/issues/1324)) +* **layers:** bump to 1.26.5 + +## Pull Requests + +* Merge pull request [#285](https://github.com/aws-powertools/powertools-lambda-python/issues/285) from heitorlessa/chore/skip-dep-workflow +* Merge pull request [#284](https://github.com/aws-powertools/powertools-lambda-python/issues/284) from heitorlessa/chore/dummy + + +<a name="v1.26.5"></a> +## [v1.26.5] - 2022-07-20 +## Bug Fixes + +* mathc the name of the cdk synth from the build phase +* typo in input for layer workflow +* no need to cache npm since we only install cdk cli and don't have .lock files +* add entire ARN role instead of account and role name +* path to artefact +* unzip the right artifact name +* download artefact into the layer dir +* sight, yes a whitespace character breaks the build +* **ci:** checkout project before validating related issue workflow +* **ci:** install poetry before calling setup/python with cache ([#1315](https://github.com/aws-powertools/powertools-lambda-python/issues/1315)) +* **ci:** remove additional quotes in PR action ([#1317](https://github.com/aws-powertools/powertools-lambda-python/issues/1317)) +* **ci:** lambda layer workflow release version and conditionals ([#1316](https://github.com/aws-powertools/powertools-lambda-python/issues/1316)) +* **ci:** fetch all git info so we can check tags +* **ci:** lambda layer workflow release version and conditionals ([#1316](https://github.com/aws-powertools/powertools-lambda-python/issues/1316)) +* **ci:** keep layer version permission ([#1318](https://github.com/aws-powertools/powertools-lambda-python/issues/1318)) +* **ci:** regex to catch combination of related issues workflow +* **deps:** correct mypy types as dev dependency ([#1322](https://github.com/aws-powertools/powertools-lambda-python/issues/1322)) +* **logger:** preserve std keys when using custom formatters ([#1264](https://github.com/aws-powertools/powertools-lambda-python/issues/1264)) + +## Documentation + +* **event-handler:** snippets split, improved, and lint ([#1279](https://github.com/aws-powertools/powertools-lambda-python/issues/1279)) +* **governance:** typos on PR template fixes [#1314](https://github.com/aws-powertools/powertools-lambda-python/issues/1314) +* **governance:** add security doc to the root + +## Maintenance + +* **ci:** limits concurrency for docs workflow +* **ci:** adds caching when installing python dependencies ([#1311](https://github.com/aws-powertools/powertools-lambda-python/issues/1311)) +* **ci:** update project with version 1.26.4 +* **ci:** fix reference error in related_issue +* **deps:** bump constructs from 10.1.1 to 10.1.51 ([#1323](https://github.com/aws-powertools/powertools-lambda-python/issues/1323)) +* **deps-dev:** bump mypy from 0.961 to 0.971 ([#1320](https://github.com/aws-powertools/powertools-lambda-python/issues/1320)) +* **governance:** fix typo on semantic commit link introduced in [#1](https://github.com/aws-powertools/powertools-lambda-python/issues/1)aef4 +* **layers:** add release pipeline in GitHub Actions ([#1278](https://github.com/aws-powertools/powertools-lambda-python/issues/1278)) +* **layers:** bump to 22 for 1.26.3 + + +<a name="v1.26.4"></a> +## [v1.26.4] - 2022-07-18 +## Bug Fixes + +* **ci:** checkout project before validating related issue workflow +* **ci:** fixes typos and small issues on github scripts ([#1302](https://github.com/aws-powertools/powertools-lambda-python/issues/1302)) +* **ci:** address conditional type on_merge +* **ci:** address pr title semantic not found logic +* **ci:** address gh-actions additional quotes; remove debug +* **ci:** regex group name for on_merge workflow +* **ci:** escape outputs as certain PRs can break GH Actions expressions +* **ci:** move conditionals from yaml to code; leftover +* **ci:** move conditionals from yaml to code +* **ci:** accept core arg in label related issue workflow +* **ci:** match the name of the cdk synth from the build phase +* **ci:** regex to catch combination of related issues workflow +* **logger:** preserve std keys when using custom formatters ([#1264](https://github.com/aws-powertools/powertools-lambda-python/issues/1264)) +* **parser:** raise ValidationError when SNS->SQS keys are intentionally missing ([#1299](https://github.com/aws-powertools/powertools-lambda-python/issues/1299)) + +## Documentation + +* **event-handler:** snippets split, improved, and lint ([#1279](https://github.com/aws-powertools/powertools-lambda-python/issues/1279)) +* **graphql:** snippets split, improved, and lint ([#1287](https://github.com/aws-powertools/powertools-lambda-python/issues/1287)) +* **homepage:** emphasize additional powertools languages ([#1292](https://github.com/aws-powertools/powertools-lambda-python/issues/1292)) +* **metrics:** snippets split, improved, and lint + +## Maintenance + +* **ci:** increase release automation and limit to one manual step ([#1297](https://github.com/aws-powertools/powertools-lambda-python/issues/1297)) +* **ci:** make export PR reusable +* **ci:** auto-merge cdk lib and lambda layer construct +* **ci:** convert inline gh-script to file +* **ci:** lockdown 3rd party workflows to pin sha ([#1301](https://github.com/aws-powertools/powertools-lambda-python/issues/1301)) +* **ci:** automatically add area label based on title ([#1300](https://github.com/aws-powertools/powertools-lambda-python/issues/1300)) +* **ci:** disable output debugging as pr body isnt accepted +* **ci:** experiment with conditional on outputs +* **ci:** improve error handling for non-issue numbers +* **ci:** add end to end testing mechanism ([#1247](https://github.com/aws-powertools/powertools-lambda-python/issues/1247)) +* **ci:** limits concurrency for docs workflow +* **ci:** fix reference error in related_issue +* **ci:** move error prone env to code as constants +* **ci:** move all scripts under .github/scripts +* **deps:** bump cdk-lambda-powertools-python-layer ([#1284](https://github.com/aws-powertools/powertools-lambda-python/issues/1284)) +* **deps:** bump jsii from 1.61.0 to 1.62.0 ([#1294](https://github.com/aws-powertools/powertools-lambda-python/issues/1294)) +* **deps:** bump constructs from 10.1.1 to 10.1.46 ([#1306](https://github.com/aws-powertools/powertools-lambda-python/issues/1306)) +* **deps:** bump actions/setup-node from 2 to 3 ([#1281](https://github.com/aws-powertools/powertools-lambda-python/issues/1281)) +* **deps:** bump fastjsonschema from 2.15.3 to 2.16.1 ([#1309](https://github.com/aws-powertools/powertools-lambda-python/issues/1309)) +* **deps:** bump constructs from 10.1.1 to 10.1.49 ([#1308](https://github.com/aws-powertools/powertools-lambda-python/issues/1308)) +* **deps:** bump attrs from 21.2.0 to 21.4.0 ([#1282](https://github.com/aws-powertools/powertools-lambda-python/issues/1282)) +* **deps:** bump aws-cdk-lib from 2.29.0 to 2.31.1 ([#1290](https://github.com/aws-powertools/powertools-lambda-python/issues/1290)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.24.12 to 1.24.27 ([#1293](https://github.com/aws-powertools/powertools-lambda-python/issues/1293)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.24.0 to 1.24.29 ([#1295](https://github.com/aws-powertools/powertools-lambda-python/issues/1295)) +* **governance:** remove any step relying on master branch +* **governance:** update emeritus affiliation +* **layers:** add release pipeline in GitHub Actions ([#1278](https://github.com/aws-powertools/powertools-lambda-python/issues/1278)) +* **layers:** bump to 22 for 1.26.3 + + +<a name="v1.26.3"></a> +## [v1.26.3] - 2022-07-04 +## Bug Fixes + +* **ci:** remove utf-8 body in octokit body req +* **ci:** improve msg visibility on closed issues +* **ci:** disable merged_pr workflow +* **ci:** merged_pr add issues write access +* **ci:** quote prBody GH expr on_opened_pr +* **ci:** reusable workflow secrets param +* **logger:** support additional args for handlers when injecting lambda context ([#1276](https://github.com/aws-powertools/powertools-lambda-python/issues/1276)) +* **logger:** preserve std keys when using custom formatters ([#1264](https://github.com/aws-powertools/powertools-lambda-python/issues/1264)) + +## Documentation + +* **lint:** add markdownlint rules and automation ([#1256](https://github.com/aws-powertools/powertools-lambda-python/issues/1256)) +* **logger:** document enriching logs with logrecord attributes ([#1271](https://github.com/aws-powertools/powertools-lambda-python/issues/1271)) +* **logger:** snippets split, improved, and lint ([#1262](https://github.com/aws-powertools/powertools-lambda-python/issues/1262)) +* **metrics:** snippets split, improved, and lint ([#1272](https://github.com/aws-powertools/powertools-lambda-python/issues/1272)) +* **tracer:** snippets split, improved, and lint ([#1261](https://github.com/aws-powertools/powertools-lambda-python/issues/1261)) +* **tracer:** split and lint code snippets ([#1260](https://github.com/aws-powertools/powertools-lambda-python/issues/1260)) + +## Maintenance + +* move to approach B for multiple IaC +* add sam build gitignore +* bump to version 1.26.3 +* **ci:** reactivate on_merged_pr workflow +* **ci:** improve wording on closed issues action +* **ci:** deactivate on_merged_pr workflow +* **deps:** bump aws-xray-sdk from 2.9.0 to 2.10.0 ([#1270](https://github.com/aws-powertools/powertools-lambda-python/issues/1270)) +* **deps:** bump dependabot/fetch-metadata from 1.1.1 to 1.3.2 ([#1269](https://github.com/aws-powertools/powertools-lambda-python/issues/1269)) +* **deps:** bump dependabot/fetch-metadata from 1.3.2 to 1.3.3 ([#1273](https://github.com/aws-powertools/powertools-lambda-python/issues/1273)) +* **deps-dev:** bump flake8-bugbear from 22.6.22 to 22.7.1 ([#1274](https://github.com/aws-powertools/powertools-lambda-python/issues/1274)) +* **deps-dev:** bump flake8-bugbear from 22.4.25 to 22.6.22 ([#1258](https://github.com/aws-powertools/powertools-lambda-python/issues/1258)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.24.0 to 1.24.12 ([#1255](https://github.com/aws-powertools/powertools-lambda-python/issues/1255)) +* **deps-dev:** bump mypy-boto3-secretsmanager ([#1252](https://github.com/aws-powertools/powertools-lambda-python/issues/1252)) +* **governance:** fix on_merged_pr workflow syntax +* **governance:** warn message on closed issues +* **layers:** bump to 21 for 1.26.2 +* **test-perf:** use pytest-benchmark to improve reliability ([#1250](https://github.com/aws-powertools/powertools-lambda-python/issues/1250)) + + +<a name="v1.26.2"></a> +## [v1.26.2] - 2022-06-16 +## Bug Fixes + +* **event-handler:** body to empty string in CORS preflight (ALB non-compliant) ([#1249](https://github.com/aws-powertools/powertools-lambda-python/issues/1249)) + +## Code Refactoring + +* rename to clear_state +* rename to remove_custom_keys + +## Documentation + +* fix anchor + +## Features + +* **logger:** add option to clear state per invocation + +## Maintenance + +* bump to 1.26.2 +* **deps:** bump actions/setup-python from 3 to 4 ([#1244](https://github.com/aws-powertools/powertools-lambda-python/issues/1244)) +* **deps-dev:** bump mypy from 0.960 to 0.961 ([#1241](https://github.com/aws-powertools/powertools-lambda-python/issues/1241)) +* **deps-dev:** bump mypy-boto3-ssm from 1.23.0.post1 to 1.24.0 ([#1231](https://github.com/aws-powertools/powertools-lambda-python/issues/1231)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.23.8 to 1.24.0 ([#1232](https://github.com/aws-powertools/powertools-lambda-python/issues/1232)) +* **deps-dev:** bump mypy-boto3-dynamodb from 1.23.0.post1 to 1.24.0 ([#1234](https://github.com/aws-powertools/powertools-lambda-python/issues/1234)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.23.0.post1 to 1.24.0 ([#1233](https://github.com/aws-powertools/powertools-lambda-python/issues/1233)) +* **governance:** auto-merge on all PR events +* **governance:** add release label on pr merge +* **governance:** enforce safe scope on pr merge labelling +* **governance:** limit build workflow to code changes only +* **governance:** auto-merge workflow_dispatch off +* **governance:** auto-merge to use squash +* **governance:** check for related issue in new PRs +* **governance:** auto-merge mypy-stub dependabot +* **governance:** address gh reusable workflow limitation +* **governance:** fix workflow action requirements & syntax +* **governance:** warn message on closed issues +* **metrics:** revert dimensions test before splitting ([#1243](https://github.com/aws-powertools/powertools-lambda-python/issues/1243)) + + +<a name="v1.26.1"></a> +## [v1.26.1] - 2022-06-07 +## Bug Fixes + +* **metrics:** raise SchemaValidationError for >8 metric dimensions ([#1240](https://github.com/aws-powertools/powertools-lambda-python/issues/1240)) + +## Documentation + +* **governance:** link roadmap and maintainers doc +* **maintainers:** initial maintainers playbook ([#1222](https://github.com/aws-powertools/powertools-lambda-python/issues/1222)) +* **roadmap:** use pinned pause issue instead + +## Maintenance + +* bump version 1.26.1 +* **deps-dev:** bump mypy from 0.950 to 0.960 ([#1224](https://github.com/aws-powertools/powertools-lambda-python/issues/1224)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.23.0.post1 to 1.23.8 ([#1225](https://github.com/aws-powertools/powertools-lambda-python/issues/1225)) + + +<a name="v1.26.0"></a> +## [v1.26.0] - 2022-05-20 +## Bug Fixes + +* **batch:** missing space in BatchProcessingError message ([#1201](https://github.com/aws-powertools/powertools-lambda-python/issues/1201)) +* **batch:** docstring fix for success_handler() record parameter ([#1202](https://github.com/aws-powertools/powertools-lambda-python/issues/1202)) +* **docs:** remove Slack link ([#1210](https://github.com/aws-powertools/powertools-lambda-python/issues/1210)) + +## Documentation + +* **layer:** upgrade to 1.25.10 +* **roadmap:** add new roadmap section ([#1204](https://github.com/aws-powertools/powertools-lambda-python/issues/1204)) + +## Features + +* **parameters:** accept boto3_client to support private endpoints and ease testing ([#1096](https://github.com/aws-powertools/powertools-lambda-python/issues/1096)) + +## Maintenance + +* bump to 1.26.0 +* **deps:** bump pydantic from 1.9.0 to 1.9.1 ([#1221](https://github.com/aws-powertools/powertools-lambda-python/issues/1221)) +* **deps:** bump email-validator from 1.1.3 to 1.2.1 ([#1199](https://github.com/aws-powertools/powertools-lambda-python/issues/1199)) +* **deps-dev:** bump mypy-boto3-secretsmanager from 1.21.34 to 1.23.0.post1 ([#1218](https://github.com/aws-powertools/powertools-lambda-python/issues/1218)) +* **deps-dev:** bump mypy-boto3-appconfig from 1.21.34 to 1.23.0.post1 ([#1219](https://github.com/aws-powertools/powertools-lambda-python/issues/1219)) +* **deps-dev:** bump mypy-boto3-ssm from 1.21.34 to 1.23.0.post1 ([#1220](https://github.com/aws-powertools/powertools-lambda-python/issues/1220)) + + +<a name="v1.25.10"></a> +## [v1.25.10] - 2022-04-29 +## Bug Fixes + +* **data-classes:** Add missing SES fields and ([#1045](https://github.com/aws-powertools/powertools-lambda-python/issues/1045)) +* **deps:** Ignore boto3 changes until needed ([#1151](https://github.com/aws-powertools/powertools-lambda-python/issues/1151)) +* **deps-dev:** remove jmespath due to dev deps conflict ([#1148](https://github.com/aws-powertools/powertools-lambda-python/issues/1148)) +* **event_handler:** exception_handler to handle ServiceError exceptions ([#1160](https://github.com/aws-powertools/powertools-lambda-python/issues/1160)) +* **event_handler:** Allow for event_source support ([#1159](https://github.com/aws-powertools/powertools-lambda-python/issues/1159)) +* **parser:** Add missing fields for SESEvent ([#1027](https://github.com/aws-powertools/powertools-lambda-python/issues/1027)) + +## Documentation + +* **layer:** upgrade to 1.25.9 + +## Features + +* **parameters:** add clear_cache method for providers ([#1194](https://github.com/aws-powertools/powertools-lambda-python/issues/1194)) + +## Maintenance + +* include regression in changelog +* bump to 1.25.10 +* **ci:** changelog pre-generation to fetch tags from origin +* **ci:** disable mergify configuration after breaking changes ([#1188](https://github.com/aws-powertools/powertools-lambda-python/issues/1188)) +* **ci:** post release on tagged issues too +* **deps:** bump codecov/codecov-action from 3.0.0 to 3.1.0 ([#1143](https://github.com/aws-powertools/powertools-lambda-python/issues/1143)) +* **deps:** bump github/codeql-action from 1 to 2 ([#1154](https://github.com/aws-powertools/powertools-lambda-python/issues/1154)) +* **deps-dev:** bump flake8-eradicate from 1.2.0 to 1.2.1 ([#1158](https://github.com/aws-powertools/powertools-lambda-python/issues/1158)) +* **deps-dev:** bump mypy from 0.942 to 0.950 ([#1162](https://github.com/aws-powertools/powertools-lambda-python/issues/1162)) +* **deps-dev:** bump mkdocs-git-revision-date-plugin ([#1146](https://github.com/aws-powertools/powertools-lambda-python/issues/1146)) +* **deps-dev:** bump flake8-bugbear from 22.1.11 to 22.4.25 ([#1156](https://github.com/aws-powertools/powertools-lambda-python/issues/1156)) +* **deps-dev:** bump xenon from 0.8.0 to 0.9.0 ([#1145](https://github.com/aws-powertools/powertools-lambda-python/issues/1145)) +* **deps-dev:** bump mypy from 0.931 to 0.942 ([#1133](https://github.com/aws-powertools/powertools-lambda-python/issues/1133)) + +## Regression + +* **parser:** Add missing fields for SESEvent ([#1027](https://github.com/aws-powertools/powertools-lambda-python/issues/1027)) ([#1190](https://github.com/aws-powertools/powertools-lambda-python/issues/1190)) + + +<a name="v1.25.9"></a> +## [v1.25.9] - 2022-04-21 +## Bug Fixes + +* **deps:** correct py36 marker for jmespath + +## Maintenance + +* bump to 1.25.9 + + +<a name="v1.25.8"></a> +## [v1.25.8] - 2022-04-21 +## Bug Fixes + +* removed ambiguous quotes from labels. +* **deps:** update jmespath marker to support 1.0 and py3.6 ([#1139](https://github.com/aws-powertools/powertools-lambda-python/issues/1139)) +* **governance:** update label in names in issues + +## Documentation + +* **install:** instructions to reduce pydantic package size ([#1077](https://github.com/aws-powertools/powertools-lambda-python/issues/1077)) +* **layer:** remove link from clipboard button ([#1135](https://github.com/aws-powertools/powertools-lambda-python/issues/1135)) +* **layer:** update to 1.25.7 + +## Maintenance + +* bump to 1.25.8 +* **deps:** bump codecov/codecov-action from 2.1.0 to 3.0.0 ([#1102](https://github.com/aws-powertools/powertools-lambda-python/issues/1102)) +* **deps:** bump actions/upload-artifact from 2 to 3 ([#1103](https://github.com/aws-powertools/powertools-lambda-python/issues/1103)) +* **deps-dev:** bump mkdocs-material from 8.2.4 to 8.2.7 ([#1131](https://github.com/aws-powertools/powertools-lambda-python/issues/1131)) +* **deps-dev:** bump pytest from 6.2.5 to 7.0.1 ([#1063](https://github.com/aws-powertools/powertools-lambda-python/issues/1063)) + + +<a name="v1.25.7"></a> +## [v1.25.7] - 2022-04-08 +## Bug Fixes + +* **api_gateway:** allow whitespace in routes' path parameter ([#1099](https://github.com/aws-powertools/powertools-lambda-python/issues/1099)) +* **api_gateway:** allow whitespace in routes' path parameter ([#1099](https://github.com/aws-powertools/powertools-lambda-python/issues/1099)) +* **idempotency:** pass by value on idem key to guard inadvert mutations ([#1090](https://github.com/aws-powertools/powertools-lambda-python/issues/1090)) +* **logger:** clear_state should keep custom key formats ([#1095](https://github.com/aws-powertools/powertools-lambda-python/issues/1095)) +* **middleware_factory:** ret type annotation for handler dec ([#1066](https://github.com/aws-powertools/powertools-lambda-python/issues/1066)) + +## Documentation + +* **layer:** update to 1.25.6; cosmetic changes + +## Maintenance + +* bump to 1.25.7 +* **governance:** refresh pull request template sections +* **governance:** update external non-triage effort disclaimer +* **governance:** update static typing to a form +* **governance:** update rfc to a form +* **governance:** update feat request to a form +* **governance:** bug report form typo +* **governance:** update docs report to a form +* **governance:** update bug report to a form +* **governance:** new ask a question +* **governance:** new static typing report + + +<a name="v1.25.6"></a> +## [v1.25.6] - 2022-04-01 +## Bug Fixes + +* **logger:** clear_state regression on absent standard keys ([#1088](https://github.com/aws-powertools/powertools-lambda-python/issues/1088)) + +## Documentation + +* **layer:** bump to 1.25.5 + +## Maintenance + +* bump to 1.25.6 + + +<a name="v1.25.5"></a> +## [v1.25.5] - 2022-03-18 +## Bug Fixes + +* **logger-utils:** regression on exclude set leading to no formatter ([#1080](https://github.com/aws-powertools/powertools-lambda-python/issues/1080)) + +## Maintenance + +* bump to 1.25.5 + + +<a name="v1.25.4"></a> +## [v1.25.4] - 2022-03-17 +## Bug Fixes + +* package_logger as const over logger instance +* repurpose test to cover parent loggers case +* use addHandler over monkeypatch + +## Documentation + +* **appsync:** fix typo +* **contributing:** operational excellence pause +* **layer:** update to 1.25.3 + +## Maintenance + +* bump to 1.25.4 +* remove duplicate test +* comment reason for change +* remove unnecessary test +* lint unused import + +## Regression + +* service_name fixture + +## Pull Requests + +* Merge pull request [#1075](https://github.com/aws-powertools/powertools-lambda-python/issues/1075) from mploski/fix/existing-loggers-duplicated-logs + + +<a name="v1.25.3"></a> +## [v1.25.3] - 2022-03-09 +## Bug Fixes + +* **logger:** ensure state is cleared for custom formatters ([#1072](https://github.com/aws-powertools/powertools-lambda-python/issues/1072)) + +## Documentation + +* **plugin:** add mermaid to create diagram as code ([#1070](https://github.com/aws-powertools/powertools-lambda-python/issues/1070)) + +## Maintenance + +* bump to 1.25.3 + + +<a name="v1.25.2"></a> +## [v1.25.2] - 2022-03-07 +## Bug Fixes + +* **event_handler:** docs snippets, high-level import CorsConfig ([#1019](https://github.com/aws-powertools/powertools-lambda-python/issues/1019)) +* **lambda-authorizer:** allow proxy resources path in arn ([#1051](https://github.com/aws-powertools/powertools-lambda-python/issues/1051)) +* **metrics:** flush upon a single metric 100th data point ([#1046](https://github.com/aws-powertools/powertools-lambda-python/issues/1046)) + +## Documentation + +* **layer:** update to 1.25.1 +* **parser:** APIGatewayProxyEvent to APIGatewayProxyEventModel ([#1061](https://github.com/aws-powertools/powertools-lambda-python/issues/1061)) + +## Maintenance -* **batch:** report multiple failures ([#967](https://github.com/awslabs/aws-lambda-powertools-python/issues/967)) -* **data-classes:** docstring typos and clean up ([#937](https://github.com/awslabs/aws-lambda-powertools-python/issues/937)) -* **parameters:** appconfig internal _get docstrings ([#934](https://github.com/awslabs/aws-lambda-powertools-python/issues/934)) +* bump to 1.25.2 +* **deps:** bump actions/setup-python from 2.3.1 to 3 ([#1048](https://github.com/aws-powertools/powertools-lambda-python/issues/1048)) +* **deps:** bump actions/checkout from 2 to 3 ([#1052](https://github.com/aws-powertools/powertools-lambda-python/issues/1052)) +* **deps:** bump actions/github-script from 5 to 6 ([#1023](https://github.com/aws-powertools/powertools-lambda-python/issues/1023)) +* **deps:** bump fastjsonschema from 2.15.2 to 2.15.3 ([#949](https://github.com/aws-powertools/powertools-lambda-python/issues/949)) +* **deps-dev:** bump mkdocs-material from 8.1.9 to 8.2.4 ([#1054](https://github.com/aws-powertools/powertools-lambda-python/issues/1054)) -### Documentation + +<a name="v1.25.1"></a> +## [v1.25.1] - 2022-02-14 +## Bug Fixes + +* **batch:** bugfix to clear exceptions between executions ([#1022](https://github.com/aws-powertools/powertools-lambda-python/issues/1022)) + +## Maintenance + +* bump to 1.25.1 +* **layers:** bump to 10 for 1.25.0 + + +<a name="v1.25.0"></a> +## [v1.25.0] - 2022-02-09 +## Bug Fixes + +* **apigateway:** remove indentation in debug_mode ([#987](https://github.com/aws-powertools/powertools-lambda-python/issues/987)) +* **batch:** delete >10 messages in legacy sqs processor ([#818](https://github.com/aws-powertools/powertools-lambda-python/issues/818)) +* **ci:** pr label regex for special chars in title +* **logger:** exclude source_logger in copy_config_to_registered_loggers ([#1001](https://github.com/aws-powertools/powertools-lambda-python/issues/1001)) +* **logger:** test generates logfile + +## Documentation + +* fix syntax errors and line highlights ([#1004](https://github.com/aws-powertools/powertools-lambda-python/issues/1004)) +* add better BDD coments +* **event-handler:** improve testing section for graphql ([#996](https://github.com/aws-powertools/powertools-lambda-python/issues/996)) +* **layer:** update to 1.24.2 +* **parameters:** add testing your code section ([#1017](https://github.com/aws-powertools/powertools-lambda-python/issues/1017)) +* **theme:** upgrade mkdocs-material to 8.x ([#1002](https://github.com/aws-powertools/powertools-lambda-python/issues/1002)) +* **tutorial:** fix broken internal links ([#1000](https://github.com/aws-powertools/powertools-lambda-python/issues/1000)) + +## Features + +* **event-handler:** new resolvers to fix current_event typing ([#978](https://github.com/aws-powertools/powertools-lambda-python/issues/978)) +* **logger:** log_event support event data classes (e.g. S3Event) ([#984](https://github.com/aws-powertools/powertools-lambda-python/issues/984)) +* **mypy:** complete mypy support for the entire codebase ([#943](https://github.com/aws-powertools/powertools-lambda-python/issues/943)) + +## Maintenance + +* bump to 1.25.0 +* correct docs +* correct docs +* use isinstance over type +* **deps-dev:** bump flake8-bugbear from 21.11.29 to 22.1.11 ([#955](https://github.com/aws-powertools/powertools-lambda-python/issues/955)) +* **metrics:** fix tests when warnings are disabled ([#994](https://github.com/aws-powertools/powertools-lambda-python/issues/994)) + +## Pull Requests + +* Merge pull request [#971](https://github.com/aws-powertools/powertools-lambda-python/issues/971) from gyft/fix-logger-util-tests + + +<a name="v1.24.2"></a> +## [v1.24.2] - 2022-01-21 +## Bug Fixes + +* **data-classes:** underscore support in api gateway authorizer resource name ([#969](https://github.com/aws-powertools/powertools-lambda-python/issues/969)) + +## Documentation + +* **layer:** update to 1.24.1 + +## Maintenance + +* bump to 1.24.2 + + +<a name="v1.24.1"></a> +## [v1.24.1] - 2022-01-20 +## Bug Fixes + +* remove unused json import +* remove apigw contract when using event-handler, apigw tracing +* use decorators, split cold start to ease reading +* incorrect log keys, indentation, snippet consistency +* remove f-strings that doesn't evaluate expr +* **batch:** report multiple failures ([#967](https://github.com/aws-powertools/powertools-lambda-python/issues/967)) +* **data-classes:** docstring typos and clean up ([#937](https://github.com/aws-powertools/powertools-lambda-python/issues/937)) +* **parameters:** appconfig internal _get docstrings ([#934](https://github.com/aws-powertools/powertools-lambda-python/issues/934)) + +## Documentation * rename quickstart to tutorial in readme -* **batch:** snippet typo on batch processed messages iteration ([#951](https://github.com/awslabs/aws-lambda-powertools-python/issues/951)) -* **batch:** fix typo in context manager keyword ([#938](https://github.com/awslabs/aws-lambda-powertools-python/issues/938)) -* **homepage:** link to typescript version ([#950](https://github.com/awslabs/aws-lambda-powertools-python/issues/950)) +* rename to tutorial given the size +* add final consideration section +* **batch:** snippet typo on batch processed messages iteration ([#951](https://github.com/aws-powertools/powertools-lambda-python/issues/951)) +* **batch:** fix typo in context manager keyword ([#938](https://github.com/aws-powertools/powertools-lambda-python/issues/938)) +* **homepage:** link to typescript version ([#950](https://github.com/aws-powertools/powertools-lambda-python/issues/950)) * **install:** new lambda layer for 1.24.0 release * **metrics:** keep it consistent with other sections, update metric names -* **nav:** make REST and GraphQL event handlers more explicit ([#959](https://github.com/awslabs/aws-lambda-powertools-python/issues/959)) +* **nav:** make REST and GraphQL event handlers more explicit ([#959](https://github.com/aws-powertools/powertools-lambda-python/issues/959)) * **quickstart:** expand on intro line * **quickstart:** tidy requirements up +* **quickstart:** make section agnostic to json lib * **quickstart:** same process for Logger * **quickstart:** add sub-sections, fix highlight & code * **quickstart:** sentence fragmentation, tidy up -* **quickstart:** make section agnostic to json lib * **tenets:** make core, non-core more explicit -* **tracer:** update ServiceLens image w/ API GW, copywriting * **tracer:** warning to note on local traces * **tracer:** add initial image, requirements * **tracer:** add annotation, metadata, and image +* **tracer:** update ServiceLens image w/ API GW, copywriting +* **tutorial:** fix path to images ([#963](https://github.com/aws-powertools/powertools-lambda-python/issues/963)) + +## Features + +* **ci:** auto-notify & close issues on release +* **logger:** clone powertools logger config to any Python logger ([#927](https://github.com/aws-powertools/powertools-lambda-python/issues/927)) + +## Maintenance + +* bump to 1.24.1 +* bump to 1.24.1 +* **ci:** run codeql analysis on push only +* **ci:** fix mergify dependabot queue +* **ci:** add queue name in mergify +* **ci:** remove mergify legacy key +* **ci:** update mergify bot breaking change +* **ci:** safely label PR based on title +* **deps:** bump pydantic from 1.8.2 to 1.9.0 ([#933](https://github.com/aws-powertools/powertools-lambda-python/issues/933)) +* **deps-dev:** bump mypy from 0.930 to 0.931 ([#941](https://github.com/aws-powertools/powertools-lambda-python/issues/941)) + +## Regression + +* order to APP logger/service name due to screenshots -## 1.24.0 - 2021-12-31 +## Pull Requests -### Bug Fixes +* Merge pull request [#769](https://github.com/aws-powertools/powertools-lambda-python/issues/769) from mploski/docs/quick-start -* **apigateway:** support [@app](https://github.com/app).not_found() syntax & housekeeping ([#926](https://github.com/awslabs/aws-lambda-powertools-python/issues/926)) -* **event-sources:** handle dynamodb null type as none, not bool ([#929](https://github.com/awslabs/aws-lambda-powertools-python/issues/929)) -* **warning:** future distutils deprecation in 3.12 ([#921](https://github.com/awslabs/aws-lambda-powertools-python/issues/921)) -### Documentation +<a name="v1.24.0"></a> +## [v1.24.0] - 2021-12-31 +## Bug Fixes + +* **apigateway:** support [@app](https://github.com/app).not_found() syntax & housekeeping ([#926](https://github.com/aws-powertools/powertools-lambda-python/issues/926)) +* **event-sources:** handle dynamodb null type as none, not bool ([#929](https://github.com/aws-powertools/powertools-lambda-python/issues/929)) +* **warning:** future distutils deprecation ([#921](https://github.com/aws-powertools/powertools-lambda-python/issues/921)) -* **general**: consistency round admonitions and snippets ([#919](https://github.com/awslabs/aws-lambda-powertools-python/issues/919)) -* **homepage**: Added GraphQL Sample API to Examples section of README.md ([#930](https://github.com/awslabs/aws-lambda-powertools-python/issues/930)) +## Documentation + +* consistency around admonitions and snippets ([#919](https://github.com/aws-powertools/powertools-lambda-python/issues/919)) +* Added GraphQL Sample API to Examples section of README.md ([#930](https://github.com/aws-powertools/powertools-lambda-python/issues/930)) * **batch:** remove leftover from legacy * **layer:** bump Lambda Layer to version 6 -* **tracer:** new ignore_endpoint feature ([#931](https://github.com/awslabs/aws-lambda-powertools-python/issues/931)) +* **tracer:** new ignore_endpoint feature ([#931](https://github.com/aws-powertools/powertools-lambda-python/issues/931)) -### Features +## Features -* **event-sources:** cache parsed json in data class ([#909](https://github.com/awslabs/aws-lambda-powertools-python/issues/909)) -* **feature_flags:** support beyond boolean values (JSON values) ([#804](https://github.com/awslabs/aws-lambda-powertools-python/issues/804)) -* **idempotency:** support dataclasses & pydantic models payloads ([#908](https://github.com/awslabs/aws-lambda-powertools-python/issues/908)) -* **logger:** support use_datetime_directive for timestamps ([#920](https://github.com/awslabs/aws-lambda-powertools-python/issues/920)) -* **tracer:** ignore tracing for certain hostname(s) or url(s) ([#910](https://github.com/awslabs/aws-lambda-powertools-python/issues/910)) +* **event-sources:** cache parsed json in data class ([#909](https://github.com/aws-powertools/powertools-lambda-python/issues/909)) +* **feature_flags:** support beyond boolean values (JSON values) ([#804](https://github.com/aws-powertools/powertools-lambda-python/issues/804)) +* **idempotency:** support dataclasses & pydantic models payloads ([#908](https://github.com/aws-powertools/powertools-lambda-python/issues/908)) +* **logger:** support use_datetime_directive for timestamps ([#920](https://github.com/aws-powertools/powertools-lambda-python/issues/920)) +* **tracer:** ignore tracing for certain hostname(s) or url(s) ([#910](https://github.com/aws-powertools/powertools-lambda-python/issues/910)) -### Maintenance +## Maintenance -* **deps-dev:** bump mypy from 0.920 to 0.930 ([#925](https://github.com/awslabs/aws-lambda-powertools-python/issues/925)) +* bump to 1.24.0 +* **deps-dev:** bump mypy from 0.920 to 0.930 ([#925](https://github.com/aws-powertools/powertools-lambda-python/issues/925)) -## 1.23.0 - 2021-12-20 -### Bug Fixes +<a name="v1.23.0"></a> +## [v1.23.0] - 2021-12-20 +## Bug Fixes -* **apigateway:** allow list of HTTP methods in route method ([#838](https://github.com/awslabs/aws-lambda-powertools-python/issues/838)) -* **event-sources:** pass authorizer data to APIGatewayEventAuthorizer ([#897](https://github.com/awslabs/aws-lambda-powertools-python/issues/897)) -* **event-sources:** handle claimsOverrideDetails set to null ([#878](https://github.com/awslabs/aws-lambda-powertools-python/issues/878)) -* **idempotency:** include decorated fn name in hash ([#869](https://github.com/awslabs/aws-lambda-powertools-python/issues/869)) -* **metrics:** explicit type to single_metric ctx manager ([#865](https://github.com/awslabs/aws-lambda-powertools-python/issues/865)) -* **parameters:** mypy appconfig transform and return types ([#877](https://github.com/awslabs/aws-lambda-powertools-python/issues/877)) -* **parser:** mypy overload parse when using envelope ([#885](https://github.com/awslabs/aws-lambda-powertools-python/issues/885)) -* **parser:** kinesis sequence number is str, not int ([#907](https://github.com/awslabs/aws-lambda-powertools-python/issues/907)) -* **parser:** mypy support for payload type override as models ([#883](https://github.com/awslabs/aws-lambda-powertools-python/issues/883)) -* **tracer:** add warm start annotation (ColdStart=False) ([#851](https://github.com/awslabs/aws-lambda-powertools-python/issues/851)) +* **apigateway:** allow list of HTTP methods in route method ([#838](https://github.com/aws-powertools/powertools-lambda-python/issues/838)) +* **event-sources:** Pass authorizer data to APIGatewayEventAuthorizer ([#897](https://github.com/aws-powertools/powertools-lambda-python/issues/897)) +* **event-sources:** handle claimsOverrideDetails set to null ([#878](https://github.com/aws-powertools/powertools-lambda-python/issues/878)) +* **idempotency:** include decorated fn name in hash ([#869](https://github.com/aws-powertools/powertools-lambda-python/issues/869)) +* **metrics:** explicit type to single_metric ctx manager ([#865](https://github.com/aws-powertools/powertools-lambda-python/issues/865)) +* **parameters:** appconfig transform and return types ([#877](https://github.com/aws-powertools/powertools-lambda-python/issues/877)) +* **parser:** overload parse when using envelope ([#885](https://github.com/aws-powertools/powertools-lambda-python/issues/885)) +* **parser:** kinesis sequence number is str, not int ([#907](https://github.com/aws-powertools/powertools-lambda-python/issues/907)) +* **parser:** mypy support for payload type override as models ([#883](https://github.com/aws-powertools/powertools-lambda-python/issues/883)) +* **tracer:** add warm start annotation (ColdStart=False) ([#851](https://github.com/aws-powertools/powertools-lambda-python/issues/851)) -### Documentation +## Documentation -* **nav**: reference cloudformation custom resource helper (CRD) ([#914](https://github.com/awslabs/aws-lambda-powertools-python/issues/914)) +* external reference to cloudformation custom resource helper ([#914](https://github.com/aws-powertools/powertools-lambda-python/issues/914)) * add new public Slack invite * disable search blur in non-prod env * update Lambda Layers version -* **apigateway:** add new not_found feature ([#915](https://github.com/awslabs/aws-lambda-powertools-python/issues/915)) -* **apigateway:** fix sample layout provided ([#864](https://github.com/awslabs/aws-lambda-powertools-python/issues/864)) -* **appsync:** fix users.py typo to locations [#830](https://github.com/awslabs/aws-lambda-powertools-python/issues/830) +* **apigateway:** add new not_found feature ([#915](https://github.com/aws-powertools/powertools-lambda-python/issues/915)) +* **apigateway:** fix sample layout provided ([#864](https://github.com/aws-powertools/powertools-lambda-python/issues/864)) +* **appsync:** fix users.py typo to locations [#830](https://github.com/aws-powertools/powertools-lambda-python/issues/830) * **lambda_layer:** fix CDK layer syntax -### Features +## Features -* **apigateway:** add exception_handler support ([#898](https://github.com/awslabs/aws-lambda-powertools-python/issues/898)) -* **apigateway:** access parent api resolver from router ([#842](https://github.com/awslabs/aws-lambda-powertools-python/issues/842)) -* **batch:** new BatchProcessor for SQS, DynamoDB, Kinesis ([#886](https://github.com/awslabs/aws-lambda-powertools-python/issues/886)) -* **logger:** allow handler with custom kwargs signature ([#913](https://github.com/awslabs/aws-lambda-powertools-python/issues/913)) -* **tracer:** add service annotation when service is set ([#861](https://github.com/awslabs/aws-lambda-powertools-python/issues/861)) +* **apigateway:** add exception_handler support ([#898](https://github.com/aws-powertools/powertools-lambda-python/issues/898)) +* **apigateway:** access parent api resolver from router ([#842](https://github.com/aws-powertools/powertools-lambda-python/issues/842)) +* **batch:** new BatchProcessor for SQS, DynamoDB, Kinesis ([#886](https://github.com/aws-powertools/powertools-lambda-python/issues/886)) +* **logger:** allow handler with custom kwargs signature ([#913](https://github.com/aws-powertools/powertools-lambda-python/issues/913)) +* **tracer:** add service annotation when service is set ([#861](https://github.com/aws-powertools/powertools-lambda-python/issues/861)) -### Maintenance +## Maintenance -* minor housekeeping before release ([#912](https://github.com/awslabs/aws-lambda-powertools-python/issues/912)) * correct pr label order +* minor housekeeping before release ([#912](https://github.com/aws-powertools/powertools-lambda-python/issues/912)) +* bump to 1.23.0 * **ci:** split latest docs workflow -* **deps:** bump fastjsonschema from 2.15.1 to 2.15.2 ([#891](https://github.com/awslabs/aws-lambda-powertools-python/issues/891)) -* **deps:** bump actions/setup-python from 2.2.2 to 2.3.0 ([#831](https://github.com/awslabs/aws-lambda-powertools-python/issues/831)) -* **deps:** support arm64 when developing locally ([#862](https://github.com/awslabs/aws-lambda-powertools-python/issues/862)) -* **deps:** bump actions/setup-python from 2.3.0 to 2.3.1 ([#852](https://github.com/awslabs/aws-lambda-powertools-python/issues/852)) -* **deps:** bump aws-xray-sdk from 2.8.0 to 2.9.0 ([#876](https://github.com/awslabs/aws-lambda-powertools-python/issues/876)) -* **deps-dev:** bump mypy from 0.910 to 0.920 ([#903](https://github.com/awslabs/aws-lambda-powertools-python/issues/903)) -* **deps-dev:** bump flake8 from 3.9.2 to 4.0.1 ([#789](https://github.com/awslabs/aws-lambda-powertools-python/issues/789)) -* **deps-dev:** bump black from 21.10b0 to 21.11b1 ([#839](https://github.com/awslabs/aws-lambda-powertools-python/issues/839)) -* **deps-dev:** bump black from 21.11b1 to 21.12b0 ([#872](https://github.com/awslabs/aws-lambda-powertools-python/issues/872)) - -## 1.22.0 - 2021-11-17 - -Tenet update! We've updated **Idiomatic** tenet to **Progressive** to reflect the new Router feature in Event Handler, and more importantly the new wave of customers coming from SRE, Data Analysis, and Data Science background. - -* BEFORE: **Idiomatic**. Utilities follow programming language idioms and language-specific best practices. -* AFTER: **Progressive**. Utilities are designed to be incrementally adoptable for customers at any stage of their Serverless journey. They follow language idioms and their community’s common practices. -### Bug Fixes +* **deps:** bump fastjsonschema from 2.15.1 to 2.15.2 ([#891](https://github.com/aws-powertools/powertools-lambda-python/issues/891)) +* **deps:** bump actions/setup-python from 2.2.2 to 2.3.0 ([#831](https://github.com/aws-powertools/powertools-lambda-python/issues/831)) +* **deps:** bump aws-xray-sdk from 2.8.0 to 2.9.0 ([#876](https://github.com/aws-powertools/powertools-lambda-python/issues/876)) +* **deps:** support arm64 when developing locally ([#862](https://github.com/aws-powertools/powertools-lambda-python/issues/862)) +* **deps:** bump actions/setup-python from 2.3.0 to 2.3.1 ([#852](https://github.com/aws-powertools/powertools-lambda-python/issues/852)) +* **deps-dev:** bump flake8 from 3.9.2 to 4.0.1 ([#789](https://github.com/aws-powertools/powertools-lambda-python/issues/789)) +* **deps-dev:** bump black from 21.10b0 to 21.11b1 ([#839](https://github.com/aws-powertools/powertools-lambda-python/issues/839)) +* **deps-dev:** bump black from 21.11b1 to 21.12b0 ([#872](https://github.com/aws-powertools/powertools-lambda-python/issues/872)) +* **deps-dev:** bump mypy from 0.910 to 0.920 ([#903](https://github.com/aws-powertools/powertools-lambda-python/issues/903)) + + +<a name="v1.22.0"></a> +## [v1.22.0] - 2021-11-17 +## Bug Fixes -* **ci:** change supported python version from 3.6.1 to 3.6.2, bump black ([#807](https://github.com/awslabs/aws-lambda-powertools-python/issues/807)) +* change supported python version from 3.6.1 to 3.6.2, bump black ([#807](https://github.com/aws-powertools/powertools-lambda-python/issues/807)) +* **ci:** comment custom publish version checker * **ci:** skip sync master on docs hotfix -* **parser:** body and query strings can be null or omitted in ApiGatewayProxyEventModel and ApiGatewayProxyEventV2Model ([#820](https://github.com/awslabs/aws-lambda-powertools-python/issues/820)) - -### Code Refactoring - -* **apigateway:** Add BaseRouter and duplicate route check ([#757](https://github.com/awslabs/aws-lambda-powertools-python/issues/757)) - -### Documentation - -* **docs:** updated Lambda Layers definition & limitations. ([#775](https://github.com/awslabs/aws-lambda-powertools-python/issues/775)) -* **docs:** Idiomatic tenet updated to Progressive -* **docs:** use higher contrast font to improve accessibility ([#822](https://github.com/awslabs/aws-lambda-powertools-python/issues/822)) -* **docs:** fix indentation of SAM snippets in install section ([#778](https://github.com/awslabs/aws-lambda-powertools-python/issues/778)) -* **docs:** improve public lambda layer wording, add clipboard buttons to improve UX ([#762](https://github.com/awslabs/aws-lambda-powertools-python/issues/762)) -* **docs:** add amplify-cli instructions for public layer ([#754](https://github.com/awslabs/aws-lambda-powertools-python/issues/754)) -* **api-gateway:** add new router feature to allow route splitting in API Gateway and ALB ([#767](https://github.com/awslabs/aws-lambda-powertools-python/issues/767)) -* **apigateway:** re-add sample layout, add considerations ([#826](https://github.com/awslabs/aws-lambda-powertools-python/issues/826)) -* **appsync:** add new router feature to allow GraphQL Resolver composition ([#821](https://github.com/awslabs/aws-lambda-powertools-python/issues/821)) -* **idempotency:** add support for DynamoDB composite keys ([#808](https://github.com/awslabs/aws-lambda-powertools-python/issues/808)) -* **tenets:** update Idiomatic tenet to Progressive ([#823](https://github.com/awslabs/aws-lambda-powertools-python/issues/823)) -* **docs:** remove Lambda Layer version tag -### Features - -* **apigateway:** add Router to allow large routing composition ([#645](https://github.com/awslabs/aws-lambda-powertools-python/issues/645)) -* **appsync:** add Router to allow large resolver composition ([#776](https://github.com/awslabs/aws-lambda-powertools-python/issues/776)) -* **data-classes:** ActiveMQ and RabbitMQ support ([#770](https://github.com/awslabs/aws-lambda-powertools-python/issues/770)) -* **logger:** add ALB correlation ID support ([#816](https://github.com/awslabs/aws-lambda-powertools-python/issues/816)) - -### Maintenance - -* **deps:** bump boto3 from 1.19.6 to 1.20.3 ([#809](https://github.com/awslabs/aws-lambda-powertools-python/issues/809)) -* **deps:** bump boto3 from 1.18.58 to 1.18.59 ([#760](https://github.com/awslabs/aws-lambda-powertools-python/issues/760)) -* **deps:** bump urllib3 from 1.26.4 to 1.26.5 ([#787](https://github.com/awslabs/aws-lambda-powertools-python/issues/787)) -* **deps:** bump boto3 from 1.18.61 to 1.19.6 ([#783](https://github.com/awslabs/aws-lambda-powertools-python/issues/783)) -* **deps:** bump boto3 from 1.18.56 to 1.18.58 ([#755](https://github.com/awslabs/aws-lambda-powertools-python/issues/755)) -* **deps:** bump boto3 from 1.18.59 to 1.18.61 ([#766](https://github.com/awslabs/aws-lambda-powertools-python/issues/766)) -* **deps:** bump boto3 from 1.20.3 to 1.20.5 ([#817](https://github.com/awslabs/aws-lambda-powertools-python/issues/817)) -* **deps-dev:** bump coverage from 6.0.1 to 6.0.2 ([#764](https://github.com/awslabs/aws-lambda-powertools-python/issues/764)) -* **deps-dev:** bump pytest-asyncio from 0.15.1 to 0.16.0 ([#782](https://github.com/awslabs/aws-lambda-powertools-python/issues/782)) -* **deps-dev:** bump flake8-eradicate from 1.1.0 to 1.2.0 ([#784](https://github.com/awslabs/aws-lambda-powertools-python/issues/784)) -* **deps-dev:** bump flake8-comprehensions from 3.6.1 to 3.7.0 ([#759](https://github.com/awslabs/aws-lambda-powertools-python/issues/759)) -* **deps-dev:** bump flake8-isort from 4.0.0 to 4.1.1 ([#785](https://github.com/awslabs/aws-lambda-powertools-python/issues/785)) -* **deps-dev:** bump coverage from 6.0 to 6.0.1 ([#751](https://github.com/awslabs/aws-lambda-powertools-python/issues/751)) -* **deps-dev:** bump mkdocs-material from 7.3.3 to 7.3.5 ([#781](https://github.com/awslabs/aws-lambda-powertools-python/issues/781)) -* **deps-dev:** bump mkdocs-material from 7.3.5 to 7.3.6 ([#791](https://github.com/awslabs/aws-lambda-powertools-python/issues/791)) -* **deps-dev:** bump mkdocs-material from 7.3.2 to 7.3.3 ([#758](https://github.com/awslabs/aws-lambda-powertools-python/issues/758)) - -## 1.21.1 - 2021-10-07 - -### Regression - -* **metrics:** typing regression on log_metrics callable ([#744](https://github.com/awslabs/aws-lambda-powertools-python/issues/744)) - -### Documentation - -* add new public layer ARNs ([#746](https://github.com/awslabs/aws-lambda-powertools-python/issues/746)) -### Maintenance - -* ignore constants in test cov ([#745](https://github.com/awslabs/aws-lambda-powertools-python/issues/745)) -* github-actions: add support for publishing fallback -* **deps:** bump boto3 from 1.18.54 to 1.18.56 ([#742](https://github.com/awslabs/aws-lambda-powertools-python/issues/742)) -* **deps-dev:** bump mkdocs-material from 7.3.1 to 7.3.2 ([#741](https://github.com/awslabs/aws-lambda-powertools-python/issues/741)) - -## 1.21.0 - 2021-10-05 - -### Bug Fixes - -* **data-classes:** use correct asdict funciton ([#666](https://github.com/awslabs/aws-lambda-powertools-python/issues/666)) -* **feature-flags:** rules should evaluate with an AND op ([#724](https://github.com/awslabs/aws-lambda-powertools-python/issues/724)) -* **idempotency:** sorting keys before hashing ([#722](https://github.com/awslabs/aws-lambda-powertools-python/issues/722)) +* **parser:** body/QS can be null or omitted in apigw v1/v2 ([#820](https://github.com/aws-powertools/powertools-lambda-python/issues/820)) + +## Code Refactoring + +* **apigateway:** Add BaseRouter and duplicate route check ([#757](https://github.com/aws-powertools/powertools-lambda-python/issues/757)) + +## Documentation + +* updated Lambda Layers definition & limitations. ([#775](https://github.com/aws-powertools/powertools-lambda-python/issues/775)) +* Idiomatic tenet updated to Progressive +* use higher contrast font ([#822](https://github.com/aws-powertools/powertools-lambda-python/issues/822)) +* use higher contrast font +* fix indentation of SAM snippets in install section ([#778](https://github.com/aws-powertools/powertools-lambda-python/issues/778)) +* improve public lambda layer wording, clipboard buttons ([#762](https://github.com/aws-powertools/powertools-lambda-python/issues/762)) +* add amplify-cli instructions for public layer ([#754](https://github.com/aws-powertools/powertools-lambda-python/issues/754)) +* **api-gateway:** add support for new router feature ([#767](https://github.com/aws-powertools/powertools-lambda-python/issues/767)) +* **apigateway:** re-add sample layout, add considerations ([#826](https://github.com/aws-powertools/powertools-lambda-python/issues/826)) +* **appsync:** add new router feature ([#821](https://github.com/aws-powertools/powertools-lambda-python/issues/821)) +* **idempotency:** add support for DynamoDB composite keys ([#808](https://github.com/aws-powertools/powertools-lambda-python/issues/808)) +* **tenets:** update Idiomatic tenet to Progressive ([#823](https://github.com/aws-powertools/powertools-lambda-python/issues/823)) + +## Features + +* **apigateway:** add Router to allow large routing composition ([#645](https://github.com/aws-powertools/powertools-lambda-python/issues/645)) +* **appsync:** add Router to allow large resolver composition ([#776](https://github.com/aws-powertools/powertools-lambda-python/issues/776)) +* **data-classes:** ActiveMQ and RabbitMQ support ([#770](https://github.com/aws-powertools/powertools-lambda-python/issues/770)) +* **logger:** add ALB correlation ID support ([#816](https://github.com/aws-powertools/powertools-lambda-python/issues/816)) + +## Maintenance + +* fix var expr +* remove Lambda Layer version tag +* bump to 1.22.0 +* conditional to publish docs only attempt 3 +* conditional to publish docs only attempt 2 +* conditional to publish docs only +* **deps:** bump boto3 from 1.18.58 to 1.18.59 ([#760](https://github.com/aws-powertools/powertools-lambda-python/issues/760)) +* **deps:** bump boto3 from 1.18.56 to 1.18.58 ([#755](https://github.com/aws-powertools/powertools-lambda-python/issues/755)) +* **deps:** bump urllib3 from 1.26.4 to 1.26.5 ([#787](https://github.com/aws-powertools/powertools-lambda-python/issues/787)) +* **deps:** bump boto3 from 1.19.6 to 1.20.3 ([#809](https://github.com/aws-powertools/powertools-lambda-python/issues/809)) +* **deps:** bump boto3 from 1.18.61 to 1.19.6 ([#783](https://github.com/aws-powertools/powertools-lambda-python/issues/783)) +* **deps:** bump boto3 from 1.20.3 to 1.20.5 ([#817](https://github.com/aws-powertools/powertools-lambda-python/issues/817)) +* **deps:** bump boto3 from 1.18.59 to 1.18.61 ([#766](https://github.com/aws-powertools/powertools-lambda-python/issues/766)) +* **deps-dev:** bump coverage from 6.0.1 to 6.0.2 ([#764](https://github.com/aws-powertools/powertools-lambda-python/issues/764)) +* **deps-dev:** bump pytest-asyncio from 0.15.1 to 0.16.0 ([#782](https://github.com/aws-powertools/powertools-lambda-python/issues/782)) +* **deps-dev:** bump flake8-eradicate from 1.1.0 to 1.2.0 ([#784](https://github.com/aws-powertools/powertools-lambda-python/issues/784)) +* **deps-dev:** bump flake8-isort from 4.0.0 to 4.1.1 ([#785](https://github.com/aws-powertools/powertools-lambda-python/issues/785)) +* **deps-dev:** bump mkdocs-material from 7.3.2 to 7.3.3 ([#758](https://github.com/aws-powertools/powertools-lambda-python/issues/758)) +* **deps-dev:** bump flake8-comprehensions from 3.6.1 to 3.7.0 ([#759](https://github.com/aws-powertools/powertools-lambda-python/issues/759)) +* **deps-dev:** bump mkdocs-material from 7.3.3 to 7.3.5 ([#781](https://github.com/aws-powertools/powertools-lambda-python/issues/781)) +* **deps-dev:** bump coverage from 6.0 to 6.0.1 ([#751](https://github.com/aws-powertools/powertools-lambda-python/issues/751)) +* **deps-dev:** bump mkdocs-material from 7.3.5 to 7.3.6 ([#791](https://github.com/aws-powertools/powertools-lambda-python/issues/791)) +* **deps-dev:** bump coverage from 6.0.2 to 6.1.2 ([#810](https://github.com/aws-powertools/powertools-lambda-python/issues/810)) +* **deps-dev:** bump isort from 5.9.3 to 5.10.1 ([#811](https://github.com/aws-powertools/powertools-lambda-python/issues/811)) + + +<a name="v1.21.1"></a> +## [v1.21.1] - 2021-10-07 +## Documentation + +* add new public layer ARNs ([#746](https://github.com/aws-powertools/powertools-lambda-python/issues/746)) + +## Maintenance + +* include public layers changelog +* bump to 1.21.1 +* include regression in changelog +* ignore constants in test cov ([#745](https://github.com/aws-powertools/powertools-lambda-python/issues/745)) +* ignore constants in tests cov +* add support for publishing fallback +* **deps:** bump boto3 from 1.18.54 to 1.18.56 ([#742](https://github.com/aws-powertools/powertools-lambda-python/issues/742)) +* **deps-dev:** bump mkdocs-material from 7.3.1 to 7.3.2 ([#741](https://github.com/aws-powertools/powertools-lambda-python/issues/741)) + +## Regression + +* **metrics:** typing regression on log_metrics callable ([#744](https://github.com/aws-powertools/powertools-lambda-python/issues/744)) + + +<a name="v1.21.0"></a> +## [v1.21.0] - 2021-10-05 +## Bug Fixes + +* **data-classes:** use correct asdict funciton ([#666](https://github.com/aws-powertools/powertools-lambda-python/issues/666)) +* **feature-flags:** rules should evaluate with an AND op ([#724](https://github.com/aws-powertools/powertools-lambda-python/issues/724)) +* **idempotency:** sorting keys before hashing ([#722](https://github.com/aws-powertools/powertools-lambda-python/issues/722)) * **idempotency:** sorting keys before hashing -* **logger:** push extra keys to the end ([#722](https://github.com/awslabs/aws-lambda-powertools-python/issues/722)) -* **mypy:** a few return types, type signatures, and untyped areas ([#718](https://github.com/awslabs/aws-lambda-powertools-python/issues/718)) +* **logger:** push extra keys to the end ([#722](https://github.com/aws-powertools/powertools-lambda-python/issues/722)) +* **mypy:** a few return types, type signatures, and untyped areas ([#718](https://github.com/aws-powertools/powertools-lambda-python/issues/718)) -### Code Refactoring +## Code Refactoring -* **data-classes:** clean up internal logic for APIGatewayAuthorizerResponse ([#643](https://github.com/awslabs/aws-lambda-powertools-python/issues/643)) +* **data-classes:** clean up internal logic for APIGatewayAuthorizerResponse ([#643](https://github.com/aws-powertools/powertools-lambda-python/issues/643)) -### Documentation +## Documentation -* Terraform reference for SAR Lambda Layer ([#716](https://github.com/awslabs/aws-lambda-powertools-python/issues/716)) -* **event-handler:** document catch-all routes ([#705](https://github.com/awslabs/aws-lambda-powertools-python/issues/705)) -* **idempotency:** fix misleading idempotent examples ([#661](https://github.com/awslabs/aws-lambda-powertools-python/issues/661)) +* Terraform reference for SAR Lambda Layer ([#716](https://github.com/aws-powertools/powertools-lambda-python/issues/716)) +* add team behind it and email +* **event-handler:** document catch-all routes ([#705](https://github.com/aws-powertools/powertools-lambda-python/issues/705)) +* **idempotency:** fix misleading idempotent examples ([#661](https://github.com/aws-powertools/powertools-lambda-python/issues/661)) * **jmespath:** clarify envelope terminology -* **parser:** fix incorrect import in root_validator example ([#735](https://github.com/awslabs/aws-lambda-powertools-python/issues/735)) +* **parser:** fix incorrect import in root_validator example ([#735](https://github.com/aws-powertools/powertools-lambda-python/issues/735)) -### Features +## Features -* expose jmespath powertools functions ([#736](https://github.com/awslabs/aws-lambda-powertools-python/issues/736)) -* boto3 sessions in batch, parameters & idempotency ([#717](https://github.com/awslabs/aws-lambda-powertools-python/issues/717)) -* **feature-flags**: add get_raw_configuration property in store; expose store ([#720](https://github.com/awslabs/aws-lambda-powertools-python/issues/720)) -* **feature-flags:** Bring your own logger for debug ([#709](https://github.com/awslabs/aws-lambda-powertools-python/issues/709)) -* **feature-flags:** improve "IN/NOT_IN"; new rule actions ([#710](https://github.com/awslabs/aws-lambda-powertools-python/issues/710)) -* **feature-flags:** get_raw_configuration property in Store ([#720](https://github.com/awslabs/aws-lambda-powertools-python/issues/720)) -* **feature_flags:** Added inequality conditions ([#721](https://github.com/awslabs/aws-lambda-powertools-python/issues/721)) -* **idempotency:** makes customers unit testing easier ([#719](https://github.com/awslabs/aws-lambda-powertools-python/issues/719)) -* **validator:** include missing data elements from a validation error ([#686](https://github.com/awslabs/aws-lambda-powertools-python/issues/686)) +* expose jmespath powertools functions ([#736](https://github.com/aws-powertools/powertools-lambda-python/issues/736)) +* add get_raw_configuration property in store; expose store +* boto3 sessions in batch, parameters & idempotency ([#717](https://github.com/aws-powertools/powertools-lambda-python/issues/717)) +* **feature-flags:** Bring your own logger for debug ([#709](https://github.com/aws-powertools/powertools-lambda-python/issues/709)) +* **feature-flags:** improve "IN/NOT_IN"; new rule actions ([#710](https://github.com/aws-powertools/powertools-lambda-python/issues/710)) +* **feature-flags:** get_raw_configuration property in Store ([#720](https://github.com/aws-powertools/powertools-lambda-python/issues/720)) +* **feature_flags:** Added inequality conditions ([#721](https://github.com/aws-powertools/powertools-lambda-python/issues/721)) +* **idempotency:** makes customers unit testing easier ([#719](https://github.com/aws-powertools/powertools-lambda-python/issues/719)) +* **validator:** include missing data elements from a validation error ([#686](https://github.com/aws-powertools/powertools-lambda-python/issues/686)) -### Maintenance +## Maintenance * add python 3.9 support -* **deps:** bump boto3 from 1.18.51 to 1.18.54 ([#733](https://github.com/awslabs/aws-lambda-powertools-python/issues/733)) -* **deps:** bump boto3 from 1.18.32 to 1.18.38 ([#671](https://github.com/awslabs/aws-lambda-powertools-python/issues/671)) -* **deps:** bump boto3 from 1.18.38 to 1.18.41 ([#677](https://github.com/awslabs/aws-lambda-powertools-python/issues/677)) -* **deps:** bump boto3 from 1.18.49 to 1.18.51 ([#713](https://github.com/awslabs/aws-lambda-powertools-python/issues/713)) -* **deps:** bump boto3 from 1.18.41 to 1.18.49 ([#703](https://github.com/awslabs/aws-lambda-powertools-python/issues/703)) -* **deps:** bump codecov/codecov-action from 2.0.2 to 2.1.0 ([#675](https://github.com/awslabs/aws-lambda-powertools-python/issues/675)) -* **deps-dev:** bump coverage from 5.5 to 6.0 ([#732](https://github.com/awslabs/aws-lambda-powertools-python/issues/732)) -* **deps-dev:** bump mkdocs-material from 7.2.8 to 7.3.0 ([#695](https://github.com/awslabs/aws-lambda-powertools-python/issues/695)) -* **deps-dev:** bump mkdocs-material from 7.2.6 to 7.2.8 ([#682](https://github.com/awslabs/aws-lambda-powertools-python/issues/682)) -* **deps-dev:** bump flake8-bugbear from 21.4.3 to 21.9.1 ([#676](https://github.com/awslabs/aws-lambda-powertools-python/issues/676)) -* **deps-dev:** bump flake8-bugbear from 21.9.1 to 21.9.2 ([#712](https://github.com/awslabs/aws-lambda-powertools-python/issues/712)) -* **deps-dev:** bump radon from 4.5.2 to 5.1.0 ([#673](https://github.com/awslabs/aws-lambda-powertools-python/issues/673)) -* **deps-dev:** bump mkdocs-material from 7.3.0 to 7.3.1 ([#731](https://github.com/awslabs/aws-lambda-powertools-python/issues/731)) -* **deps-dev:** bump xenon from 0.7.3 to 0.8.0 ([#669](https://github.com/awslabs/aws-lambda-powertools-python/issues/669)) +* bump to 1.21.0 +* **deps:** bump boto3 from 1.18.41 to 1.18.49 ([#703](https://github.com/aws-powertools/powertools-lambda-python/issues/703)) +* **deps:** bump boto3 from 1.18.32 to 1.18.38 ([#671](https://github.com/aws-powertools/powertools-lambda-python/issues/671)) +* **deps:** bump boto3 from 1.18.38 to 1.18.41 ([#677](https://github.com/aws-powertools/powertools-lambda-python/issues/677)) +* **deps:** bump boto3 from 1.18.51 to 1.18.54 ([#733](https://github.com/aws-powertools/powertools-lambda-python/issues/733)) +* **deps:** bump boto3 from 1.18.49 to 1.18.51 ([#713](https://github.com/aws-powertools/powertools-lambda-python/issues/713)) +* **deps:** bump codecov/codecov-action from 2.0.2 to 2.1.0 ([#675](https://github.com/aws-powertools/powertools-lambda-python/issues/675)) +* **deps-dev:** bump flake8-bugbear from 21.9.1 to 21.9.2 ([#712](https://github.com/aws-powertools/powertools-lambda-python/issues/712)) +* **deps-dev:** bump mkdocs-material from 7.3.0 to 7.3.1 ([#731](https://github.com/aws-powertools/powertools-lambda-python/issues/731)) +* **deps-dev:** bump mkdocs-material from 7.2.8 to 7.3.0 ([#695](https://github.com/aws-powertools/powertools-lambda-python/issues/695)) +* **deps-dev:** bump mkdocs-material from 7.2.6 to 7.2.8 ([#682](https://github.com/aws-powertools/powertools-lambda-python/issues/682)) +* **deps-dev:** bump flake8-bugbear from 21.4.3 to 21.9.1 ([#676](https://github.com/aws-powertools/powertools-lambda-python/issues/676)) +* **deps-dev:** bump coverage from 5.5 to 6.0 ([#732](https://github.com/aws-powertools/powertools-lambda-python/issues/732)) +* **deps-dev:** bump radon from 4.5.2 to 5.1.0 ([#673](https://github.com/aws-powertools/powertools-lambda-python/issues/673)) +* **deps-dev:** bump pytest-cov from 2.12.1 to 3.0.0 ([#730](https://github.com/aws-powertools/powertools-lambda-python/issues/730)) +* **deps-dev:** bump xenon from 0.7.3 to 0.8.0 ([#669](https://github.com/aws-powertools/powertools-lambda-python/issues/669)) + + +<a name="v1.20.2"></a> +## [v1.20.2] - 2021-09-02 +## Bug Fixes + +* Fix issue with strip_prefixes ([#647](https://github.com/aws-powertools/powertools-lambda-python/issues/647)) -### Bug Fixes +## Maintenance + +* bump to 1.20.2 +* **deps:** bump boto3 from 1.18.26 to 1.18.32 ([#663](https://github.com/aws-powertools/powertools-lambda-python/issues/663)) +* **deps-dev:** bump mkdocs-material from 7.2.4 to 7.2.6 ([#665](https://github.com/aws-powertools/powertools-lambda-python/issues/665)) +* **deps-dev:** bump pytest from 6.2.4 to 6.2.5 ([#662](https://github.com/aws-powertools/powertools-lambda-python/issues/662)) +* **license:** Add THIRD-PARTY-LICENSES ([#641](https://github.com/aws-powertools/powertools-lambda-python/issues/641)) + + +<a name="v1.20.1"></a> +## [v1.20.1] - 2021-08-22 +## Bug Fixes -* **event-handler:** fix issue with strip_prefixes and root level resolvers ([#646](https://github.com/awslabs/aws-lambda-powertools-python/issues/646)) +* **idempotency:** sorting keys before hashing ([#639](https://github.com/aws-powertools/powertools-lambda-python/issues/639)) -### Maintenance +## Maintenance -* **deps:** bump boto3 from 1.18.26 to 1.18.32 ([#663](https://github.com/awslabs/aws-lambda-powertools-python/issues/663)) -* **deps-dev:** bump mkdocs-material from 7.2.4 to 7.2.6 ([#665](https://github.com/awslabs/aws-lambda-powertools-python/issues/665)) -* **deps-dev:** bump pytest from 6.2.4 to 6.2.5 ([#662](https://github.com/awslabs/aws-lambda-powertools-python/issues/662)) -* **deps-dev:** bump mike from 0.6.0 to 1.0.1 ([#453](https://github.com/awslabs/aws-lambda-powertools-python/issues/453)) -* **license:** add third party license to pyproject.toml ([#641](https://github.com/awslabs/aws-lambda-powertools-python/issues/641)) -## 1.20.2 - 2021-09-02 +* bump to 1.20.1 +* markdown linter fixes ([#636](https://github.com/aws-powertools/powertools-lambda-python/issues/636)) +* setup codespaces ([#637](https://github.com/aws-powertools/powertools-lambda-python/issues/637)) +* **license:** add third party license ([#635](https://github.com/aws-powertools/powertools-lambda-python/issues/635)) -### Bug Fixes -* **event-handler:** fix issue with strip_prefixes and root level resolvers ([#646](https://github.com/awslabs/aws-lambda-powertools-python/issues/646)) +<a name="v1.20.0"></a> +## [v1.20.0] - 2021-08-21 +## Bug Fixes -### Maintenance +* **api-gateway:** HTTP API strip stage name from request path ([#622](https://github.com/aws-powertools/powertools-lambda-python/issues/622)) +* **docs:** correct feature_flags link and json exmaples ([#605](https://github.com/aws-powertools/powertools-lambda-python/issues/605)) -* **deps:** bump boto3 from 1.18.26 to 1.18.32 ([#663](https://github.com/awslabs/aws-lambda-powertools-python/issues/663)) -* **deps-dev:** bump mkdocs-material from 7.2.4 to 7.2.6 ([#665](https://github.com/awslabs/aws-lambda-powertools-python/issues/665)) -* **deps-dev:** bump pytest from 6.2.4 to 6.2.5 ([#662](https://github.com/awslabs/aws-lambda-powertools-python/issues/662)) -* **deps-dev:** bump mike from 0.6.0 to 1.0.1 ([#453](https://github.com/awslabs/aws-lambda-powertools-python/issues/453)) -* **license:** add third party license to pyproject.toml ([#641](https://github.com/awslabs/aws-lambda-powertools-python/issues/641)) +## Code Refactoring -## 1.20.1 - 2021-08-22 +* **event_handler:** match to match_results; 3.10 new keyword ([#616](https://github.com/aws-powertools/powertools-lambda-python/issues/616)) -### Bug Fixes +## Documentation -* **idempotency:** sorting keys before hashing ([#639](https://github.com/awslabs/aws-lambda-powertools-python/issues/639)) +* **api-gateway:** add new API mapping support +* **data-class:** fix invalid syntax in new AppSync Authorizer +* **data-classes:** make authorizer concise; use enum ([#630](https://github.com/aws-powertools/powertools-lambda-python/issues/630)) -### Maintenance +## Features -* markdown linter fixes ([#636](https://github.com/awslabs/aws-lambda-powertools-python/issues/636)) -* setup codespaces ([#637](https://github.com/awslabs/aws-lambda-powertools-python/issues/637)) -* **license:** add third party license ([#635](https://github.com/awslabs/aws-lambda-powertools-python/issues/635)) +* **data-classes:** authorizer for http api and rest api ([#620](https://github.com/aws-powertools/powertools-lambda-python/issues/620)) +* **data-classes:** data_as_bytes prop KinesisStreamRecordPayload ([#628](https://github.com/aws-powertools/powertools-lambda-python/issues/628)) +* **data-classes:** AppSync Lambda authorizer event ([#610](https://github.com/aws-powertools/powertools-lambda-python/issues/610)) +* **event-handler:** prefixes to strip for custom mappings ([#579](https://github.com/aws-powertools/powertools-lambda-python/issues/579)) +* **general:** support for Python 3.9 ([#626](https://github.com/aws-powertools/powertools-lambda-python/issues/626)) +* **idempotency:** support for any synchronous function ([#625](https://github.com/aws-powertools/powertools-lambda-python/issues/625)) -## 1.20.0 - 2021-08-21 +## Maintenance +* update changelog to reflect out-of-band commits +* bump to 1.20.0 +* update new changelog version tag +* **actions:** include new labels +* **api-docs:** enable allow_reuse to fix the docs ([#612](https://github.com/aws-powertools/powertools-lambda-python/issues/612)) +* **deps:** bump boto3 from 1.18.25 to 1.18.26 ([#627](https://github.com/aws-powertools/powertools-lambda-python/issues/627)) +* **deps:** bump boto3 from 1.18.24 to 1.18.25 ([#623](https://github.com/aws-powertools/powertools-lambda-python/issues/623)) +* **deps:** bump boto3 from 1.18.22 to 1.18.24 ([#619](https://github.com/aws-powertools/powertools-lambda-python/issues/619)) +* **deps:** bump boto3 from 1.18.21 to 1.18.22 ([#614](https://github.com/aws-powertools/powertools-lambda-python/issues/614)) +* **deps:** bump boto3 from 1.18.17 to 1.18.21 ([#608](https://github.com/aws-powertools/powertools-lambda-python/issues/608)) +* **deps-dev:** bump flake8-comprehensions from 3.6.0 to 3.6.1 ([#615](https://github.com/aws-powertools/powertools-lambda-python/issues/615)) +* **deps-dev:** bump flake8-comprehensions from 3.5.0 to 3.6.0 ([#609](https://github.com/aws-powertools/powertools-lambda-python/issues/609)) +* **deps-dev:** bump mkdocs-material from 7.2.3 to 7.2.4 ([#607](https://github.com/aws-powertools/powertools-lambda-python/issues/607)) +* **docs:** correct markdown based on markdown lint ([#603](https://github.com/aws-powertools/powertools-lambda-python/issues/603)) +* **shared:** fix cyclic import & refactor data extraction fn ([#613](https://github.com/aws-powertools/powertools-lambda-python/issues/613)) + + +<a name="v1.19.0"></a> +## [v1.19.0] - 2021-08-11 ## Bug Fixes -* **api-gateway:** HTTP API strip stage name from request path ([#622](https://github.com/awslabs/aws-lambda-powertools-python/issues/622)) +* **deps:** bump poetry to latest ([#592](https://github.com/aws-powertools/powertools-lambda-python/issues/592)) +* **feature-flags:** bug handling multiple conditions ([#599](https://github.com/aws-powertools/powertools-lambda-python/issues/599)) +* **feature-toggles:** correct cdk example ([#601](https://github.com/aws-powertools/powertools-lambda-python/issues/601)) +* **parser:** apigw wss validation check_message_id; housekeeping ([#553](https://github.com/aws-powertools/powertools-lambda-python/issues/553)) -### Code Refactoring +## Code Refactoring -* **event-handler:** match to match_results; 3.10 new keyword ([#616](https://github.com/awslabs/aws-lambda-powertools-python/issues/616)) +* **feature-flags:** add debug for all features evaluation" ([#590](https://github.com/aws-powertools/powertools-lambda-python/issues/590)) +* **feature_flags:** optimize UX and maintenance ([#563](https://github.com/aws-powertools/powertools-lambda-python/issues/563)) -### Documentation +## Documentation -* **data-classes:** make authorizer concise; use enum ([#630](https://github.com/awslabs/aws-lambda-powertools-python/issues/630)) -* **feature-flags:** correct link and json examples ([#605](https://github.com/awslabs/aws-lambda-powertools-python/issues/605)) -* **data-class:** fix invalid syntax in new AppSync Authorizer -* **api-gateway:** add new API mapping support +* **event-handler:** new custom serializer option +* **feature-flags:** add guidance when to use vs env vars vs parameters +* **feature-flags:** fix sample feature name in evaluate +* **feature-flags:** create concrete documentation ([#594](https://github.com/aws-powertools/powertools-lambda-python/issues/594)) +* **feature-toggles:** correct docs and typing ([#588](https://github.com/aws-powertools/powertools-lambda-python/issues/588)) +* **feature_flags:** fix SAM infra, convert CDK to Python +* **parameters:** auto-transforming values based on suffix ([#573](https://github.com/aws-powertools/powertools-lambda-python/issues/573)) +* **readme:** add code coverage badge ([#577](https://github.com/aws-powertools/powertools-lambda-python/issues/577)) +* **tracer:** update wording that it auto-disables on non-Lambda env -### Features +## Features + +* **api-gateway:** add support for custom serializer ([#568](https://github.com/aws-powertools/powertools-lambda-python/issues/568)) +* **data-classes:** decode json_body if based64 encoded ([#560](https://github.com/aws-powertools/powertools-lambda-python/issues/560)) +* **feature flags:** Add not_in action and rename contains to in ([#589](https://github.com/aws-powertools/powertools-lambda-python/issues/589)) +* **params:** expose high level max_age, raise_on_transform_error ([#567](https://github.com/aws-powertools/powertools-lambda-python/issues/567)) +* **tracer:** disable tracer when for non-Lambda envs ([#598](https://github.com/aws-powertools/powertools-lambda-python/issues/598)) + +## Maintenance + +* only build docs on docs path +* update pypi description, keywords +* bump to 1.19.0 +* enable autolabel based on PR title +* include feature-flags docs hotfix +* **deps:** bump boto3 from 1.18.15 to 1.18.17 ([#597](https://github.com/aws-powertools/powertools-lambda-python/issues/597)) +* **deps:** bump boto3 from 1.18.1 to 1.18.15 ([#591](https://github.com/aws-powertools/powertools-lambda-python/issues/591)) +* **deps:** bump codecov/codecov-action from 2.0.1 to 2.0.2 ([#558](https://github.com/aws-powertools/powertools-lambda-python/issues/558)) +* **deps-dev:** bump mkdocs-material from 7.2.1 to 7.2.2 ([#582](https://github.com/aws-powertools/powertools-lambda-python/issues/582)) +* **deps-dev:** bump mkdocs-material from 7.2.2 to 7.2.3 ([#596](https://github.com/aws-powertools/powertools-lambda-python/issues/596)) +* **deps-dev:** bump pdoc3 from 0.9.2 to 0.10.0 ([#584](https://github.com/aws-powertools/powertools-lambda-python/issues/584)) +* **deps-dev:** bump isort from 5.9.2 to 5.9.3 ([#574](https://github.com/aws-powertools/powertools-lambda-python/issues/574)) +* **deps-dev:** bump mkdocs-material from 7.2.0 to 7.2.1 ([#566](https://github.com/aws-powertools/powertools-lambda-python/issues/566)) +* **deps-dev:** bump mkdocs-material from 7.1.11 to 7.2.0 ([#551](https://github.com/aws-powertools/powertools-lambda-python/issues/551)) +* **deps-dev:** bump flake8-black from 0.2.1 to 0.2.3 ([#541](https://github.com/aws-powertools/powertools-lambda-python/issues/541)) + + +<a name="v1.18.1"></a> +## [v1.18.1] - 2021-07-23 +## Bug Fixes -* **data-classes:** authorizer for API Gateway HTTP and REST API ([#620](https://github.com/awslabs/aws-lambda-powertools-python/issues/620)) -* **data-classes:** new data_as_bytes property in KinesisStreamRecordPayload ([#628](https://github.com/awslabs/aws-lambda-powertools-python/issues/628)) -* **data-classes:** AppSync Lambda authorizer event ([#610](https://github.com/awslabs/aws-lambda-powertools-python/issues/610)) -* **event-handler:** prefixes to strip for custom domain mapping paths ([#579](https://github.com/awslabs/aws-lambda-powertools-python/issues/579)) -* **general:** support for Python 3.9 ([#626](https://github.com/awslabs/aws-lambda-powertools-python/issues/626)) -* **idempotency:** support for any synchronous function ([#625](https://github.com/awslabs/aws-lambda-powertools-python/issues/625)) +* **api-gateway:** route regression non-word and unsafe URI chars ([#556](https://github.com/aws-powertools/powertools-lambda-python/issues/556)) -### Maintenance +## Maintenance -* **actions:** include new labels -* **api-docs:** enable allow_reuse to fix the docs ([#612](https://github.com/awslabs/aws-lambda-powertools-python/issues/612)) -* **deps:** bump boto3 from 1.18.25 to 1.18.26 ([#627](https://github.com/awslabs/aws-lambda-powertools-python/issues/627)) -* **deps:** bump boto3 from 1.18.24 to 1.18.25 ([#623](https://github.com/awslabs/aws-lambda-powertools-python/issues/623)) -* **deps:** bump boto3 from 1.18.22 to 1.18.24 ([#619](https://github.com/awslabs/aws-lambda-powertools-python/issues/619)) -* **deps:** bump boto3 from 1.18.17 to 1.18.21 ([#608](https://github.com/awslabs/aws-lambda-powertools-python/issues/608)) -* **deps:** bump boto3 from 1.18.21 to 1.18.22 ([#614](https://github.com/awslabs/aws-lambda-powertools-python/issues/614)) -* **deps-dev:** bump flake8-comprehensions from 3.5.0 to 3.6.0 ([#609](https://github.com/awslabs/aws-lambda-powertools-python/issues/609)) -* **deps-dev:** bump flake8-comprehensions from 3.6.0 to 3.6.1 ([#615](https://github.com/awslabs/aws-lambda-powertools-python/issues/615)) -* **deps-dev:** bump mkdocs-material from 7.2.3 to 7.2.4 ([#607](https://github.com/awslabs/aws-lambda-powertools-python/issues/607)) -* **docs:** correct markdown based on markdown lint ([#603](https://github.com/awslabs/aws-lambda-powertools-python/issues/603)) -* **shared:** fix cyclic import & refactor data extraction fn ([#613](https://github.com/awslabs/aws-lambda-powertools-python/issues/613)) +* bump 1.18.1 -## 1.19.0 - 2021-08-11 -### Bug Fixes +<a name="v1.18.0"></a> +## [v1.18.0] - 2021-07-20 +## Bug Fixes -* **deps:** bump poetry to latest ([#592](https://github.com/awslabs/aws-lambda-powertools-python/issues/592)) -* **feature-flags:** bug handling multiple conditions ([#599](https://github.com/awslabs/aws-lambda-powertools-python/issues/599)) -* **parser:** API Gateway WebSocket validation under check_message_id; plus some housekeeping ([#553](https://github.com/awslabs/aws-lambda-powertools-python/issues/553)) -* **feature-toggles:** correct cdk example ([#601](https://github.com/awslabs/aws-lambda-powertools-python/issues/601)) +* **api-gateway:** non-greedy route pattern regex ([#533](https://github.com/aws-powertools/powertools-lambda-python/issues/533)) +* **api-gateway:** incorrect plain text mimetype [#506](https://github.com/aws-powertools/powertools-lambda-python/issues/506) +* **data-classes:** include milliseconds in scalar types ([#504](https://github.com/aws-powertools/powertools-lambda-python/issues/504)) +* **mypy:** fixes to resolve no implicit optional errors ([#521](https://github.com/aws-powertools/powertools-lambda-python/issues/521)) +* **parser:** Make ApiGateway version, authorizer fields optional ([#532](https://github.com/aws-powertools/powertools-lambda-python/issues/532)) +* **tracer:** mypy generic to preserve decorated method signature ([#529](https://github.com/aws-powertools/powertools-lambda-python/issues/529)) -### Code Refactoring +## Code Refactoring -* **feature-flags:** add debug statements for all feature evaluations ([#590](https://github.com/awslabs/aws-lambda-powertools-python/issues/590)) -* **feature-flags:** optimize UX and maintenance ([#563](https://github.com/awslabs/aws-lambda-powertools-python/issues/563)) +* **feature-toggles:** Code coverage and housekeeping ([#530](https://github.com/aws-powertools/powertools-lambda-python/issues/530)) -### Documentation +## Documentation -* **event-handler:** new custom serializer option -* **feature-flags:** create concrete documentation ([#594](https://github.com/awslabs/aws-lambda-powertools-python/issues/594)) -* **feature-flags:** correct docs and typing ([#588](https://github.com/awslabs/aws-lambda-powertools-python/issues/588)) -* **parameters:** auto-transforming values based on suffix ([#573](https://github.com/awslabs/aws-lambda-powertools-python/issues/573)) -* **readme:** add code coverage badge ([#577](https://github.com/awslabs/aws-lambda-powertools-python/issues/577)) -* **tracer:** update wording that it auto-disables on non-Lambda env -* **feature-flags:** fix SAM infra, convert CDK to Python -* **feature-flags:** fix sample feature name in evaluate method -* **feature-flags:** add guidance when to use vs env vars vs parameters -### Features +* **api-gateway:** document new HTTP service error exceptions ([#546](https://github.com/aws-powertools/powertools-lambda-python/issues/546)) +* **logger:** document new get_correlation_id method ([#545](https://github.com/aws-powertools/powertools-lambda-python/issues/545)) -* **api-gateway:** add support for custom serializer ([#568](https://github.com/awslabs/aws-lambda-powertools-python/issues/568)) -* **data-classes:** decode json_body if based64 encoded ([#560](https://github.com/awslabs/aws-lambda-powertools-python/issues/560)) -* **feature-flags:** Add not_in action and rename contains to in ([#589](https://github.com/awslabs/aws-lambda-powertools-python/issues/589)) -* **params:** expose params `max_age`, `raise_on_transform_error` to high level functions ([#567](https://github.com/awslabs/aws-lambda-powertools-python/issues/567)) -* **tracer:** auto-disable tracer for non-Lambda environments to ease testing ([#598](https://github.com/awslabs/aws-lambda-powertools-python/issues/598)) +## Features -### Maintenance +* **api-gateway:** add debug mode ([#507](https://github.com/aws-powertools/powertools-lambda-python/issues/507)) +* **api-gateway:** add common service errors ([#506](https://github.com/aws-powertools/powertools-lambda-python/issues/506)) +* **event-handler:** Support AppSyncResolverEvent subclassing ([#526](https://github.com/aws-powertools/powertools-lambda-python/issues/526)) +* **feat-toggle:** New simple feature toggles rule engine (WIP) ([#494](https://github.com/aws-powertools/powertools-lambda-python/issues/494)) +* **logger:** add get_correlation_id method ([#516](https://github.com/aws-powertools/powertools-lambda-python/issues/516)) +* **mypy:** add mypy support to makefile ([#508](https://github.com/aws-powertools/powertools-lambda-python/issues/508)) -* **deps:** bump boto3 from 1.18.1 to 1.18.15 ([#591](https://github.com/awslabs/aws-lambda-powertools-python/issues/591)) -* **deps:** bump codecov/codecov-action from 2.0.1 to 2.0.2 ([#558](https://github.com/awslabs/aws-lambda-powertools-python/issues/558)) -* **deps:** bump boto3 from 1.18.15 to 1.18.17 ([#597](https://github.com/awslabs/aws-lambda-powertools-python/issues/597)) -* **deps-dev:** bump mkdocs-material from 7.2.2 to 7.2.3 ([#596](https://github.com/awslabs/aws-lambda-powertools-python/issues/596)) -* **deps-dev:** bump mkdocs-material from 7.2.1 to 7.2.2 ([#582](https://github.com/awslabs/aws-lambda-powertools-python/issues/582)) -* **deps-dev:** bump pdoc3 from 0.9.2 to 0.10.0 ([#584](https://github.com/awslabs/aws-lambda-powertools-python/issues/584)) -* **deps-dev:** bump isort from 5.9.2 to 5.9.3 ([#574](https://github.com/awslabs/aws-lambda-powertools-python/issues/574)) -* **deps-dev:** bump mkdocs-material from 7.2.0 to 7.2.1 ([#566](https://github.com/awslabs/aws-lambda-powertools-python/issues/566)) -* **deps-dev:** bump mkdocs-material from 7.1.11 to 7.2.0 ([#551](https://github.com/awslabs/aws-lambda-powertools-python/issues/551)) -* **deps-dev:** bump flake8-black from 0.2.1 to 0.2.3 ([#541](https://github.com/awslabs/aws-lambda-powertools-python/issues/541)) -## 1.18.1 - 2021-07-23 - -### Bug Fixes - -* **api-gateway:** route regression for non-word and unsafe URI chars ([#556](https://github.com/awslabs/aws-lambda-powertools-python/issues/556)) - -## 1.18.0 - 2021-07-20 - -### Bug Fixes - -* **api-gateway:** non-greedy route pattern regex which incorrectly mapped certain route params to function params ([#533](https://github.com/awslabs/aws-lambda-powertools-python/issues/533)) -* **api-gateway:** incorrect plain text mimetype constant [#506](https://github.com/awslabs/aws-lambda-powertools-python/issues/506) -* **data-classes:** include milliseconds in scalar types to correctly align with AppSync scalars ([#504](https://github.com/awslabs/aws-lambda-powertools-python/issues/504)) -* **mypy:** addresses lack of optional types ([#521](https://github.com/awslabs/aws-lambda-powertools-python/issues/521)) -* **parser:** make ApiGateway version, authorizer fields optional ([#532](https://github.com/awslabs/aws-lambda-powertools-python/issues/532)) -* **tracer:** mypy generic to preserve decorated method signature ([#529](https://github.com/awslabs/aws-lambda-powertools-python/issues/529)) - -### Code Refactoring - -* **feature-toggles:** code coverage and housekeeping ([#530](https://github.com/awslabs/aws-lambda-powertools-python/issues/530)) - -### Documentation - -* **api-gateway:** new HTTP service error exceptions ([#546](https://github.com/awslabs/aws-lambda-powertools-python/issues/546)) -* **logger:** new get_correlation_id method ([#545](https://github.com/awslabs/aws-lambda-powertools-python/issues/545)) - -### Features - -* **api-gateway:** add debug mode ([#507](https://github.com/awslabs/aws-lambda-powertools-python/issues/507)) -* **api-gateway:** add common HTTP service errors ([#506](https://github.com/awslabs/aws-lambda-powertools-python/issues/506)) -* **event-handler:** Support AppSyncResolverEvent subclassing ([#526](https://github.com/awslabs/aws-lambda-powertools-python/issues/526)) -* **feat-toggle:** New simple feature toggles rule engine (WIP) ([#494](https://github.com/awslabs/aws-lambda-powertools-python/issues/494)) -* **logger:** add get_correlation_id method ([#516](https://github.com/awslabs/aws-lambda-powertools-python/issues/516)) - -### Maintenance - -* **mypy:** add mypy support to makefile ([#508](https://github.com/awslabs/aws-lambda-powertools-python/issues/508)) -* **deps:** bump codecov/codecov-action from 1 to 2.0.1 ([#539](https://github.com/awslabs/aws-lambda-powertools-python/issues/539)) -* **deps:** bump boto3 from 1.18.0 to 1.18.1 ([#528](https://github.com/awslabs/aws-lambda-powertools-python/issues/528)) -* **deps:** bump boto3 from 1.17.110 to 1.18.0 ([#527](https://github.com/awslabs/aws-lambda-powertools-python/issues/527)) -* **deps:** bump boto3 from 1.17.102 to 1.17.110 ([#523](https://github.com/awslabs/aws-lambda-powertools-python/issues/523)) -* **deps-dev:** bump mkdocs-material from 7.1.10 to 7.1.11 ([#542](https://github.com/awslabs/aws-lambda-powertools-python/issues/542)) -* **deps-dev:** bump mkdocs-material from 7.1.9 to 7.1.10 ([#522](https://github.com/awslabs/aws-lambda-powertools-python/issues/522)) -* **deps-dev:** bump isort from 5.9.1 to 5.9.2 ([#514](https://github.com/awslabs/aws-lambda-powertools-python/issues/514)) -* **event-handler:** adjusts API Gateway/ALB service errors exception docstrings to not confuse AppSync customers - -## 1.17.1 - 2021-07-02 - -### Bug Fixes - -* **Validator:** Handle built-in custom formats like `date-time` when type is `string` ([#498](https://github.com/awslabs/aws-lambda-powertools-python/issues/498)) - -### Documentation - -* **Layers:** Add Layers example for Serverless framework & CDK ([#500](https://github.com/awslabs/aws-lambda-powertools-python/issues/500)) -* **Misc.:** Enable dark mode switch ([#471](https://github.com/awslabs/aws-lambda-powertools-python/issues/471)) -* **Tracer:** Additional scenario when to disable auto-capture for responses larger than 64K ([#499](https://github.com/awslabs/aws-lambda-powertools-python/issues/499)) - -### Maintenance - -* **deps:** bump boto3 from 1.17.101 to 1.17.102 ([#493](https://github.com/awslabs/aws-lambda-powertools-python/issues/493)) -* **deps:** bump boto3 from 1.17.91 to 1.17.101 ([#490](https://github.com/awslabs/aws-lambda-powertools-python/issues/490)) -* **deps:** bump email-validator from 1.1.2 to 1.1.3 ([#478](https://github.com/awslabs/aws-lambda-powertools-python/issues/478)) -* **deps:** bump boto3 from 1.17.89 to 1.17.91 ([#473](https://github.com/awslabs/aws-lambda-powertools-python/issues/473)) -* **deps-dev:** bump flake8-eradicate from 1.0.0 to 1.1.0 ([#492](https://github.com/awslabs/aws-lambda-powertools-python/issues/492)) -* **deps-dev:** bump isort from 5.8.0 to 5.9.1 ([#487](https://github.com/awslabs/aws-lambda-powertools-python/issues/487)) -* **deps-dev:** bump mkdocs-material from 7.1.7 to 7.1.9 ([#491](https://github.com/awslabs/aws-lambda-powertools-python/issues/491)) - -## 1.17.0 - 2021-06-08 - -### Added - -* **Documentation**: Include new public roadmap ([#452](https://github.com/awslabs/aws-lambda-powertools-python/issues/452)) -* **Documentation**: Remove old todo in idempotency docs -* **Data classes:** New `AttributeValueType` to get type and value from data in `DynamoDBStreamEvent` ([#462](https://github.com/awslabs/aws-lambda-powertools-python/issues/462)) -* **Data classes:** New decorator `event_source` to instantiate data_classes ([#442](https://github.com/awslabs/aws-lambda-powertools-python/issues/442)) -* **Logger:** New `clear_state` parameter to clear previously added custom keys upon invocation ([#467](https://github.com/awslabs/aws-lambda-powertools-python/issues/467)) -* **Parser:** Support for API Gateway HTTP API [#434](https://github.com/awslabs/aws-lambda-powertools-python/issues/434) ([#441](https://github.com/awslabs/aws-lambda-powertools-python/issues/441)) - -### Maintenance - -* **deps**: bump xenon from 0.7.1 to 0.7.3 ([#446](https://github.com/awslabs/aws-lambda-powertools-python/issues/446)) -* assited changelog pre-generation, auto-label PR ([#443](https://github.com/awslabs/aws-lambda-powertools-python/issues/443)) -* enable dependabot for dep upgrades ([#444](https://github.com/awslabs/aws-lambda-powertools-python/issues/444)) -* enable mergify ([#450](https://github.com/awslabs/aws-lambda-powertools-python/issues/450)) -* **deps**: bump mkdocs-material from 7.1.5 to 7.1.6 ([#451](https://github.com/awslabs/aws-lambda-powertools-python/issues/451)) -* **deps**: bump boto3 from 1.17.78 to 1.17.84 ([#449](https://github.com/awslabs/aws-lambda-powertools-python/issues/449)) -* update mergify to require approval on dependabot ([#456](https://github.com/awslabs/aws-lambda-powertools-python/issues/456)) -* **deps**: bump actions/setup-python from 1 to 2.2.2 ([#445](https://github.com/awslabs/aws-lambda-powertools-python/issues/445)) -* **deps:** bump boto3 from 1.17.87 to 1.17.88 ([#463](https://github.com/awslabs/aws-lambda-powertools-python/issues/463)) -* **deps:** bump boto3 from 1.17.88 to 1.17.89 ([#466](https://github.com/awslabs/aws-lambda-powertools-python/issues/466)) -* **deps:** bump boto3 from 1.17.84 to 1.17.85 ([#455](https://github.com/awslabs/aws-lambda-powertools-python/issues/455)) -* **deps:** bump boto3 from 1.17.85 to 1.17.86 ([#458](https://github.com/awslabs/aws-lambda-powertools-python/issues/458)) -* **deps:** bump boto3 from 1.17.86 to 1.17.87 ([#459](https://github.com/awslabs/aws-lambda-powertools-python/issues/459)) -* **deps-dev:** bump mkdocs-material from 7.1.6 to 7.1.7 ([#464](https://github.com/awslabs/aws-lambda-powertools-python/issues/464)) -* **deps-dev:** bump pytest-cov from 2.12.0 to 2.12.1 ([#454](https://github.com/awslabs/aws-lambda-powertools-python/issues/454)) -* **mergify:** disable check for matrix jobs +## Maintenance + +* bump 1.18.0 ([#547](https://github.com/aws-powertools/powertools-lambda-python/issues/547)) +* **deps:** bump codecov/codecov-action from 1 to 2.0.1 ([#539](https://github.com/aws-powertools/powertools-lambda-python/issues/539)) +* **deps:** bump boto3 from 1.18.0 to 1.18.1 ([#528](https://github.com/aws-powertools/powertools-lambda-python/issues/528)) +* **deps:** bump boto3 from 1.17.110 to 1.18.0 ([#527](https://github.com/aws-powertools/powertools-lambda-python/issues/527)) +* **deps:** bump boto3 from 1.17.102 to 1.17.110 ([#523](https://github.com/aws-powertools/powertools-lambda-python/issues/523)) +* **deps-dev:** bump mkdocs-material from 7.1.10 to 7.1.11 ([#542](https://github.com/aws-powertools/powertools-lambda-python/issues/542)) +* **deps-dev:** bump mkdocs-material from 7.1.9 to 7.1.10 ([#522](https://github.com/aws-powertools/powertools-lambda-python/issues/522)) +* **deps-dev:** bump isort from 5.9.1 to 5.9.2 ([#514](https://github.com/aws-powertools/powertools-lambda-python/issues/514)) +* **event-handler:** adjusts exception docstrings to not confuse AppSync customers + + +<a name="v1.17.1"></a> +## [v1.17.1] - 2021-07-02 +## Bug Fixes + +* **validator:** handle built-in custom formats correctly ([#498](https://github.com/aws-powertools/powertools-lambda-python/issues/498)) + +## Documentation + +* add Layers example for Serverless framework & CDK ([#500](https://github.com/aws-powertools/powertools-lambda-python/issues/500)) +* enable dark mode switch ([#471](https://github.com/aws-powertools/powertools-lambda-python/issues/471)) +* **logger:** add FAQ for cross-account searches ([#501](https://github.com/aws-powertools/powertools-lambda-python/issues/501)) +* **tracer:** additional scenario when to disable auto-capture ([#499](https://github.com/aws-powertools/powertools-lambda-python/issues/499)) + +## Maintenance + +* bump 1.17.1 ([#502](https://github.com/aws-powertools/powertools-lambda-python/issues/502)) +* **deps:** bump boto3 from 1.17.101 to 1.17.102 ([#493](https://github.com/aws-powertools/powertools-lambda-python/issues/493)) +* **deps:** bump boto3 from 1.17.91 to 1.17.101 ([#490](https://github.com/aws-powertools/powertools-lambda-python/issues/490)) +* **deps:** bump email-validator from 1.1.2 to 1.1.3 ([#478](https://github.com/aws-powertools/powertools-lambda-python/issues/478)) +* **deps:** bump boto3 from 1.17.89 to 1.17.91 ([#473](https://github.com/aws-powertools/powertools-lambda-python/issues/473)) +* **deps-dev:** bump flake8-eradicate from 1.0.0 to 1.1.0 ([#492](https://github.com/aws-powertools/powertools-lambda-python/issues/492)) +* **deps-dev:** bump isort from 5.8.0 to 5.9.1 ([#487](https://github.com/aws-powertools/powertools-lambda-python/issues/487)) +* **deps-dev:** bump mkdocs-material from 7.1.7 to 7.1.9 ([#491](https://github.com/aws-powertools/powertools-lambda-python/issues/491)) + + +<a name="v1.17.0"></a> +## [v1.17.0] - 2021-06-08 +## Documentation + +* include new public roadmap ([#452](https://github.com/aws-powertools/powertools-lambda-python/issues/452)) +* **data_classes:** fix missing dynamodb stream get_type/value +* **idempotency:** remove old todo + +## Features + +* **data-classes:** add AttributeValueType to DynamoDBStreamEvent ([#462](https://github.com/aws-powertools/powertools-lambda-python/issues/462)) +* **data-classes:** decorator to instantiate data_classes and docs updates ([#442](https://github.com/aws-powertools/powertools-lambda-python/issues/442)) +* **logger:** add option to clear state per invocation ([#467](https://github.com/aws-powertools/powertools-lambda-python/issues/467)) +* **parser:** add support for API Gateway HTTP API [#434](https://github.com/aws-powertools/powertools-lambda-python/issues/434) ([#441](https://github.com/aws-powertools/powertools-lambda-python/issues/441)) + +## Maintenance + +* bump xenon from 0.7.1 to 0.7.3 ([#446](https://github.com/aws-powertools/powertools-lambda-python/issues/446)) +* fix changelog file redirection +* include dependencies label under maintenance +* ignore codecov upload +* reintroduce codecov token +* fix path for PR auto-labelling +* assited changelog pre-generation, auto-label PR ([#443](https://github.com/aws-powertools/powertools-lambda-python/issues/443)) +* enable dependabot for dep upgrades ([#444](https://github.com/aws-powertools/powertools-lambda-python/issues/444)) +* enable mergify ([#450](https://github.com/aws-powertools/powertools-lambda-python/issues/450)) +* dependabot/mergify guardrail for major versions +* fix dependabot commit messages prefix +* fix dependabot unique set config +* bump mkdocs-material from 7.1.5 to 7.1.6 ([#451](https://github.com/aws-powertools/powertools-lambda-python/issues/451)) +* bump boto3 from 1.17.78 to 1.17.84 ([#449](https://github.com/aws-powertools/powertools-lambda-python/issues/449)) +* update mergify to require approval on dependabot ([#456](https://github.com/aws-powertools/powertools-lambda-python/issues/456)) +* bump actions/setup-python from 1 to 2.2.2 ([#445](https://github.com/aws-powertools/powertools-lambda-python/issues/445)) +* trial boring cyborg automation +* **deps:** bump boto3 from 1.17.87 to 1.17.88 ([#463](https://github.com/aws-powertools/powertools-lambda-python/issues/463)) +* **deps:** bump boto3 from 1.17.88 to 1.17.89 ([#466](https://github.com/aws-powertools/powertools-lambda-python/issues/466)) +* **deps:** bump boto3 from 1.17.84 to 1.17.85 ([#455](https://github.com/aws-powertools/powertools-lambda-python/issues/455)) +* **deps:** bump boto3 from 1.17.85 to 1.17.86 ([#458](https://github.com/aws-powertools/powertools-lambda-python/issues/458)) +* **deps:** bump boto3 from 1.17.86 to 1.17.87 ([#459](https://github.com/aws-powertools/powertools-lambda-python/issues/459)) +* **deps-dev:** bump mkdocs-material from 7.1.6 to 7.1.7 ([#464](https://github.com/aws-powertools/powertools-lambda-python/issues/464)) +* **deps-dev:** bump pytest-cov from 2.12.0 to 2.12.1 ([#454](https://github.com/aws-powertools/powertools-lambda-python/issues/454)) * **mergify:** use job name to match GH Actions +* **mergify:** disable check for matrix jobs + + +<a name="v1.16.1"></a> +## [v1.16.1] - 2021-05-23 +## Features + +* **parser:** security issue in Pydantic [#436](https://github.com/aws-powertools/powertools-lambda-python/issues/436) ([#437](https://github.com/aws-powertools/powertools-lambda-python/issues/437)) + +## Maintenance + +* bump to 1.16.1 + + +<a name="v1.16.0"></a> +## [v1.16.0] - 2021-05-17 +## Features + +* **data-classes:** decode base64 encoded body ([#425](https://github.com/aws-powertools/powertools-lambda-python/issues/425)) +* **data-classes:** support for code pipeline job event ([#416](https://github.com/aws-powertools/powertools-lambda-python/issues/416)) + +## Maintenance + +* bump to 1.16.0 + + +<a name="v1.15.1"></a> +## [v1.15.1] - 2021-05-13 +## Bug Fixes + +* **docs:** Use updated names for ProxyEventType ([#424](https://github.com/aws-powertools/powertools-lambda-python/issues/424)) + +## Documentation + +* update list of features +* **event_handler:** add missing note on trimmed responses + +## Maintenance + +* bump to 1.15.1 + + +<a name="v1.15.0"></a> +## [v1.15.0] - 2021-05-06 +## Bug Fixes + +* **deps:** Bump aws-xray-sdk from 2.6.0 to 2.8.0 ([#413](https://github.com/aws-powertools/powertools-lambda-python/issues/413)) +* **docs:** workflow to include api ref in latest alias ([#408](https://github.com/aws-powertools/powertools-lambda-python/issues/408)) +* **parser:** Improve types for parser.py ([#419](https://github.com/aws-powertools/powertools-lambda-python/issues/419)) +* **validator:** event type annotation as any in validate fn ([#405](https://github.com/aws-powertools/powertools-lambda-python/issues/405)) + +## Code Refactoring + +* simplify custom formatter for minor changes ([#417](https://github.com/aws-powertools/powertools-lambda-python/issues/417)) +* **event-handler:** api gateway handler review changes ([#420](https://github.com/aws-powertools/powertools-lambda-python/issues/420)) +* **event-handler:** Add ResponseBuilder and more docs ([#412](https://github.com/aws-powertools/powertools-lambda-python/issues/412)) +* **logger:** BYOFormatter and Handler, UTC support, and more ([#404](https://github.com/aws-powertools/powertools-lambda-python/issues/404)) + +## Documentation + +* **api_gateway:** new event handler for API Gateway and ALB ([#418](https://github.com/aws-powertools/powertools-lambda-python/issues/418)) +* **event_handler:** fix closing brackets in CORS sample +* **event_handler:** remove beta flag from new HTTP utility +* **idempotency:** remove beta flag +* **logger:** improvements extensibility & new features ([#415](https://github.com/aws-powertools/powertools-lambda-python/issues/415)) +* **parser:** fix table and heading syntax +* **tracer:** Fix line highlighting ([#395](https://github.com/aws-powertools/powertools-lambda-python/issues/395)) + +## Features + +* add support to persist default dimensions ([#410](https://github.com/aws-powertools/powertools-lambda-python/issues/410)) +* **event-handle:** allow for cors=None setting ([#421](https://github.com/aws-powertools/powertools-lambda-python/issues/421)) +* **event-handler:** add http ProxyEvent handler ([#369](https://github.com/aws-powertools/powertools-lambda-python/issues/369)) +* **parser:** Support for API GW v1 proxy schema & envelope ([#403](https://github.com/aws-powertools/powertools-lambda-python/issues/403)) + +## Maintenance + +* bump to 1.15.0 ([#422](https://github.com/aws-powertools/powertools-lambda-python/issues/422)) + + +<a name="v1.14.0"></a> +## [v1.14.0] - 2021-04-09 +## Bug Fixes + +* perf tests for Logger and fail str msgs +* downgrade poetry to 1.1.4 ([#385](https://github.com/aws-powertools/powertools-lambda-python/issues/385)) +* lock X-Ray SDK to 2.6.0 ([#384](https://github.com/aws-powertools/powertools-lambda-python/issues/384)) +* **data-classes:** Add missing operationName ([#373](https://github.com/aws-powertools/powertools-lambda-python/issues/373)) +* **idempotent:** Correctly raise IdempotencyKeyError ([#378](https://github.com/aws-powertools/powertools-lambda-python/issues/378)) +* **metrics:** AttributeError raised by MediaManager and Typing and docs ([#357](https://github.com/aws-powertools/powertools-lambda-python/issues/357)) +* **parser:** S3Model support empty keys ([#375](https://github.com/aws-powertools/powertools-lambda-python/issues/375)) +* **tracer:** Correct type hint for MyPy ([#365](https://github.com/aws-powertools/powertools-lambda-python/issues/365)) +* **workflow:** github actions depends on for release + +## Documentation + +* Fix doc links and line highlights ([#380](https://github.com/aws-powertools/powertools-lambda-python/issues/380)) +* fix extra key for versioning +* update mkdocs-material to 7.1.0 +* Correct link targets and line highlights ([#390](https://github.com/aws-powertools/powertools-lambda-python/issues/390)) +* introduce event handlers utility section ([#388](https://github.com/aws-powertools/powertools-lambda-python/issues/388)) +* enable versioning feature ([#374](https://github.com/aws-powertools/powertools-lambda-python/issues/374)) +* **idempotency:** add default configuration for those not using CFN ([#391](https://github.com/aws-powertools/powertools-lambda-python/issues/391)) +* **index:** fix link to event handler +* **logger:** add example on how to set UTC timestamp ([#392](https://github.com/aws-powertools/powertools-lambda-python/issues/392)) +* **validator:** include more complete examples & intro to JSON Schema ([#389](https://github.com/aws-powertools/powertools-lambda-python/issues/389)) + +## Features + +* **event-handler:** Add AppSync handler decorator ([#363](https://github.com/aws-powertools/powertools-lambda-python/issues/363)) +* **parameter:** add dynamodb_endpoint_url for local_testing ([#376](https://github.com/aws-powertools/powertools-lambda-python/issues/376)) +* **parser:** Add S3 Object Lambda Event ([#362](https://github.com/aws-powertools/powertools-lambda-python/issues/362)) + +## Maintenance + +* bump to 1.14.0 +* add approved by field in RFC template +* make RFC proposal more explicit +* update automated steps in release process + + +<a name="v1.13.0"></a> +## [v1.13.0] - 2021-03-23 +## Bug Fixes + +* **deps:** Bump dependencies and fix some of the dev tooling ([#354](https://github.com/aws-powertools/powertools-lambda-python/issues/354)) +* **lint:** Move `tests/THIRD-PARTY-LICENSES` to root ([#352](https://github.com/aws-powertools/powertools-lambda-python/issues/352)) + +## Features + +* **data-classes:** Add S3 Object Lambda Event ([#353](https://github.com/aws-powertools/powertools-lambda-python/issues/353)) + +## Maintenance + +* include internals in release template +* bump to 1.13.0 +* correct 3rd party license + + +<a name="v1.12.0"></a> +## [v1.12.0] - 2021-03-17 +## Bug Fixes + +* **idempotency:** TypeError when calling is_missing_idempotency_key with an int ([#315](https://github.com/aws-powertools/powertools-lambda-python/issues/315)) +* **idempotency:** Correctly handle save_inprogress errors ([#313](https://github.com/aws-powertools/powertools-lambda-python/issues/313)) + +## Code Refactoring + +* **parameters:** Consistently reference env ([#319](https://github.com/aws-powertools/powertools-lambda-python/issues/319)) + +## Documentation + +* surface new 1.12.0 features and enhancements ([#344](https://github.com/aws-powertools/powertools-lambda-python/issues/344)) +* Correct code examples ([#317](https://github.com/aws-powertools/powertools-lambda-python/issues/317)) +* **data-classes:** Add more cognito code examples ([#340](https://github.com/aws-powertools/powertools-lambda-python/issues/340)) +* **idempotency:** Correct examples and line highlights ([#312](https://github.com/aws-powertools/powertools-lambda-python/issues/312)) +* **metrics:** Corrections to the code examples ([#314](https://github.com/aws-powertools/powertools-lambda-python/issues/314)) +* **metrics:** remove minimum dimensions +* **metrics:** Correct code examples in markdown ([#316](https://github.com/aws-powertools/powertools-lambda-python/issues/316)) +* **tracer:** Fix Tracer typing hinting for Pycharm ([#345](https://github.com/aws-powertools/powertools-lambda-python/issues/345)) + +## Features + +* **data-classes:** Add appsync scalar_types_utils ([#339](https://github.com/aws-powertools/powertools-lambda-python/issues/339)) +* **data-classes:** AppSync Resolver Event ([#323](https://github.com/aws-powertools/powertools-lambda-python/issues/323)) +* **idempotent:** Include function name in the idempotent key ([#326](https://github.com/aws-powertools/powertools-lambda-python/issues/326)) +* **logging:** Add correlation_id support ([#321](https://github.com/aws-powertools/powertools-lambda-python/issues/321)) +* **logging:** Include exception_name ([#320](https://github.com/aws-powertools/powertools-lambda-python/issues/320)) +* **parameters:** Add force_fetch option ([#341](https://github.com/aws-powertools/powertools-lambda-python/issues/341)) -## 1.16.1 - 2021-05-23 +## Maintenance -### Fixed +* bump to 1.12.0 +* remove auto-label as restrictions prevent it from working +* increase perf SLA due to slow GitHub Actions machine +* add PR size labelling action # 2 +* add PR size labelling action +* add PR auto-label action +* remove gatsby mention as migrated completed -* **Parser**: Upgrade Pydantic to 1.8.2 due to CVE-2021-29510 -## 1.16.0 - 2021-05-17 -### Features -- **data-classes(API Gateway, ALB):** New method to decode base64 encoded body ([#425](https://github.com/awslabs/aws-lambda-powertools-python/issues/425)) -- **data-classes(CodePipeline):** Support for CodePipeline job event and methods to handle artifacts more easily ([#416](https://github.com/awslabs/aws-lambda-powertools-python/issues/416)) +<a name="v1.11.0"></a> +## [v1.11.0] - 2021-03-05 +## Bug Fixes + +* import time latency by lazily loading high level modules ([#301](https://github.com/aws-powertools/powertools-lambda-python/issues/301)) +* correct behaviour to avoid caching "INPROGRESS" records ([#295](https://github.com/aws-powertools/powertools-lambda-python/issues/295)) +* **idempotency:** PR feedback on config and kwargs + +## Code Refactoring + +* **idempotent:** Change UX to use a config class for non-persistence related features ([#306](https://github.com/aws-powertools/powertools-lambda-python/issues/306)) +* **metrics:** optimize validation and serialization ([#307](https://github.com/aws-powertools/powertools-lambda-python/issues/307)) + +## Documentation + +* **batch:** add example on how to integrate with sentry.io ([#308](https://github.com/aws-powertools/powertools-lambda-python/issues/308)) +* **data-classes:** Correct import for DynamoDBRecordEventName ([#299](https://github.com/aws-powertools/powertools-lambda-python/issues/299)) +* **dataclasses:** new Connect Contact Flow ([#310](https://github.com/aws-powertools/powertools-lambda-python/issues/310)) +* **idempotency:** tidy up doc before release ([#309](https://github.com/aws-powertools/powertools-lambda-python/issues/309)) +* **idempotent:** Fix typos and code formatting ([#305](https://github.com/aws-powertools/powertools-lambda-python/issues/305)) + +## Features + +* Idempotency helper utility ([#245](https://github.com/aws-powertools/powertools-lambda-python/issues/245)) +* **data-classes:** Add connect contact flow event ([#304](https://github.com/aws-powertools/powertools-lambda-python/issues/304)) +* **idempotency:** Add raise_on_no_idempotency_key flag ([#297](https://github.com/aws-powertools/powertools-lambda-python/issues/297)) +* **idempotency:** Fix KeyError when local_cache is True and an error is raised in the lambda handler ([#300](https://github.com/aws-powertools/powertools-lambda-python/issues/300)) +* **idempotent:** Add support for jmespath_options ([#302](https://github.com/aws-powertools/powertools-lambda-python/issues/302)) + +## Maintenance + +* update changelog ([#311](https://github.com/aws-powertools/powertools-lambda-python/issues/311)) +* adjusts Metrics SLA for slow py36 interpreters +* remove unsuccessful labeler bot +* update labeler bot to sync upon PR changes +* attempt 1 to fix PR labeler + + +<a name="v1.10.5"></a> +## [v1.10.5] - 2021-02-17 +## Maintenance + +* version bump to 1.10.5 ([#292](https://github.com/aws-powertools/powertools-lambda-python/issues/292)) + + +<a name="v1.10.4"></a> +## [v1.10.4] - 2021-02-17 +## Bug Fixes + +* sync features in main page +* meta tags, and ext link to open in new tab + +## Documentation + +* **data-classes:** Fix anchor tags to be lower case ([#288](https://github.com/aws-powertools/powertools-lambda-python/issues/288)) + +## Maintenance + +* version bump to 1.10.4 ([#291](https://github.com/aws-powertools/powertools-lambda-python/issues/291)) +* add default runtime key +* Correct the docs location ([#289](https://github.com/aws-powertools/powertools-lambda-python/issues/289)) +* enable PR labeler workflow +* add auto-label for known files + +## Regression + +* search input size + + +<a name="v1.10.3"></a> +## [v1.10.3] - 2021-02-12 +## Bug Fixes + +* sfix typing hit for envelope parse model ([#286](https://github.com/aws-powertools/powertools-lambda-python/issues/286)) +* disable batching of X-Ray subsegments ([#284](https://github.com/aws-powertools/powertools-lambda-python/issues/284)) + +## Documentation + +* migrate documentation from Gatsby to MkDocs material ([#279](https://github.com/aws-powertools/powertools-lambda-python/issues/279)) + +## Maintenance + +* bump to 1.10.3 ([#287](https://github.com/aws-powertools/powertools-lambda-python/issues/287)) + + +<a name="v1.10.2"></a> +## [v1.10.2] - 2021-02-04 +## Bug Fixes + +* remove unnecessary typing-extensions for py3.8 ([#281](https://github.com/aws-powertools/powertools-lambda-python/issues/281)) +* batch processing exceptions ([#276](https://github.com/aws-powertools/powertools-lambda-python/issues/276)) + +## Documentation + +* **appconfig:** Use correct import for docstring ([#271](https://github.com/aws-powertools/powertools-lambda-python/issues/271)) + +## Maintenance + +* bump to 1.10.2 ([#282](https://github.com/aws-powertools/powertools-lambda-python/issues/282)) +* fix immer and socket.io CVEs ([#278](https://github.com/aws-powertools/powertools-lambda-python/issues/278)) +* typo in parser docs + + +<a name="v1.10.1"></a> +## [v1.10.1] - 2021-01-19 +## Features + +* add support for SNS->SQS protocol ([#272](https://github.com/aws-powertools/powertools-lambda-python/issues/272)) + +## Maintenance + +* bump to 1.10.1 ([#273](https://github.com/aws-powertools/powertools-lambda-python/issues/273)) + + +<a name="v1.10.0"></a> +## [v1.10.0] - 2021-01-18 +## Documentation + +* fix import ([#267](https://github.com/aws-powertools/powertools-lambda-python/issues/267)) +* add info about extras layer ([#260](https://github.com/aws-powertools/powertools-lambda-python/issues/260)) +* fix note whitespace +* add missing parser models ([#254](https://github.com/aws-powertools/powertools-lambda-python/issues/254)) + +## Features + +* toggle to disable log deduplication locally for pytest live log [#262](https://github.com/aws-powertools/powertools-lambda-python/issues/262) ([#268](https://github.com/aws-powertools/powertools-lambda-python/issues/268)) +* Add AppConfig parameter provider ([#236](https://github.com/aws-powertools/powertools-lambda-python/issues/236)) +* support extra parameter in Logger messages ([#257](https://github.com/aws-powertools/powertools-lambda-python/issues/257)) +* support custom formats in JSON Schema validation ([#247](https://github.com/aws-powertools/powertools-lambda-python/issues/247)) + +## Maintenance + +* bump to 1.10.0 ([#270](https://github.com/aws-powertools/powertools-lambda-python/issues/270)) +* move env names to constant file ([#264](https://github.com/aws-powertools/powertools-lambda-python/issues/264)) +* update stale bot +* general simplifications and cleanup ([#255](https://github.com/aws-powertools/powertools-lambda-python/issues/255)) +* hardcode axios transitive resolution ([#256](https://github.com/aws-powertools/powertools-lambda-python/issues/256)) + + +<a name="v1.9.1"></a> +## [v1.9.1] - 2020-12-21 +## Bug Fixes + +* ensures all Loggers have unique service names + +## Code Refactoring + +* convert dict into a literal dict object and re-use it + +## Documentation + +* add clarification to Tracer docs for how `capture_method` decorator can cause function responses to be read and serialized. + +## Features + +* **pep-561:** Create py.typed file and include into pyproject. + +## Maintenance + +* bump to 1.9.1 ([#252](https://github.com/aws-powertools/powertools-lambda-python/issues/252)) +* add changelog +* implement phony targets correctly +* **deps:** bump ini from 1.3.5 to 1.3.8 in /docs + +## Pull Requests -## 1.15.1 - 2021-05-13 +* Merge pull request [#250](https://github.com/aws-powertools/powertools-lambda-python/issues/250) from heitorlessa/fix/[#249](https://github.com/aws-powertools/powertools-lambda-python/issues/249) +* Merge pull request [#235](https://github.com/aws-powertools/powertools-lambda-python/issues/235) from Nr18/phony +* Merge pull request [#244](https://github.com/aws-powertools/powertools-lambda-python/issues/244) from awslabs/docs/capture_method_clarification +* Merge pull request [#241](https://github.com/aws-powertools/powertools-lambda-python/issues/241) from awslabs/dependabot/npm_and_yarn/docs/ini-1.3.8 +* Merge pull request [#237](https://github.com/aws-powertools/powertools-lambda-python/issues/237) from gmcrocetti/pep-561 +* Merge pull request [#234](https://github.com/aws-powertools/powertools-lambda-python/issues/234) from Nr18/test-equal +* Merge pull request [#233](https://github.com/aws-powertools/powertools-lambda-python/issues/233) from GroovyDan/improv/add_equality_check_to_dict_wrapper +* Merge pull request [#232](https://github.com/aws-powertools/powertools-lambda-python/issues/232) from gyft/add-missing-tests -### Fixed -* **Logger**: Fix a regression with the `%s` operator +<a name="v1.9.0"></a> +## [v1.9.0] - 2020-12-04 +## Bug Fixes + +* s3 model import +* cloudwatch logs envelope typo + +## Documentation + +* add Kinesis Streams as a supported model & envelope +* add S3 as a supported model +* add CW Logs as a supported envelope +* add CW Logs as a supported model +* add Alb as a supported model +* shadow sidebar to remain expanded +* add source code link in nav bar +* fix broken link for github + +## Features + +* Add Kinesis lambda event support to Parser utility +* Add cloudwatch lambda event support to Parser utility +* Add alb lambda event support to Parser utility [#228](https://github.com/aws-powertools/powertools-lambda-python/issues/228) +* Add Kinesis lambda event support to Parser utility +* Add S3 lambda event support to Parser utility [#224](https://github.com/aws-powertools/powertools-lambda-python/issues/224) +* Add Ses lambda event support to Parser utility [#213](https://github.com/aws-powertools/powertools-lambda-python/issues/213) + +## Maintenance + + +## Pull Requests + +* Merge pull request [#227](https://github.com/aws-powertools/powertools-lambda-python/issues/227) from risenberg-cyberark/kinesis +* Merge pull request [#225](https://github.com/aws-powertools/powertools-lambda-python/issues/225) from risenberg-cyberark/s3 +* Merge pull request [#231](https://github.com/aws-powertools/powertools-lambda-python/issues/231) from risenberg-cyberark/cloudwatch +* Merge pull request [#229](https://github.com/aws-powertools/powertools-lambda-python/issues/229) from risenberg-cyberark/alb +* Merge pull request [#223](https://github.com/aws-powertools/powertools-lambda-python/issues/223) from heitorlessa/docs/add-source-code-link +* Merge pull request [#222](https://github.com/aws-powertools/powertools-lambda-python/issues/222) from awslabs/docs-fix-broken-link +* Merge pull request [#219](https://github.com/aws-powertools/powertools-lambda-python/issues/219) from igorlg/docs/logger-supress-clarify +* Merge pull request [#214](https://github.com/aws-powertools/powertools-lambda-python/issues/214) from risenberg-cyberark/ses + + +<a name="v1.8.0"></a> +## [v1.8.0] - 2020-11-20 +## Bug Fixes + +* replace now deprecated set-env with new GitHub Env file +* remove dummy heading to prevent htmlAst bug + +## Documentation + +* correct example usage of SES data class +* add faq section +* add minimal permission set for using layer + +## Features + +* include new replay-name field in parser and data_classes +* **data_classes:** API Gateway V2 IAM and Lambda + +## Maintenance + +* bump to 1.8.0 +* bump dependencies +* **docs:** Add some of the missing docstrings + +## Pull Requests + +* Merge pull request [#212](https://github.com/aws-powertools/powertools-lambda-python/issues/212) from heitorlessa/chore/bump-1.8.0 +* Merge pull request [#211](https://github.com/aws-powertools/powertools-lambda-python/issues/211) from heitorlessa/feat/eventbridge-replay-support +* Merge pull request [#209](https://github.com/aws-powertools/powertools-lambda-python/issues/209) from awslabs/docs/correct_ses_dataclass_example +* Merge pull request [#207](https://github.com/aws-powertools/powertools-lambda-python/issues/207) from risenberg-cyberark/sns +* Merge pull request [#205](https://github.com/aws-powertools/powertools-lambda-python/issues/205) from heitorlessa/chore/update-docs-dep +* Merge pull request [#202](https://github.com/aws-powertools/powertools-lambda-python/issues/202) from Nr18/logger-faq +* Merge pull request [#204](https://github.com/aws-powertools/powertools-lambda-python/issues/204) from am29d/docs/add-iam-permissions-for-layer +* Merge pull request [#201](https://github.com/aws-powertools/powertools-lambda-python/issues/201) from gyft/feat-data-classes-event-updates + + +<a name="v1.7.0"></a> +## [v1.7.0] - 2020-10-26 +## Bug Fixes + +* _parse return type +* high and security peer dependency vulnerabilities +* change to Yarn to support manual resolutions +* generic type to match ABC bound class +* debug logging in envelopes before each parsing +* remove malformed 3.1. sentence +* ensures parser can take json strings as input +* parse high level import +* code inspect issues +* unnecessary return; better error handling +* snake_case +* comment out validators [#118](https://github.com/aws-powertools/powertools-lambda-python/issues/118) +* CR fixes Merge branch 'develop' of https://github.com/awslabs/aws-lambda-powertools-python into pydantic +* reduce complexity of dynamo envelope +* poetry update + pydantic, typing_extensions as optional +* add only pydantic (+1 squashed commit) Squashed commits: [804f251] fix poetry.lock, revert changes +* Correct typo +* remove only dev extras +* remove jmespath extras in Make + +## Code Refactoring + +* pydantic as optional dependancy, remove lambdaContext +* change to advanced parser + +## Documentation + +* reorder parser's payload sample position +* add more info on conditional keys [#195](https://github.com/aws-powertools/powertools-lambda-python/issues/195) +* add a note that decorator will replace the event +* address Ran's feedback +* reorder data validation; improve envelopes section +* reorder extending models as parse fn wasn't introduced +* use yarn's resolution to fix incompatible dependency +* add cold start data +* add a FAQ section +* ensure examples can be copied/pasted as-is +* add extending built-in models +* add envelope section +* add data model validation section +* use non-hello world model to better exemplify parsing +* add 101 parsing events content +* initial structure for parser docs +* initial sketch of parser docs +* update examples in README + +## Features + +* experiment with codeQL over LGTM +* add standalone parse function +* Advanced parser utility (pydantic) +* RFC: Validate incoming and outgoing events utility [#95](https://github.com/aws-powertools/powertools-lambda-python/issues/95) +* **data_classes:** case insensitive header lookup +* **data_classes:** Cognito custom auth triggers + +## Maintenance + +* fix repository URL +* spacing +* typo in list +* typo on code generation tool +* remove flake8 polyfill as explicit dep +* explicit DynamoDB Stream schema naming +* lint +* kwarg over arg to ease refactoring +* remove test for commented code +* fix make build syntax for internal build whl +* upgrade docs dep +* remove dev deps from example project +* remove kitchen sink example +* upgrade gatsby +* upgrade amplify, antd, and gatsby plugins +* upgrade apollo-docs theme +* remove dev deps from example project +* remove kitchen sink example + +## Reverts +* fix: remove jmespath extras in Make +* fix: remove jmespath extras in Make + +## Pull Requests + +* Merge pull request [#200](https://github.com/aws-powertools/powertools-lambda-python/issues/200) from heitorlessa/chore/bump-1.7.0 +* Merge pull request [#199](https://github.com/aws-powertools/powertools-lambda-python/issues/199) from heitorlessa/docs/clarify-dynamic-log-keys +* Merge pull request [#198](https://github.com/aws-powertools/powertools-lambda-python/issues/198) from awslabs/improv/suppress-logger-propagation +* Merge pull request [#192](https://github.com/aws-powertools/powertools-lambda-python/issues/192) from heitorlessa/docs/parser +* Merge pull request [#196](https://github.com/aws-powertools/powertools-lambda-python/issues/196) from awslabs/dependabot/npm_and_yarn/docs/object-path-0.11.5 +* Merge pull request [#189](https://github.com/aws-powertools/powertools-lambda-python/issues/189) from heitorlessa/improv/parser[#118](https://github.com/aws-powertools/powertools-lambda-python/issues/118) +* Merge pull request [#186](https://github.com/aws-powertools/powertools-lambda-python/issues/186) from gyft/feat-case-insensitive-dict +* Merge pull request [#188](https://github.com/aws-powertools/powertools-lambda-python/issues/188) from gyft/tests-pydantic +* Merge pull request [#178](https://github.com/aws-powertools/powertools-lambda-python/issues/178) from gyft/cognito-custom-auth +* Merge pull request [#118](https://github.com/aws-powertools/powertools-lambda-python/issues/118) from risenberg-cyberark/pydantic +* Merge pull request [#181](https://github.com/aws-powertools/powertools-lambda-python/issues/181) from awslabs/fix/docs-sec-vuln +* Merge pull request [#180](https://github.com/aws-powertools/powertools-lambda-python/issues/180) from heitorlessa/chore/remove-example + + +<a name="v1.6.1"></a> +## [v1.6.1] - 2020-09-23 +## Maintenance + +* bump to 1.6.1 ([#177](https://github.com/aws-powertools/powertools-lambda-python/issues/177)) + + +<a name="v1.6.0"></a> +## [v1.6.0] - 2020-09-22 +## Bug Fixes + +* apply Tom's suggestion +* branding +* Correct description for data classes util +* duplicate features content +* navigation, branding +* remove DeleteMessageBatch call to SQS api if there are no messages to delete ([#170](https://github.com/aws-powertools/powertools-lambda-python/issues/170)) +* correct type hint Dict instead of dict + +## Code Refactoring + +* correct type hint + +## Documentation + +* fixed more typos, correct index reference to new util +* fix typo in DynamoDB example +* add docs for data classes utility +* improve wording on jmespath fns +* document validator utility -## 1.15.0 - 2021-05-06 +## Features -### Added +* add custom jmespath functions support +* emf multiple metric values ([#167](https://github.com/aws-powertools/powertools-lambda-python/issues/167)) +* add initial validator tests +* add cloudwatch_logs based on Bryan's feedback +* add powertools_base64 custom fn +* add built-in envelopes +* add jmespath as optional dependency +* add initial draft simple validator +* **trigger:** data class and helper functions for lambda trigger events ([#159](https://github.com/aws-powertools/powertools-lambda-python/issues/159)) -* **Event handlers**: New API Gateway and ALB utility to reduce routing boilerplate and more -* **Documentation**: Logger enhancements such as bring your own formatter, handler, UTC support, and testing for Python 3.6 -* **Parser**: Support for API Gateway REST Proxy event and envelope -* **Logger**: Support for bringing custom formatter, custom handler, custom JSON serializer and deserializer, UTC support, expose `LambdaPowertoolsFormatter` -* **Metrics**: Support for persisting default dimensions that should always be added +## Maintenance -### Fixed +* typo +* bump to 1.6.0 +* better type hinting +* update changelog +* fix docstring; import order -* **Documentation**: Fix highlights, Parser types -* **Validator**: Fix event type annotations for `validate` standalone function -* **Parser**: Improve and fix types -* **Internal**: Remove X-Ray SDK version pinning as serialization regression has been fixed in 2.8.0 -* **Internal**: Latest documentation correctly includes a copy of API docs reference +## Pull Requests -## 1.14.0 - 2021-04-09 +* Merge pull request [#175](https://github.com/aws-powertools/powertools-lambda-python/issues/175) from heitorlessa/chore/bump-1.6.0 +* Merge pull request [#171](https://github.com/aws-powertools/powertools-lambda-python/issues/171) from awslabs/docs/data_classes +* Merge pull request [#174](https://github.com/aws-powertools/powertools-lambda-python/issues/174) from heitorlessa/improv/docs-logger-metrics-testing +* Merge pull request [#168](https://github.com/aws-powertools/powertools-lambda-python/issues/168) from gyft/tests-missing +* Merge pull request [#153](https://github.com/aws-powertools/powertools-lambda-python/issues/153) from heitorlessa/feat/validator-utility + + +<a name="v1.5.0"></a> +## [v1.5.0] - 2020-09-04 +## Bug Fixes + +* throw exception by default if messages processing fails +* add sqs_batch_processor as its own method +* ensure debug log event has latest ctx +* update image with correct sample +* ensures xray_trace_id is refreshed +* typo in example +* include proposed suggestions +* **base-partial:** append record instead of entry +* **logging:** Don't include `json_default` in logs ([#132](https://github.com/aws-powertools/powertools-lambda-python/issues/132)) + +## Code Refactoring + +* changes partial_sqs middleware in favor of a generic interface always expecting a BatchProcessor +* replace LambdaEvent with Dict[str, Any] +* remove initial reference +* fix import issues and provide context in docblocks +* split properties and add docblocks +* split the objects into seperate files +* make requested changes +* use None instead of +* batch middleware +* remove references to BaseProcessor. Left BasePartialProcessor +* change return for failure/success handlers +* **sqs:** add module middlewares +* **sqs:** change methods to protected +* **tests:** update tests to new batch processor middleware +* **tests:** processor using default config + +## Documentation + +* address readability feedbacks +* add detail to batch processing +* simplify documentation more SQS specific focus Update for sqs_batch_processor interface +* rephrase the wording to make it more clear +* refactor example; improve docs about creating your own processor +* add newly created Slack Channel +* describe the typing utility +* add troubleshooting section +* add xray_trace_id key +* fix suggestions made by [@heitorlessa](https://github.com/heitorlessa) +* add description where to find the layer arn ([#145](https://github.com/aws-powertools/powertools-lambda-python/issues/145)) +* new section "Migrating from other Loggers" ([#148](https://github.com/aws-powertools/powertools-lambda-python/issues/148)) +* minor edit to letter case part 2 +* user specific documentation +* Fix doc for log sampling ([#135](https://github.com/aws-powertools/powertools-lambda-python/issues/135)) +* **partial-processor:** add simple docstrings to success/failure handlers +* **sqs:** docstrings for PartialSQS +* **sqs-base:** docstring for base class + +## Features + +* add xray_trace_id key when tracing is active [#137](https://github.com/aws-powertools/powertools-lambda-python/issues/137) +* initial implementation as the proposed gist is +* add sqs failure processors +* include base processors +* add batch module +* add package level import for batch utility +* **logger:** readable log_dict seq +* **logging:** suppress some log keys +* **logging:** allow for custom json order +* **parameters:** transform = "auto" ([#133](https://github.com/aws-powertools/powertools-lambda-python/issues/133)) +* **sqs:** add optional config parameter +* **sqs:** improve validation for queue_url + +## Maintenance + +* tiny changes for readability +* add debug logging for sqs batch processing +* remove middlewares module, moving decorator functionality to base and sqs +* add test for sqs_batch_processor interface +* add sqs_batch_processor decorator to simplify interface +* fix typos, docstrings and type hints ([#154](https://github.com/aws-powertools/powertools-lambda-python/issues/154)) +* doc typo +* **batch:** Housekeeping for recent changes ([#157](https://github.com/aws-powertools/powertools-lambda-python/issues/157)) + +## Pull Requests + +* Merge pull request [#149](https://github.com/aws-powertools/powertools-lambda-python/issues/149) from Nr18/static-types +* Merge pull request [#155](https://github.com/aws-powertools/powertools-lambda-python/issues/155) from awslabs/docs/batch_processing_util +* Merge pull request [#100](https://github.com/aws-powertools/powertools-lambda-python/issues/100) from gmcrocetti/partial-sqs-batch +* Merge pull request [#151](https://github.com/aws-powertools/powertools-lambda-python/issues/151) from Nr18/troubleshooting +* Merge pull request [#150](https://github.com/aws-powertools/powertools-lambda-python/issues/150) from heitorlessa/feat/logger-add-xray-trace-id +* Merge pull request [#140](https://github.com/aws-powertools/powertools-lambda-python/issues/140) from gyft/fix-log-key-order +* Merge pull request [#142](https://github.com/aws-powertools/powertools-lambda-python/issues/142) from gyft/fix-letter-case + + +<a name="v1.4.0"></a> +## [v1.4.0] - 2020-08-25 +## Bug Fixes + +* upgrade dot-prop, serialize-javascript +* remove actual response from debug logs +* naming and staticmethod consistency +* correct in_subsegment assertion +* update cold_start doc to reflect [#125](https://github.com/aws-powertools/powertools-lambda-python/issues/125) +* split ColdStart metric to its own EMF blob [#125](https://github.com/aws-powertools/powertools-lambda-python/issues/125) +* **ssm:** Make decrypt an explicit option and refactoring ([#123](https://github.com/aws-powertools/powertools-lambda-python/issues/123)) + +## Documentation + +* add Lambda Layer SAR App url and ARN +* move tenets; remove extra space +* use table for clarity +* add blog post, and quick example +* subtle rewording for better clarity +* fix typos, log_event & sampling wording +* make sensitive info more explicit with an example +* create Patching modules section; cleanup response wording +* move concurrent asynchronous under escape hatch +* grammar +* bring new feature upfront when returning sensitive info + +## Features + +* capture_response as metadata option [#127](https://github.com/aws-powertools/powertools-lambda-python/issues/127) + +## Maintenance + +* bump to 1.4.0 +* update internal docstrings for consistency +* update changelog to reflect new feature +* clarify changelog bugfix vs breaking change +* remove/correct unnecessary debug logs +* fix debug log adding unused obj +* grammar +* add metrics fix description +* correct typos + +## Pull Requests + +* Merge pull request [#129](https://github.com/aws-powertools/powertools-lambda-python/issues/129) from am29d/feat/lambda-layers +* Merge pull request [#130](https://github.com/aws-powertools/powertools-lambda-python/issues/130) from heitorlessa/docs/readability-improvements +* Merge pull request [#128](https://github.com/aws-powertools/powertools-lambda-python/issues/128) from heitorlessa/feat/tracer-disallow-response-metadata +* Merge pull request [#126](https://github.com/aws-powertools/powertools-lambda-python/issues/126) from heitorlessa/fix/metrics-cold-start-split + + +<a name="v1.3.1"></a> +## [v1.3.1] - 2020-08-22 +## Bug Fixes + +* **capture_method:** should yield inside with ([#124](https://github.com/aws-powertools/powertools-lambda-python/issues/124)) + +## Maintenance + +* version bump to 1.3.1 +* **deps:** bump prismjs from 1.20.0 to 1.21.0 in /docs +* **deps:** bump elliptic from 6.5.2 to 6.5.3 in /docs + +## Pull Requests + +* Merge pull request [#120](https://github.com/aws-powertools/powertools-lambda-python/issues/120) from awslabs/dependabot/npm_and_yarn/docs/elliptic-6.5.3 +* Merge pull request [#121](https://github.com/aws-powertools/powertools-lambda-python/issues/121) from awslabs/dependabot/npm_and_yarn/docs/prismjs-1.21.0 + + +<a name="v1.3.0"></a> +## [v1.3.0] - 2020-08-21 +## Features + +* add parameter utility ([#96](https://github.com/aws-powertools/powertools-lambda-python/issues/96)) + +## Maintenance -### Added -* **Event handlers**: New core utility to easily handle incoming requests tightly integrated with Data Classes; AppSync being the first as we gauge from the community what additional ones would be helpful -* **Documentation**: Enabled versioning to access docs on a per release basis or staging docs (`develop` branch) -* **Documentation**: Links now open in a new tab and improved snippet line highlights -* **Documentation(validation)**: JSON Schema snippets and more complete examples -* **Documentation(idempotency)**: Table with expected configuration values for hash key and TTL attribute name when using the default behaviour -* **Documentation(logger)**: New example on how to set logging record timestamps in UTC -* **Parser(S3)**: Support for the new S3 Object Lambda Event model (`S3ObjectLambdaEvent`) -* **Parameters**: Support for DynamoDB Local via `endpoint_url` parameter, including docs -* **Internal**: Include `make pr` in pre-commit hooks when contributing to shorten feedback loop on pre-commit specific linting -### Fixed +<a name="v1.2.0"></a> +## [v1.2.0] - 2020-08-20 +## Features -* **Parser**: S3Model now supports keys with 0 length -* **Tracer**: Lock X-Ray SDK to 2.6.0 as there's been a regression upstream in 2.7.0 on serializing & capturing exceptions -* **Data Classes(API Gateway)**: Add missing property `operationName` within request context -* **Misc.**: Numerous typing fixes to better to support MyPy across all utilities -* **Internal**: Downgraded poetry to 1.1.4 as there's been a regression with `importlib-metadata` in 1.1.5 not yet fixed +* add support for tracing of generators using capture_method decorator ([#113](https://github.com/aws-powertools/powertools-lambda-python/issues/113)) -## 1.13.0 - 2021-03-23 +## Maintenance -### Added -* **Data Classes**: New S3 Object Lambda event -### Fixed +<a name="v1.1.3"></a> +## [v1.1.3] - 2020-08-18 +## Bug Fixes + +* remove root logger handler set by Lambda [#115](https://github.com/aws-powertools/powertools-lambda-python/issues/115) + +## Maintenance + +* bump to 1.1.3 + +## Pull Requests + +* Merge pull request [#117](https://github.com/aws-powertools/powertools-lambda-python/issues/117) from heitorlessa/chore/bump-1.1.3 +* Merge pull request [#116](https://github.com/aws-powertools/powertools-lambda-python/issues/116) from heitorlessa/fix/remove-root-logger-handler + + +<a name="v1.1.2"></a> +## [v1.1.2] - 2020-08-16 +## Bug Fixes + +* return subclass [#107](https://github.com/aws-powertools/powertools-lambda-python/issues/107) -* **Docs**: Lambda Layer SAM template reference example +## Documentation -## 1.12.0 - 2021-03-17 +* clarify auto_patch as per [#108](https://github.com/aws-powertools/powertools-lambda-python/issues/108) -### Added +## Maintenance -* **Parameters**: New `force_fetch` param to always fetch the latest and bypass cache, if available -* **Data Classes**: New AppSync Lambda Resolver event covering both Direct Lambda Resolver and Amplify GraphQL Transformer Resolver `@function` -* **Data Classes**: New AppSync scalar utilities to easily compose Lambda Resolvers with date utils, uuid, etc. -* **Logger**: Support for Correlation ID both in `inject_lambda_context` decorator and `set_correlation_id` method -* **Logger**: Include new `exception_name` key to help customers easily enumerate exceptions across all functions +* suppress LGTM alert +* add autocomplete as unreleased +* remove unused stdout fixture +* update Tracer docs as per [#108](https://github.com/aws-powertools/powertools-lambda-python/issues/108) -### Fixed +## Pull Requests -* **Tracer**: Type hint on return instance that made PyCharm no longer recognize autocompletion -* **Idempotency**: Error handling for missing idempotency key and `save_in_progress` errors +* Merge pull request [#111](https://github.com/aws-powertools/powertools-lambda-python/issues/111) from heitorlessa/chore/bump-1.1.2 +* Merge pull request [#110](https://github.com/aws-powertools/powertools-lambda-python/issues/110) from heitorlessa/improv/logger-auto-complete +* Merge pull request [#109](https://github.com/aws-powertools/powertools-lambda-python/issues/109) from heitorlessa/docs/tracer-reuse -## 1.11.0 - 2021-03-05 -### Fixed -* **Tracer**: Lazy loads X-Ray SDK to increase perf by 75% for those not instantiating Tracer -* **Metrics**: Optimize validation and serialization to increase perf by nearly 50% for large operations (<1ms) +<a name="v1.1.1"></a> +## [v1.1.1] - 2020-08-14 +## Bug Fixes -### Added +* regression 104 ([#105](https://github.com/aws-powertools/powertools-lambda-python/issues/105)) +* return log level int immediately +* add test covering logging constant -* **Dataclass**: Add new Amazon Connect contact flow event -* **Idempotency**: New Idempotency utility -* **Docs**: Add example on how to integrate Batch utility with Sentry.io -* **Internal**: Added performance SLA tests for high level imports and Metrics validation/serialization +## Maintenance -## 1.10.5 - 2021-02-17 +* bump patch version +* fix unused fixture +* fix docstring on level [str,int] consistency +* fix test level typo +* trigger docs on new release ([#102](https://github.com/aws-powertools/powertools-lambda-python/issues/102)) ([#103](https://github.com/aws-powertools/powertools-lambda-python/issues/103)) +* trigger docs on new release ([#102](https://github.com/aws-powertools/powertools-lambda-python/issues/102)) +* trigger docs on new release -No changes. Bumped version to trigger new pipeline build for layer publishing. +## Regression -## 1.10.4 - 2021-02-17 +* log level docstring as str -### Fixed +## Pull Requests -* **Docs**: Fix anchor tags to be lower case -* **Docs**: Correct the docs location for the labeller +* Merge pull request [#106](https://github.com/aws-powertools/powertools-lambda-python/issues/106) from heitorlessa/fix/regression-104 -## 1.10.3 - 2021-02-04 -### Added +<a name="v1.1.0"></a> +## [v1.1.0] - 2020-08-14 +## Bug Fixes -* **Docs**: Migrated from Gatsby to MKdocs documentation system -* **Docs**: Included Getting started and Advanced sections in Core utilities, including additional examples +* auto-assigner filename as per docs -### Fixed +## Features -* **Tracer**: Disabled batching segments as X-Ray SDK does not flush traces upon reaching limits -* **Parser**: Model type is now compliant with mypy +* add support for logger inheritance ([#99](https://github.com/aws-powertools/powertools-lambda-python/issues/99)) +* enable issue auto-assigner to core team -## 1.10.2 - 2021-02-04 +## Maintenance -### Fixed +* bump to 1.1.0 ([#101](https://github.com/aws-powertools/powertools-lambda-python/issues/101)) +* **deps:** bump lodash from 4.17.15 to 4.17.19 in /docs ([#93](https://github.com/aws-powertools/powertools-lambda-python/issues/93)) -* **Utilities**: Correctly handle and list multiple exceptions in SQS batch processing utility. -* **Docs*:: Fix typos on AppConfig docstring import, and `SnsModel` typo in parser. -* **Utilities**: `typing_extensions` package is now only installed in Python < 3.8 -## 1.10.1 - 2021-01-19 +<a name="v1.0.2"></a> +## [v1.0.2] - 2020-07-16 +## Maintenance -### Fixed +* bump to 1.0.2 ([#90](https://github.com/aws-powertools/powertools-lambda-python/issues/90)) +* support aws-xray-sdk >=2.5.0 till <3.0.0 ([#89](https://github.com/aws-powertools/powertools-lambda-python/issues/89)) -* **Utilities**: Added `SnsSqsEnvelope` in `parser` to dynamically adjust model mismatch when customers use SNS + SQS instead of SNS + Lambda, since we've discovered three payload keys are slightly different. -## 1.10.0 - 2021-01-18 +<a name="v1.0.1"></a> +## [v1.0.1] - 2020-07-05 +## Bug Fixes -### Added -- **Utilities**: Added support for AppConfig in Parameters utility -- **Logger**: Added support for `extra` parameter to add additional root fields when logging messages -- **Logger**: Added support to Pytest Live Log feat. via feature toggle `POWERTOOLS_LOG_DEDUPLICATION_DISABLED` -- **Tracer**: Added support to disable auto-capturing response and exception as metadata -- **Utilities**: Added support to handle custom string/integer formats in JSON Schema in Validator utility -- **Install**: Added new Lambda Layer with all extra dependencies installed, available in Serverless Application Repository (SAR) +* append structured logs when injecting lambda context ([#86](https://github.com/aws-powertools/powertools-lambda-python/issues/86)) -### Fixed +## Documentation -- **Docs**: Added missing SNS parser model -- **Docs**: Added new environment variables for toggling features in Logger and Tracer: `POWERTOOLS_LOG_DEDUPLICATION_DISABLED`, `POWERTOOLS_TRACER_CAPTURE_RESPONSE`, `POWERTOOLS_TRACER_CAPTURE_ERROR` -- **Docs**: Fixed incorrect import for Cognito data classes in Event Sources utility +* add blog post in the readme -## 1.9.1 - 2020-12-21 -### Fixed -- **Logger**: Bugfix to prevent parent loggers with the same name being configured more than once +<a name="v1.0.0"></a> +## [v1.0.0] - 2020-06-18 +## Documentation -### Added -- **Docs**: Add clarification to Tracer docs for how `capture_method` decorator can cause function responses to be read and serialized. -- **Utilities**: Added equality to ease testing Event source data classes -- **Package**: Added `py.typed` for initial work needed for PEP 561 compliance +* customize contributing guide ([#77](https://github.com/aws-powertools/powertools-lambda-python/issues/77)) -## 1.9.0 - 2020-12-04 +## Features -### Added -- **Utilities**: Added Kinesis, S3, CloudWatch Logs, Application Load Balancer, and SES support in `Parser` -- **Docs**: Sidebar menu are now always expanded +* docs anonymized page view ([#82](https://github.com/aws-powertools/powertools-lambda-python/issues/82)) +* add metrics metadata ([#81](https://github.com/aws-powertools/powertools-lambda-python/issues/81)) -### Fixed -- **Docs**: Broken link to GitHub to homepage +## Maintenance -## 1.8.0 - 2020-11-20 +* bump to 1.0.0 GA ([#83](https://github.com/aws-powertools/powertools-lambda-python/issues/83)) +* add missing ':' and identation in examples +* cleanup tests ([#79](https://github.com/aws-powertools/powertools-lambda-python/issues/79)) +* remove deprecated code before GA ([#78](https://github.com/aws-powertools/powertools-lambda-python/issues/78)) +* move blockquotes as hidden comments -### Added -- **Utilities**: Added support for new EventBridge Replay field in `Parser` and `Event source data classes` -- **Utilities**: Added SNS support in `Parser` -- **Utilities**: Added API Gateway HTTP API data class support for new IAM and Lambda authorizer in `Event source data classes` -- **Docs**: Add new FAQ section for Logger on how to enable debug logging for boto3 -- **Docs**: Add explicit minimal set of permissions required to use Layers provided by Serverless Application Repository (SAR) -### Fixed -- **Docs**: Fix typo in Dataclasses example for SES when fetching common email headers +<a name="v0.11.0"></a> +## [v0.11.0] - 2020-06-10 +## Bug Fixes -## 1.7.0 - 2020-10-26 +* default dimension creation now happens when metrics are serialized instead of on metrics constructor ([#74](https://github.com/aws-powertools/powertools-lambda-python/issues/74)) -### Added -- **Utilities**: Add new `Parser` utility to provide parsing and deep data validation using Pydantic Models -- **Utilities**: Add case insensitive header lookup, and Cognito custom auth triggers to `Event source data classes` +## Maintenance -### Fixed -- **Logger**: keeps Lambda root logger handler, and add log filter instead to prevent child log records duplication -- **Docs**: Improve wording on adding log keys conditionally +* update CHANGELOG -## 1.6.1 - 2020-09-23 -### Fixed -- **Utilities**: Fix issue with boolean values in DynamoDB stream event data class. +<a name="v0.10.1"></a> +## [v0.10.1] - 2020-06-10 +## Bug Fixes -## 1.6.0 - 2020-09-22 +* default dimension creation now happens when metrics are serialized instead of on metrics constructor ([#74](https://github.com/aws-powertools/powertools-lambda-python/issues/74)) -### Added -- **Metrics**: Support adding multiple metric values to a single metric name -- **Utilities**: Add new `Validator` utility to validate inbound events and responses using JSON Schema -- **Utilities**: Add new `Event source data classes` utility to easily describe event schema of popular event sources -- **Docs**: Add new `Testing your code` section to both Logger and Metrics page, and content width is now wider -- **Tracer**: Support for automatically disable Tracer when running a Chalice app +## Documentation -### Fixed -- **Docs**: Improve wording on log sampling feature in Logger, and removed duplicate content on main page -- **Utilities**: Remove DeleteMessageBatch API call when there are no messages to delete +* fix contrast on highlighted code text ([#73](https://github.com/aws-powertools/powertools-lambda-python/issues/73)) -## 1.5.0 - 2020-09-04 +## Features -### Added -- **Logger**: Add `xray_trace_id` to log output to improve integration with CloudWatch Service Lens -- **Logger**: Allow reordering of logged output -- **Utilities**: Add new `SQS batch processing` utility to handle partial failures in processing message batches -- **Utilities**: Add typing utility providing static type for lambda context object -- **Utilities**: Add `transform=auto` in parameters utility to deserialize parameter values based on the key name +* improve error handling for log_metrics decorator ([#71](https://github.com/aws-powertools/powertools-lambda-python/issues/71)) +* add high level imports ([#70](https://github.com/aws-powertools/powertools-lambda-python/issues/70)) -### Fixed -- **Logger**: The value of `json_default` formatter is no longer written to logs +## Maintenance -## 1.4.0 - 2020-08-25 +* version bump 0.10.1 +* **deps:** bump graphql-playground-html from 1.6.19 to 1.6.25 in /docs -### Added -- **All**: Official Lambda Layer via [Serverless Application Repository](https://serverlessrepo.aws.amazon.com/applications/eu-west-1/057560766410/aws-lambda-powertools-python-layer) -- **Tracer**: `capture_method` and `capture_lambda_handler` now support **capture_response=False** parameter to prevent Tracer to capture response as metadata to allow customers running Tracer with sensitive workloads +## Pull Requests -### Fixed -- **Metrics**: Cold start metric is now completely separate from application metrics dimensions, making it easier and cheaper to visualize. - - This is a breaking change if you were graphing/alerting on both application metrics with the same name to compensate this previous malfunctioning - - Marked as bugfix as this is the intended behaviour since the beginning, as you shouldn't have the same application metric with different dimensions -- **Utilities**: SSMProvider within Parameters utility now have decrypt and recursive parameters correctly defined to support autocompletion +* Merge pull request [#72](https://github.com/aws-powertools/powertools-lambda-python/issues/72) from awslabs/dependabot/npm_and_yarn/docs/graphql-playground-html-1.6.25 -### Added -- **Tracer**: capture_lambda_handler and capture_method decorators now support `capture_response` parameter to not include function's response as part of tracing metadata -## 1.3.1 - 2020-08-22 -### Fixed -- **Tracer**: capture_method decorator did not properly handle nested context managers - -## 1.3.0 - 2020-08-21 -### Added -- **Utilities**: Add new `parameters` utility to retrieve a single or multiple parameters from SSM Parameter Store, Secrets Manager, DynamoDB, or your very own +<a name="v0.10.0"></a> +## v0.10.0 - 2020-06-08 +## Bug Fixes -## 1.2.0 - 2020-08-20 -### Added -- **Tracer**: capture_method decorator now supports generator functions (including context managers) - -## 1.1.3 - 2020-08-18 -### Fixed -- **Logger**: Logs emitted twice, structured and unstructured, due to Lambda configuring the root handler - -## 1.1.2 - 2020-08-16 -### Fixed -- **Docs**: Clarify confusion on Tracer reuse and `auto_patch=False` statement -- **Logger**: Autocomplete for log statements in PyCharm - -## 1.1.1 - 2020-08-14 -### Fixed -- **Logger**: Regression on `Logger` level not accepting `int` i.e. `Logger(level=logging.INFO)` - -## 1.1.0 - 2020-08-14 -### Added -- **Logger**: Support for logger inheritance with `child` parameter - -### Fixed -- **Logger**: Log level is now case insensitive via params and env var - -## 1.0.2 - 2020-07-16 -### Fixed -- **Tracer**: Correct AWS X-Ray SDK dependency to support 2.5.0 and higher - -## 1.0.1 - 2020-07-06 -### Fixed -- **Logger**: Fix a bug with `inject_lambda_context` causing existing Logger keys to be overridden if `structure_logs` was called before - -## 1.0.0 - 2020-06-18 -### Added -- **Metrics**: `add_metadata` method to add any metric metadata you'd like to ease finding metric related data via CloudWatch Logs -- Set status as General Availability - -## 0.11.0 - 2020-06-08 -### Added -- Imports can now be made from top level of module, e.g.: `from aws_lambda_powertools import Logger, Metrics, Tracer` - -### Fixed -- **Metrics**: Fix a bug with Metrics causing an exception to be thrown when logging metrics if dimensions were not explicitly added. - -### Changed -- **Metrics**: No longer throws exception by default in case no metrics are emitted when using the log_metrics decorator. - -## 0.10.0 - 2020-06-08 -### Added -- **Metrics**: `capture_cold_start_metric` parameter added to `log_metrics` decorator -- **Metrics**: Optional `namespace` and `service` parameters added to Metrics constructor to more closely resemble other core utils - -### Changed -- **Metrics**: Default dimension is now created based on `service` parameter or `POWERTOOLS_SERVICE_NAME` env var - -### Deprecated -- **Metrics**: `add_namespace` method deprecated in favor of using `namespace` parameter to Metrics constructor or `POWERTOOLS_METRICS_NAMESPACE` env var - -## 0.9.5 - 2020-06-02 -### Fixed -- **Metrics**: Coerce non-string dimension values to string -- **Logger**: Correct `cold_start`, `function_memory_size` values from string to bool and int respectively - -## 0.9.4 - 2020-05-29 -### Fixed -- **Metrics**: Fix issue where metrics were not correctly flushed, and cleared on every invocation - -## 0.9.3 - 2020-05-16 -### Fixed -- **Tracer**: Fix Runtime Error for nested sync due to incorrect loop usage - -## 0.9.2 - 2020-05-14 -### Fixed -- **Tracer**: Import aiohttp lazily so it's not a hard dependency - -## 0.9.0 - 2020-05-12 -### Added -- **Tracer**: Support for async functions in `Tracer` via `capture_method` decorator -- **Tracer**: Support for `aiohttp` via `aiohttp_trace_config` trace config -- **Tracer**: Support for patching specific modules via `patch_modules` param -- **Tracer**: Document escape hatch mechanisms via `tracer.provider` - -## 0.8.1 - 2020-05-1 -### Fixed -* **Metrics**: Fix metric unit casting logic if one passes plain string (value or key) -* **Metrics:**: Fix `MetricUnit` enum values for - - `BytesPerSecond` - - `KilobytesPerSecond` - - `MegabytesPerSecond` - - `GigabytesPerSecond` - - `TerabytesPerSecond` - - `BitsPerSecond` - - `KilobitsPerSecond` - - `MegabitsPerSecond` - - `GigabitsPerSecond` - - `TerabitsPerSecond` - - `CountPerSecond` - -## 0.8.0 - 2020-04-24 -### Added -- **Logger**: Introduced `Logger` class for structured logging as a replacement for `logger_setup` -- **Logger**: Introduced `Logger.inject_lambda_context` decorator as a replacement for `logger_inject_lambda_context` - -### Removed -- **Logger**: Raise `DeprecationWarning` exception for both `logger_setup`, `logger_inject_lambda_context` - -## 0.7.0 - 2020-04-20 -### Added -- **Middleware factory**: Introduced Middleware Factory to build your own middleware via `lambda_handler_decorator` - -### Fixed -- **Metrics**: Fixed metrics dimensions not being included correctly in EMF - -## 0.6.3 - 2020-04-09 -### Fixed -- **Logger**: Fix `log_metrics` decorator logic not calling the decorated function, and exception handling - -## 0.6.1 - 2020-04-08 -### Added -- **Metrics**: Introduces Metrics middleware to utilise CloudWatch Embedded Metric Format - -### Deprecated -- **Metrics**: Added deprecation warning for `log_metrics` - -## 0.5.0 - 2020-02-20 -### Added -- **Logger**: Introduced log sampling for debug - Thanks to [Danilo's contribution](https://github.com/awslabs/aws-lambda-powertools/pull/7) - -## 0.1.0 - 2019-11-15 -### Added -- Public beta release +* correct env var name for publish to pypi test ([#69](https://github.com/aws-powertools/powertools-lambda-python/issues/69)) +* release-drafter action syntax +* release-drafter label for new feature/major non-breaking changes +* cast dimension value to str to avoid issue where EMF silently fails ([#52](https://github.com/aws-powertools/powertools-lambda-python/issues/52)) +* ignore path that might seem a broken link [#49](https://github.com/aws-powertools/powertools-lambda-python/issues/49) +* open api ref in a new tab [#48](https://github.com/aws-powertools/powertools-lambda-python/issues/48) +* metrics not being flushed on every invocation ([#45](https://github.com/aws-powertools/powertools-lambda-python/issues/45)) +* [#35](https://github.com/aws-powertools/powertools-lambda-python/issues/35) duplicate changelog to project root +* [#24](https://github.com/aws-powertools/powertools-lambda-python/issues/24) correct example test and docs +* CI attempt 4 +* CI attempt 3 +* CI attempt 3 +* CI attempt 2 +* add missing single_metric example; test var name +* fix import of aws_lambda_logging to relative import +* **Makefile:** format before linting +* **make:** add twine as a dev dep +* **setup:** correct invalid license classifier +* **setup:** correct license to MIT-0 in meta + +## Documentation + +* build on master only +* clarify logger debug sampling message +* clean up readme in favour of docs website +* add install in main docs website +* add pypi badge + +## Features + +* add capture_cold_start_metric for log_metrics ([#67](https://github.com/aws-powertools/powertools-lambda-python/issues/67)) +* automate publishing to pypi ([#58](https://github.com/aws-powertools/powertools-lambda-python/issues/58)) +* add pre-commit hooks ([#64](https://github.com/aws-powertools/powertools-lambda-python/issues/64)) +* update Metrics interface to resemble tracer & logger: use "service" as its namespace. +* add codecov service ([#59](https://github.com/aws-powertools/powertools-lambda-python/issues/59)) +* add security and complexity baseline [#33](https://github.com/aws-powertools/powertools-lambda-python/issues/33) ([#57](https://github.com/aws-powertools/powertools-lambda-python/issues/57)) +* add pull request template [#33](https://github.com/aws-powertools/powertools-lambda-python/issues/33) +* add RFC template for proposals +* create issue templates +* readd release drafter action [#33](https://github.com/aws-powertools/powertools-lambda-python/issues/33) +* add release drafter ([#56](https://github.com/aws-powertools/powertools-lambda-python/issues/56)) +* add stale issues config [#33](https://github.com/aws-powertools/powertools-lambda-python/issues/33) ([#55](https://github.com/aws-powertools/powertools-lambda-python/issues/55)) +* enforce semantic PR titles ([#54](https://github.com/aws-powertools/powertools-lambda-python/issues/54)) +* add algolia search for docs and api ref ([#39](https://github.com/aws-powertools/powertools-lambda-python/issues/39)) +* add documentation website ([#37](https://github.com/aws-powertools/powertools-lambda-python/issues/37)) +* add docs to CI +* Add Python3.8 support +* **logger:** add log sampling +* **pypi:** add bumpversion, public release pypi +* **pyproject.toml:** move to poetry + +## Maintenance + +* version bump ([#68](https://github.com/aws-powertools/powertools-lambda-python/issues/68)) +* public beta version +* rename Makefile target docs-dev to docs-local ([#65](https://github.com/aws-powertools/powertools-lambda-python/issues/65)) +* correct docstring for log_metrics +* fix typo in metrics doc +* Correct test comment +* remove unused import +* formatting +* plat wheels are not needed +* reformat changelog to follow KeepAChangelog standard ([#50](https://github.com/aws-powertools/powertools-lambda-python/issues/50)) +* bump to release candidate +* renamed history to changelog dependabot +* grammar issues +* bump example to use 0.8.0 features +* clean up CI workflows +* fix github badge typo +* pypi monthly download badge +* lint +* bump 0.3.1 with logging patch +* bump history +* lint +* add Python 3.8 in badge as it's supported +* CI badge +* public beta version +* **deps:** bump bleach from 3.1.0 to 3.1.1 in /python +* **deps:** bump websocket-extensions from 0.1.3 to 0.1.4 in /docs ([#66](https://github.com/aws-powertools/powertools-lambda-python/issues/66)) + +## Pull Requests + +* Merge pull request [#60](https://github.com/aws-powertools/powertools-lambda-python/issues/60) from awslabs/improv/metrics_interface +* Merge pull request [#8](https://github.com/aws-powertools/powertools-lambda-python/issues/8) from awslabs/dependabot/pip/python/bleach-3.1.1 +* Merge pull request [#7](https://github.com/aws-powertools/powertools-lambda-python/issues/7) from danilohgds/sampling_feature +* Merge pull request [#5](https://github.com/aws-powertools/powertools-lambda-python/issues/5) from jfuss/feat/python38 + + +[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.17.0...HEAD +[v3.17.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.16.0...v3.17.0 +[v3.16.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.15.1...v3.16.0 +[v3.15.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.15.0...v3.15.1 +[v3.15.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.14.0...v3.15.0 +[v3.14.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.13.0...v3.14.0 +[v3.13.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.12.0...v3.13.0 +[v3.12.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.11.0...v3.12.0 +[v3.11.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.10.0...v3.11.0 +[v3.10.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.9.0...v3.10.0 +[v3.9.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.8.0...v3.9.0 +[v3.8.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.7.0...v3.8.0 +[v3.7.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.6.0...v3.7.0 +[v3.6.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.5.0...v3.6.0 +[v3.5.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.4.1...v3.5.0 +[v3.4.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.4.0...v3.4.1 +[v3.4.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.3.0...v3.4.0 +[v3.3.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.2.0...v3.3.0 +[v3.2.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.1.0...v3.2.0 +[v3.1.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.0.0...v3.1.0 +[v3.0.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.43.1...v3.0.0 +[v2.43.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.43.0...v2.43.1 +[v2.43.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.42.0...v2.43.0 +[v2.42.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.41.0...v2.42.0 +[v2.41.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.40.1...v2.41.0 +[v2.40.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.40.0...v2.40.1 +[v2.40.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.39.1...v2.40.0 +[v2.39.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.39.0...v2.39.1 +[v2.39.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.38.1...v2.39.0 +[v2.38.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.38.0...v2.38.1 +[v2.38.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.37.0...v2.38.0 +[v2.37.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.36.0...v2.37.0 +[v2.36.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.35.1...v2.36.0 +[v2.35.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.35.0...v2.35.1 +[v2.35.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.34.2...v2.35.0 +[v2.34.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.34.1...v2.34.2 +[v2.34.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.34.0...v2.34.1 +[v2.34.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.33.1...v2.34.0 +[v2.33.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.33.0...v2.33.1 +[v2.33.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.32.0...v2.33.0 +[v2.32.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.31.0...v2.32.0 +[v2.31.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.30.2...v2.31.0 +[v2.30.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.30.1...v2.30.2 +[v2.30.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.30.0...v2.30.1 +[v2.30.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.29.1...v2.30.0 +[v2.29.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.29.0...v2.29.1 +[v2.29.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.28.1...v2.29.0 +[v2.28.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.28.0...v2.28.1 +[v2.28.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.27.1...v2.28.0 +[v2.27.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.27.0...v2.27.1 +[v2.27.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.26.1...v2.27.0 +[v2.26.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.26.0...v2.26.1 +[v2.26.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.25.1...v2.26.0 +[v2.25.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.25.0...v2.25.1 +[v2.25.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.24.0...v2.25.0 +[v2.24.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.23.1...v2.24.0 +[v2.23.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.23.0...v2.23.1 +[v2.23.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.22.0...v2.23.0 +[v2.22.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.21.0...v2.22.0 +[v2.21.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.20.0...v2.21.0 +[v2.20.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.19.0...v2.20.0 +[v2.19.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.18.0...v2.19.0 +[v2.18.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.17.0...v2.18.0 +[v2.17.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.16.2...v2.17.0 +[v2.16.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.16.1...v2.16.2 +[v2.16.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.16.0...v2.16.1 +[v2.16.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.15.0...v2.16.0 +[v2.15.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.14.1...v2.15.0 +[v2.14.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.14.0...v2.14.1 +[v2.14.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.13.0...v2.14.0 +[v2.13.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.12.0...v2.13.0 +[v2.12.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.11.0...v2.12.0 +[v2.11.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.10.0...v2.11.0 +[v2.10.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.9.1...v2.10.0 +[v2.9.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.9.0...v2.9.1 +[v2.9.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.8.0...v2.9.0 +[v2.8.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.7.1...v2.8.0 +[v2.7.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.7.0...v2.7.1 +[v2.7.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.6.0...v2.7.0 +[v2.6.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.5.0...v2.6.0 +[v2.5.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.4.0...v2.5.0 +[v2.4.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.3.1...v2.4.0 +[v2.3.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.3.0...v2.3.1 +[v2.3.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.2.0...v2.3.0 +[v2.2.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.1.0...v2.2.0 +[v2.1.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.0.0...v2.1.0 +[v2.0.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.31.1...v2.0.0 +[v1.31.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.31.0...v1.31.1 +[v1.31.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.30.0...v1.31.0 +[v1.30.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.29.2...v1.30.0 +[v1.29.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.29.1...v1.29.2 +[v1.29.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.29.0...v1.29.1 +[v1.29.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.28.0...v1.29.0 +[v1.28.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.27.0...v1.28.0 +[v1.27.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.7...v1.27.0 +[v1.26.7]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.6...v1.26.7 +[v1.26.6]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.5...v1.26.6 +[v1.26.5]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.4...v1.26.5 +[v1.26.4]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.3...v1.26.4 +[v1.26.3]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.2...v1.26.3 +[v1.26.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.1...v1.26.2 +[v1.26.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.26.0...v1.26.1 +[v1.26.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.10...v1.26.0 +[v1.25.10]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.9...v1.25.10 +[v1.25.9]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.8...v1.25.9 +[v1.25.8]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.7...v1.25.8 +[v1.25.7]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.6...v1.25.7 +[v1.25.6]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.5...v1.25.6 +[v1.25.5]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.4...v1.25.5 +[v1.25.4]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.3...v1.25.4 +[v1.25.3]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.2...v1.25.3 +[v1.25.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.1...v1.25.2 +[v1.25.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.25.0...v1.25.1 +[v1.25.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.24.2...v1.25.0 +[v1.24.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.24.1...v1.24.2 +[v1.24.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.24.0...v1.24.1 +[v1.24.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.23.0...v1.24.0 +[v1.23.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.22.0...v1.23.0 +[v1.22.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.21.1...v1.22.0 +[v1.21.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.21.0...v1.21.1 +[v1.21.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.20.2...v1.21.0 +[v1.20.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.20.1...v1.20.2 +[v1.20.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.20.0...v1.20.1 +[v1.20.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.19.0...v1.20.0 +[v1.19.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.18.1...v1.19.0 +[v1.18.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.18.0...v1.18.1 +[v1.18.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.17.1...v1.18.0 +[v1.17.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.17.0...v1.17.1 +[v1.17.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.16.1...v1.17.0 +[v1.16.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.16.0...v1.16.1 +[v1.16.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.15.1...v1.16.0 +[v1.15.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.15.0...v1.15.1 +[v1.15.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.14.0...v1.15.0 +[v1.14.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.13.0...v1.14.0 +[v1.13.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.12.0...v1.13.0 +[v1.12.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.11.0...v1.12.0 +[v1.11.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.10.5...v1.11.0 +[v1.10.5]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.10.4...v1.10.5 +[v1.10.4]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.10.3...v1.10.4 +[v1.10.3]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.10.2...v1.10.3 +[v1.10.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.10.1...v1.10.2 +[v1.10.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.10.0...v1.10.1 +[v1.10.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.9.1...v1.10.0 +[v1.9.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.9.0...v1.9.1 +[v1.9.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.8.0...v1.9.0 +[v1.8.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.7.0...v1.8.0 +[v1.7.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.6.1...v1.7.0 +[v1.6.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.6.0...v1.6.1 +[v1.6.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.5.0...v1.6.0 +[v1.5.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.4.0...v1.5.0 +[v1.4.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.3.1...v1.4.0 +[v1.3.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.3.0...v1.3.1 +[v1.3.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.2.0...v1.3.0 +[v1.2.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.1.3...v1.2.0 +[v1.1.3]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.1.2...v1.1.3 +[v1.1.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.1.1...v1.1.2 +[v1.1.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.1.0...v1.1.1 +[v1.1.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.0.2...v1.1.0 +[v1.0.2]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.0.1...v1.0.2 +[v1.0.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v1.0.0...v1.0.1 +[v1.0.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v0.11.0...v1.0.0 +[v0.11.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v0.10.1...v0.11.0 +[v0.10.1]: https://github.com/aws-powertools/powertools-lambda-python/compare/v0.10.0...v0.10.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0941fbc535b..dc04db7ce4f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,26 @@ +<!-- markdownlint-disable MD043 MD041 --> +# Table of contents <!-- omit in toc --> + +- [Contributing Guidelines](#contributing-guidelines) + - [Reporting Bugs/Feature Requests](#reporting-bugsfeature-requests) + - [Contributing via Pull Requests](#contributing-via-pull-requests) + - [Dev setup](#dev-setup) + - [Local documentation](#local-documentation) + - [Conventions](#conventions) + - [General terminology and practices](#general-terminology-and-practices) + - [Testing definition](#testing-definition) + - [Finding contributions to work on](#finding-contributions-to-work-on) + - [Code of Conduct](#code-of-conduct) + - [Security issue notifications](#security-issue-notifications) + - [Troubleshooting](#troubleshooting) + - [API reference documentation](#api-reference-documentation) + - [Licensing](#licensing) + # Contributing Guidelines -Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional -documentation, we greatly value feedback and contributions from our community. +<!-- markdownlint-disable MD013 --> +Thank you for your interest in contributing to our project. Whether it's a [bug report](https://github.com/aws-powertools/powertools-lambda-python/issues/new?assignees=&labels=bug%2Ctriage&projects=&template=bug_report.yml&title=Bug%3A+TITLE), [new feature](https://github.com/aws-powertools/powertools-lambda-python/issues/new?assignees=&labels=feature-request%2Ctriage&projects=&template=feature_request.yml&title=Feature+request%3A+TITLE), [correction](https://github.com/aws-powertools/powertools-lambda-python/issues/new/choose), or [additional documentation](https://github.com/aws-powertools/powertools-lambda-python/issues/new?assignees=&labels=documentation%2Ctriage&projects=&template=documentation_improvements.yml&title=Docs%3A+TITLE), we greatly value feedback and contributions from our community. +<!-- markdownlint-enable MD013 --> Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. @@ -10,8 +29,9 @@ information to effectively respond to your bug report or contribution. We welcome you to use the GitHub issue tracker to report bugs, suggest features, or documentation improvements. -When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already -reported the issue. Please try to include as much information as you can. +<!-- markdownlint-disable MD013 --> +[When filing an issue](https://github.com/aws-powertools/powertools-lambda-python/issues/new/choose), please check [existing open](https://github.com/aws-powertools/powertools-lambda-python/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc), or [recently closed](https://github.com/aws-powertools/powertools-lambda-python/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed), issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. +<!-- markdownlint-enable MD013 --> ## Contributing via Pull Requests @@ -19,55 +39,129 @@ Contributions via pull requests are much appreciated. Before sending us a pull r 1. You are working against the latest source on the **develop** branch. 2. You check existing open, and recently merged pull requests to make sure someone else hasn't addressed the problem already. -3. You open a [RFC issue](https://github.com/awslabs/aws-lambda-powertools-python/issues/new?assignees=&labels=RFC%2C+triage&template=rfc.md&title=RFC%3A+) to discuss any significant work - we would hate for your time to be wasted. +3. You open an [issue](https://github.com/aws-powertools/powertools-lambda-python/issues/new/choose) before you begin any implementation. We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. + +At a high level, these are the steps to get code merged in the repository - don't worry, nearly all of them are automated. + +```mermaid +timeline + title Code integration journey (CI) + Project setup <br> (make dev) : Code checkout + : Virtual environment + : Dependencies + : Git pre-commit hooks + : Local branch + : Local changes + : Local tests + + Pre-commit checks <br> (git commit) : Merge conflict check + : Trailing whitespaces + : TOML checks + : Code linting (standards) + : Markdown linting + : CloudFormation linting + : GitHub Actions linting + : Terraform linting + : Secrets linting + + Pre-Pull Request <br> (make pr) : Code linting + : Docs linting + : Static typing analysis + : Tests (unit|functional|perf|dependencies) + : Security baseline + : Complexity baseline + : +pre-commit checks + + Pull Request <br> (CI checks) : Semantic PR title check + : Related issue check + : Acknowledgment check + : Code coverage diff + : Contribution size check + : Contribution category check + : Dependency vulnerability check + : GitHub Actions security check + : +pre-pull request checks + + After merge <br> (CI checks) : End-to-end tests + : Longer SAST check + : Security posture check (scorecard) + : GitHub Actions security check + : Rebuild Changelog + : Deploy staging docs + : Update draft release +``` ### Dev setup +[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/from-referrer/) + +Firstly, [fork the repository](https://github.com/aws-powertools/powertools-lambda-python/fork). + +To setup your development environment, we recommend using our pre-configured Cloud environment: <https://gitpod.io/#https://github.com/YOUR_USERNAME/aws-lambda-powertools-python>. Replace YOUR_USERNAME with your GitHub username or organization so the Cloud environment can target your fork accordingly. + +Alternatively, you can use `make dev` within your local virtual environment. + To send us a pull request, please follow these steps: -1. Fork the repository. -2. Install dependencies in a virtual env with poetry, and pre-commit hooks: `make dev` -3. Create a new branch to focus on the specific change you are contributing e.g. `improv/logger-debug-sampling` -4. Run all tests, and code baseline checks: `make pr` +1. Create a new branch to focus on the specific change you are contributing e.g. `improv/logger-debug-sampling` +2. Run all tests, and code baseline checks: `make pr` - Git hooks will run linting and formatting while `make pr` run deep checks that also run in the CI process -4. Commit to your fork using clear commit messages. -5. Send us a pull request with a [conventional semantic title](https://github.com/awslabs/aws-lambda-powertools-python/pull/67), and answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +3. Commit to your fork using clear commit messages. +4. Send us a pull request with a [conventional semantic title](https://github.com/aws-powertools/powertools-lambda-python/pull/67), and answering any default questions in the pull request interface. +5. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). -#### Local documentation +### Local documentation You might find useful to run both the documentation website and the API reference locally while contributing: -* **API reference**: `make docs-api-local` -* **Docs website**: `make docs-local` - - If you prefer using Docker: `make docs-local-docker` +- **API reference**: `make docs-api-local` +- **Docs website**: `make docs-local` + - If you prefer using Docker: `make docs-local-docker` + +## Conventions -### Conventions +### General terminology and practices -Category | Convention -------------------------------------------------- | --------------------------------------------------------------------------------- -**Docstring** | We use a slight variation of numpy convention with markdown to help generate more readable API references. -**Style guide** | We use black as well as flake8 extensions to enforce beyond good practices [PEP8](https://pep8.org/). We strive to make use of type annotation as much as possible, but don't overdo in creating custom types. -**Core utilities** | Core utilities use a Class, always accept `service` as a constructor parameter, can work in isolation, and are also available in other languages implementation. -**Utilities** | Utilities are not as strict as core and focus on solving a developer experience problem while following the project [Tenets](https://awslabs.github.io/aws-lambda-powertools-python/#tenets). -**Exceptions** | Specific exceptions live within utilities themselves and use `Error` suffix e.g. `MetricUnitError`. -**Git commits** | We follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). These are not enforced as we squash and merge PRs, but PR titles are enforced during CI. -**Documentation** | API reference docs are generated from docstrings which should have Examples section to allow developers to have what they need within their own IDE. Documentation website covers the wider usage, tips, and strive to be concise. +| Category | Convention | +| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Docstring** | We use a slight variation of Numpy convention with markdown to help generate more readable API references. | +| **Style guide** | We use black as well as [Ruff](https://beta.ruff.rs/docs/) to enforce beyond good practices [PEP8](https://pep8.org/). We use type annotations and enforce static type checking at CI (mypy). | +| **Core utilities** | Core utilities use a Class, always accept `service` as a constructor parameter, can work in isolation, and are also available in other languages implementation. | +| **Utilities** | Utilities are not as strict as core and focus on solving a developer experience problem while following the project [Tenets](https://docs.powertools.aws.dev/lambda/python/#tenets). | +| **Exceptions** | Specific exceptions live within utilities themselves and use `Error` suffix e.g. `MetricUnitError`. | +| **Git commits** | We follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). We do not enforce conventional commits on contributors to lower the entry bar. Instead, we enforce a conventional PR title so our label automation and changelog are generated correctly. | +| **API documentation** | API reference docs are generated from docstrings which should have Examples section to allow developers to have what they need within their own IDE. Documentation website covers the wider usage, tips, and strive to be concise. | +| **Documentation** | We treat it like a product. We sub-divide content aimed at getting started (80% of customers) vs advanced usage (20%). We also ensure customers know how to unit test their code when using our features. | + +### Testing definition + +We group tests in different categories + +| Test | When to write | Notes | Speed | +| ----------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | +| Unit tests | Verify the smallest possible unit works. | Networking access is prohibited. Prefer Functional tests given our complexity. | Lightning fast (nsec to ms) | +| Functional tests | Guarantee functionality works as expected. It's a subset of integration test covering multiple units. | No external dependency. Prefer Fake implementations (in-memory) over Mocks and Stubs. | Fast (ms to few seconds at worst) | +| Integration tests | Gain confidence that code works with one or more external dependencies. | No need for a Lambda function. Use our code base against an external dependency _e.g., fetch an existing SSM parameter_. | Moderate to slow (a few minutes) | +| End-to-end tests | Gain confidence that a Lambda function with our code operates as expected. | It simulates how customers configure, deploy, and run their Lambda function - Event Source configuration, IAM permissions, etc. | Slow (minutes) | +| Performance tests | Ensure critical operations won't increase latency and costs to customers. | CI arbitrary hardware can make it flaky. We'll resume writing perf test after our new Integ/End have significant coverage. | Fast to moderate (a few seconds to a few minutes) | + +**NOTE**: Functional tests are mandatory. We have plans to create a guide on how to create these different tests. Maintainers will help indicate whether additional tests are necessary and provide assistance as required. ## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/help wanted/invalid/question/documentation), looking at any 'help wanted' issues is a great place to start. +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/help wanted/invalid/question/documentation), [looking at any 'help wanted' issues is a great place to start](https://github.com/orgs/aws-powertools/projects/3/views/5?query=is%3Aopen+sort%3Aupdated-desc). ## Code of Conduct This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. +<opensource-codeofconduct@amazon.com> with any additional questions or comments. ## Security issue notifications + If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. ## Troubleshooting @@ -78,9 +172,9 @@ When you are working on the codebase and you use the local API reference documen This happens when: -* You did not install the local dev environment yet +- You did not install the local dev environment yet - You can install dev deps with `make dev` command -* The code in the repository is raising an exception while the `pdoc` is scanning the codebase +- The code in the repository is raising an exception while the `pdoc` is scanning the codebase - Unfortunately, this exception is not shown to you, but if you run, `poetry run pdoc --pdf aws_lambda_powertools`, the exception is shown and you can prevent the exception from being raised - Once resolved the documentation should load correctly again diff --git a/LICENSE b/LICENSE index 9e30e05ab6d..fcdede53dc8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ +MIT No Attribution + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 00000000000..2fa7b2eed23 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,3 @@ +<!-- markdownlint-disable --> + +Maintainers' playbook moved: https://docs.powertools.aws.dev/lambda/python/latest/maintainers/ diff --git a/Makefile b/Makefile index 5b8e9b0d689..08f8829fe3b 100644 --- a/Makefile +++ b/Makefile @@ -1,52 +1,68 @@ -.PHONY: target dev format lint test coverage-html pr build build-docs build-docs-api build-docs-website -.PHONY: docs-local docs-api-local security-baseline complexity-baseline release-prod release-test release +.PHONY: target dev format lint test coverage-html pr build build-docs build-docs-website +.PHONY: docs-local security-baseline complexity-baseline release-prod release-test release target: @$(MAKE) pr dev: pip install --upgrade pip pre-commit poetry - poetry install --extras "pydantic" + @$(MAKE) dev-version-plugin + poetry install --extras "all redis datamasking valkey" pre-commit install +dev-quality-code: + pip install --upgrade pip pre-commit poetry + @$(MAKE) dev-version-plugin + poetry install --extras "all redis datamasking valkey" + pre-commit install + +dev-gitpod: + pip install --upgrade pip poetry + poetry install --extras "all redis datamasking valkey" + pre-commit install + +format-check: + poetry run ruff format aws_lambda_powertools tests examples --check + format: - poetry run isort aws_lambda_powertools tests - poetry run black aws_lambda_powertools tests + poetry run ruff format aws_lambda_powertools tests examples lint: format - poetry run flake8 aws_lambda_powertools/* tests/* + poetry run ruff check aws_lambda_powertools tests examples + +lint-docs: + docker run -v ${PWD}:/markdown 06kellyjac/markdownlint-cli "docs" + +lint-docs-fix: + docker run -v ${PWD}:/markdown 06kellyjac/markdownlint-cli --fix "docs" test: - poetry run pytest -m "not perf" --cov=aws_lambda_powertools --cov-report=xml + poetry run pytest -m "not perf" --ignore tests/e2e --cov=aws_lambda_powertools --cov-report=xml poetry run pytest --cache-clear tests/performance +test-dependencies: + poetry run nox --error-on-external-run --reuse-venv=yes --non-interactive + +test-pydanticv2: + poetry run pytest -m "not perf" --ignore tests/e2e + unit-test: poetry run pytest tests/unit +e2e-test: + poetry run pytest tests/e2e + coverage-html: - poetry run pytest -m "not perf" --cov=aws_lambda_powertools --cov-report=html + poetry run pytest -m "not perf" --ignore tests/e2e --cov=aws_lambda_powertools --cov-report=html pre-commit: pre-commit run --show-diff-on-failure -pr: lint pre-commit test security-baseline complexity-baseline +pr: lint lint-docs mypy pre-commit test security-baseline complexity-baseline build: pr poetry build -release-docs: - @echo "Rebuilding docs" - rm -rf site api - @echo "Updating website docs" - poetry run mike deploy --push --update-aliases ${VERSION} ${ALIAS} - @echo "Building API docs" - @$(MAKE) build-docs-api - -build-docs-api: - poetry run pdoc --html --output-dir ./api/ ./aws_lambda_powertools --force - mv -f ./api/aws_lambda_powertools/* ./api/ - rm -rf ./api/aws_lambda_powertools - docs-local: poetry run mkdocs serve @@ -54,9 +70,6 @@ docs-local-docker: docker build -t squidfunk/mkdocs-material ./docs/ docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material -docs-api-local: - poetry run pdoc --http : aws_lambda_powertools - security-baseline: poetry run bandit --baseline bandit.baseline -r aws_lambda_powertools @@ -64,7 +77,7 @@ complexity-baseline: $(info Maintenability index) poetry run radon mi aws_lambda_powertools $(info Cyclomatic complexity index) - poetry run xenon --max-absolute C --max-modules A --max-average A aws_lambda_powertools + poetry run xenon --max-absolute C --max-modules A --max-average A aws_lambda_powertools --exclude aws_lambda_powertools/shared/json_encoder.py,aws_lambda_powertools/utilities/validation/base.py,aws_lambda_powertools/event_handler/api_gateway.py # # Use `poetry version <major>/<minor></patch>` for version bump @@ -84,8 +97,14 @@ release: pr $(MAKE) release-prod changelog: - @echo "[+] Pre-generating CHANGELOG for tag: $$(git describe --abbrev=0 --tag)" - docker run -v "${PWD}":/workdir quay.io/git-chglog/git-chglog $$(git describe --abbrev=0 --tag).. > TMP_CHANGELOG.md + git fetch --tags origin + CURRENT_VERSION=$(shell git describe --abbrev=0 --tag) ;\ + echo "[+] Pre-generating CHANGELOG for tag: $$CURRENT_VERSION" ;\ + docker run -v "${PWD}":/workdir quay.io/git-chglog/git-chglog:0.15.1 > CHANGELOG.md mypy: - poetry run mypy --pretty aws_lambda_powertools + poetry run mypy --pretty aws_lambda_powertools examples + + +dev-version-plugin: + poetry self add git+https://github.com/monim67/poetry-bumpversion@348de6f247222e2953d649932426e63492e0a6bf diff --git a/README.md b/README.md index 56ad5c0b70c..0fb106fbc97 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,104 @@ -# AWS Lambda Powertools (Python) +<!-- markdownlint-disable MD013 MD041 MD043 --> +# Powertools for AWS Lambda (Python) -![Build](https://github.com/awslabs/aws-lambda-powertools/workflows/Powertools%20Python/badge.svg?branch=master) -[![codecov.io](https://codecov.io/github/awslabs/aws-lambda-powertools-python/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/awslabs/aws-lambda-powertools-python) -![PythonSupport](https://img.shields.io/static/v1?label=python&message=3.6%20|%203.7|%203.8|%203.9&color=blue?style=flat-square&logo=python) ![PyPI version](https://badge.fury.io/py/aws-lambda-powertools.svg) ![PyPi monthly downloads](https://img.shields.io/pypi/dm/aws-lambda-powertools) +[![Build](https://github.com/aws-powertools/powertools-lambda-python/actions/workflows/quality_check.yml/badge.svg)](https://github.com/aws-powertools/powertools-lambda-python/actions/workflows/python_build.yml) +[![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-python/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-python) +![PythonSupport](https://img.shields.io/static/v1?label=python&message=%203.9|%203.10|%203.11|%203.12|%203.13&color=blue?style=flat-square&logo=python) ![PyPI version](https://badge.fury.io/py/aws-lambda-powertools.svg) ![PyPi monthly downloads](https://img.shields.io/pypi/dm/aws-lambda-powertools) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-python/badge)](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-python) [![Join our Discord](https://dcbadge.vercel.app/api/server/B8zZKbbyET?style=flat-square)](https://discord.gg/B8zZKbbyET) -A suite of Python utilities for AWS Lambda functions to ease adopting best practices such as tracing, structured logging, custom metrics, and more. (AWS Lambda Powertools [Java](https://github.com/awslabs/aws-lambda-powertools-java) and [Typescript](https://github.com/awslabs/aws-lambda-powertools-typescript) is also available). +Powertools for AWS Lambda (Python) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.powertools.aws.dev/lambda/python/latest/#features). -**[📜Documentation](https://awslabs.github.io/aws-lambda-powertools-python/)** | **[🐍PyPi](https://pypi.org/project/aws-lambda-powertools/)** | **[Roadmap](https://github.com/awslabs/aws-lambda-powertools-roadmap/projects/1)** | **[Detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-lambda-powertools/)** +> Also available in [Java](https://github.com/aws-powertools/powertools-lambda-java), [Typescript](https://github.com/aws-powertools/powertools-lambda-typescript), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet). -> **An AWS Developer Acceleration (DevAx) initiative by Specialist Solution Architects | aws-devax-open-source@amazon.com** +**[📜Documentation](https://docs.powertools.aws.dev/lambda/python/)** | **[🐍PyPi](https://pypi.org/project/aws-lambda-powertools/)** | **[Roadmap](https://docs.powertools.aws.dev/lambda/python/latest/roadmap/)** | **[Detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-lambda-powertools/)** -## Features +![hero-image](https://user-images.githubusercontent.com/3340292/198254617-d0fdb672-86a6-4988-8a40-adf437135e0a.png) -* **[Tracing](https://awslabs.github.io/aws-lambda-powertools-python/latest/core/tracer/)** - Decorators and utilities to trace Lambda function handlers, and both synchronous and asynchronous functions -* **[Logging](https://awslabs.github.io/aws-lambda-powertools-python/latest/core/logger/)** - Structured logging made easier, and decorator to enrich structured logging with key Lambda context details -* **[Metrics](https://awslabs.github.io/aws-lambda-powertools-python/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) -* **[Event handler: AppSync](https://awslabs.github.io/aws-lambda-powertools-python/latest/core/event_handler/appsync/)** - AWS AppSync event handler for Lambda Direct Resolver and Amplify GraphQL Transformer function -* **[Event handler: API Gateway and ALB](https://awslabs.github.io/aws-lambda-powertools-python/latest/core/event_handler/api_gateway/)** - Amazon API Gateway REST/HTTP API and ALB event handler for Lambda functions invoked using Proxy integration -* **[Bring your own middleware](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/middleware_factory/)** - Decorator factory to create your own middleware to run logic before, and after each Lambda invocation -* **[Parameters utility](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/parameters/)** - Retrieve and cache parameter values from Parameter Store, Secrets Manager, or DynamoDB -* **[Batch processing](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/batch/)** - Handle partial failures for AWS SQS batch processing -* **[Typing](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/typing/)** - Static typing classes to speedup development in your IDE -* **[Validation](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/validation/)** - JSON Schema validator for inbound events and responses -* **[Event source data classes](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/data_classes/)** - Data classes describing the schema of common Lambda event triggers -* **[Parser](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/parser/)** - Data parsing and deep validation using Pydantic -* **[Idempotency](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/idempotency/)** - Convert your Lambda functions into idempotent operations which are safe to retry -* **[Feature Flags](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/feature_flags/)** - A simple rule engine to evaluate when one or multiple features should be enabled depending on the input +## Features +* **[Tracing](https://docs.powertools.aws.dev/lambda/python/latest/core/tracer/)** - Decorators and utilities to trace Lambda function handlers, and both synchronous and asynchronous functions +* **[Logging](https://docs.powertools.aws.dev/lambda/python/latest/core/logger/)** - Structured logging made easier, and decorator to enrich structured logging with key Lambda context details +* **[Metrics](https://docs.powertools.aws.dev/lambda/python/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) +* **[Event handler: AppSync](https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/appsync/)** - AWS AppSync event handler for Lambda Direct Resolver and Amplify GraphQL Transformer function +* **[Event handler: API Gateway and ALB](https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/)** - Amazon API Gateway REST/HTTP API and ALB event handler for Lambda functions invoked using Proxy integration +* **[Event handler: Agents for Amazon Bedrock](https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/bedrock_agents/)** - Create Agents for Amazon Bedrock, automatically generating OpenAPI schemas +* **[Bring your own middleware](https://docs.powertools.aws.dev/lambda/python/latest/utilities/middleware_factory/)** - Decorator factory to create your own middleware to run logic before, and after each Lambda invocation +* **[Parameters utility](https://docs.powertools.aws.dev/lambda/python/latest/utilities/parameters/)** - Retrieve and cache parameter values from Parameter Store, Secrets Manager, or DynamoDB +* **[Batch processing](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)** - Handle partial failures for AWS SQS batch processing +* **[Typing](https://docs.powertools.aws.dev/lambda/python/latest/utilities/typing/)** - Static typing classes to speedup development in your IDE +* **[Validation](https://docs.powertools.aws.dev/lambda/python/latest/utilities/validation/)** - JSON Schema validator for inbound events and responses +* **[Event source data classes](https://docs.powertools.aws.dev/lambda/python/latest/utilities/data_classes/)** - Data classes describing the schema of common Lambda event triggers +* **[Parser](https://docs.powertools.aws.dev/lambda/python/latest/utilities/parser/)** - Data parsing and deep validation using Pydantic +* **[Idempotency](https://docs.powertools.aws.dev/lambda/python/latest/utilities/idempotency/)** - Convert your Lambda functions into idempotent operations which are safe to retry +* **[Data Masking](https://docs.powertools.aws.dev/lambda/python/latest/utilities/data_masking/)** - Protect confidential data with easy removal or encryption +* **[Feature Flags](https://docs.powertools.aws.dev/lambda/python/latest/utilities/feature_flags/)** - A simple rule engine to evaluate when one or multiple features should be enabled depending on the input +* **[Streaming](https://docs.powertools.aws.dev/lambda/python/latest/utilities/streaming/)** - Streams datasets larger than the available memory as streaming data. ### Installation With [pip](https://pip.pypa.io/en/latest/index.html) installed, run: ``pip install aws-lambda-powertools`` - ## Tutorial and Examples -* [Tutorial](https://awslabs.github.io/aws-lambda-powertools-python/latest/tutorial) +* [Tutorial](https://docs.powertools.aws.dev/lambda/python/latest/tutorial) * [Serverless Shopping cart](https://github.com/aws-samples/aws-serverless-shopping-cart) * [Serverless Airline](https://github.com/aws-samples/aws-serverless-airline-booking) * [Serverless E-commerce platform](https://github.com/aws-samples/aws-serverless-ecommerce-platform) * [Serverless GraphQL Nanny Booking Api](https://github.com/trey-rosius/babysitter_api) +## How to support Powertools for AWS Lambda (Python)? + +### Becoming a reference customer + +Knowing which companies are using this library is important to help prioritize the project internally. If your company is using Powertools for AWS Lambda (Python), you can request to have your name and logo added to the README file by raising a [Support Powertools for AWS Lambda (Python) (become a reference)](https://github.com/aws-powertools/powertools-lambda-python/issues/new?assignees=&labels=customer-reference&template=support_powertools.yml&title=%5BSupport+Lambda+Powertools%5D%3A+%3Cyour+organization+name%3E) issue. + +The following companies, among others, use Powertools: + +* [Alma Media](https://www.almamedia.fi/en/) +* [Banxware](https://www.banxware.com/) +* [Brsk](https://www.brsk.co.uk/) +* [BusPatrol](https://buspatrol.com/) +* [Capital One](https://www.capitalone.com/) +* [Caylent](https://caylent.com/) +* [CHS Inc.](https://www.chsinc.com/) +* [CPQi (Exadel Financial Services)](https://cpqi.com/) +* [CloudZero](https://www.cloudzero.com/) +* [CyberArk](https://www.cyberark.com/) +* [Flyweight](https://flyweight.io/) +* [globaldatanet](https://globaldatanet.com/) +* [Guild](https://guild.com/) +* [IMS](https://ims.tech/) +* [Instil](https://instil.co/) +* [Jit Security](https://www.jit.io/) +* [LocalStack](https://www.localstack.cloud/) +* [Propellor.ai](https://www.propellor.ai/) +* [Pushpay](https://pushpay.com/) +* [Recast](https://getrecast.com/) +* [TopSport](https://www.topsport.com.au/) +* [Transformity](https://transformity.tech/) +* [Trek10](https://www.trek10.com/) +* [Vertex Pharmaceuticals](https://www.vrtx.com/) + +### Sharing your work + +Share what you did with Powertools for AWS Lambda (Python) 💞💞. Blog post, workshops, presentation, sample apps and others. Check out what the community has already shared about Powertools for AWS Lambda (Python) [here](https://docs.powertools.aws.dev/lambda/python/latest/we_made_this/). + +### Using Lambda Layer or SAR + +This helps us understand who uses Powertools for AWS Lambda (Python) in a non-intrusive way, and helps us gain future investments for other Powertools for AWS Lambda languages. When [using Layers](https://docs.powertools.aws.dev/lambda/python/latest/#lambda-layer), you can add Powertools for AWS Lambda (Python) as a dev dependency (or as part of your virtual env) to not impact the development process. + ## Credits * Structured logging initial implementation from [aws-lambda-logging](https://gitlab.com/hadrien/aws_lambda_logging) -* Powertools idea [DAZN Powertools](https://github.com/getndazn/dazn-lambda-powertools/) +* Powertools for AWS Lambda (Python) idea [DAZN Powertools](https://github.com/getndazn/dazn-lambda-powertools/) ## Connect -* **AWS Developers Slack**: `#lambda-powertools` - **[Invite, if you don't have an account](https://join.slack.com/t/awsdevelopers/shared_invite/zt-yryddays-C9fkWrmguDv0h2EEDzCqvw)** -* **Email**: aws-lambda-powertools-feedback@amazon.com +* **Powertools for AWS Lambda on Discord**: `#python` - **[Invite link](https://discord.gg/B8zZKbbyET)** +* **Email**: <aws-powertools-maintainers@amazon.com> + +## Security disclosures + +If you think you’ve found a potential security issue, please do not post it in the Issues. Instead, please follow the instructions [here](https://aws.amazon.com/security/vulnerability-reporting/) or [email AWS security directly](mailto:aws-security@amazon.com). ## License diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..885ffb18f2c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +<!-- markdownlint-disable MD043 --> + +## Reporting a vulnerability + +If you discover a potential security issue in this project, we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to <aws-security@amazon.com>. + +Please do **not** create a public GitHub issue. diff --git a/THIRD-PARTY-LICENSES b/THIRD-PARTY-LICENSES index 3712eac88cf..f6d647c54a7 100644 --- a/THIRD-PARTY-LICENSES +++ b/THIRD-PARTY-LICENSES @@ -1,3 +1,27 @@ +** FastAPI - https://github.com/tiangolo/fastapi/ - Used in the OpenAPI feature + + The MIT License (MIT) + + Copyright (c) 2018 Sebastián Ramírez + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ** Tensorflow - https://github.com/tensorflow/tensorflow/ Apache License diff --git a/aws_lambda_powertools/__init__.py b/aws_lambda_powertools/__init__.py index 65b5eb86730..dbec3d35635 100644 --- a/aws_lambda_powertools/__init__.py +++ b/aws_lambda_powertools/__init__.py @@ -1,13 +1,25 @@ -# -*- coding: utf-8 -*- - """Top-level package for Lambda Python Powertools.""" +from pathlib import Path -from .logging import Logger # noqa: F401 -from .metrics import Metrics, single_metric # noqa: F401 -from .package_logger import set_package_logger_handler -from .tracing import Tracer # noqa: F401 +from aws_lambda_powertools.logging import Logger +from aws_lambda_powertools.metrics import Metrics, single_metric +from aws_lambda_powertools.package_logger import set_package_logger_handler +from aws_lambda_powertools.shared.user_agent import inject_user_agent +from aws_lambda_powertools.shared.version import VERSION +from aws_lambda_powertools.tracing import Tracer +__version__ = VERSION __author__ = """Amazon Web Services""" +__all__ = [ + "Logger", + "Metrics", + "single_metric", + "Tracer", +] + +PACKAGE_PATH = Path(__file__).parent set_package_logger_handler() + +inject_user_agent() diff --git a/aws_lambda_powertools/event_handler/__init__.py b/aws_lambda_powertools/event_handler/__init__.py index def92f706f9..f374590428d 100644 --- a/aws_lambda_powertools/event_handler/__init__.py +++ b/aws_lambda_powertools/event_handler/__init__.py @@ -2,7 +2,40 @@ Event handler decorators for common Lambda events """ -from .api_gateway import ApiGatewayResolver -from .appsync import AppSyncResolver +from aws_lambda_powertools.event_handler.api_gateway import ( + ALBResolver, + APIGatewayHttpResolver, + ApiGatewayResolver, + APIGatewayRestResolver, + CORSConfig, + Response, +) +from aws_lambda_powertools.event_handler.appsync import AppSyncResolver +from aws_lambda_powertools.event_handler.bedrock_agent import BedrockAgentResolver, BedrockResponse +from aws_lambda_powertools.event_handler.bedrock_agent_function import ( + BedrockAgentFunctionResolver, + BedrockFunctionResponse, +) +from aws_lambda_powertools.event_handler.events_appsync.appsync_events import AppSyncEventsResolver +from aws_lambda_powertools.event_handler.lambda_function_url import ( + LambdaFunctionUrlResolver, +) +from aws_lambda_powertools.event_handler.vpc_lattice import VPCLatticeResolver, VPCLatticeV2Resolver -__all__ = ["AppSyncResolver", "ApiGatewayResolver"] +__all__ = [ + "AppSyncResolver", + "AppSyncEventsResolver", + "APIGatewayRestResolver", + "APIGatewayHttpResolver", + "ALBResolver", + "ApiGatewayResolver", + "BedrockAgentResolver", + "BedrockAgentFunctionResolver", + "BedrockResponse", + "BedrockFunctionResponse", + "CORSConfig", + "LambdaFunctionUrlResolver", + "Response", + "VPCLatticeResolver", + "VPCLatticeV2Resolver", +] diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py index 5017597c0f1..17b8da641d3 100644 --- a/aws_lambda_powertools/event_handler/api_gateway.py +++ b/aws_lambda_powertools/event_handler/api_gateway.py @@ -1,7 +1,8 @@ +from __future__ import annotations + import base64 import json import logging -import os import re import traceback import warnings @@ -10,24 +11,97 @@ from enum import Enum from functools import partial from http import HTTPStatus -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union +from pathlib import Path +from typing import TYPE_CHECKING, Any, Generic, Literal, Match, Pattern, TypeVar, cast + +from typing_extensions import override from aws_lambda_powertools.event_handler import content_types +from aws_lambda_powertools.event_handler.exception_handling import ExceptionHandlerManager from aws_lambda_powertools.event_handler.exceptions import NotFoundError, ServiceError -from aws_lambda_powertools.shared import constants -from aws_lambda_powertools.shared.functions import resolve_truthy_env_var_choice +from aws_lambda_powertools.event_handler.openapi.config import OpenAPIConfig +from aws_lambda_powertools.event_handler.openapi.constants import ( + DEFAULT_API_VERSION, + DEFAULT_OPENAPI_TITLE, + DEFAULT_OPENAPI_VERSION, +) +from aws_lambda_powertools.event_handler.openapi.exceptions import ( + RequestValidationError, + ResponseValidationError, + SchemaValidationError, +) +from aws_lambda_powertools.event_handler.openapi.types import ( + COMPONENT_REF_PREFIX, + METHODS_WITH_BODY, + OpenAPIResponse, + OpenAPIResponseContentModel, + OpenAPIResponseContentSchema, + response_validation_error_response_definition, + validation_error_definition, + validation_error_response_definition, +) +from aws_lambda_powertools.event_handler.util import ( + _FrozenDict, + _FrozenListDict, + _validate_openapi_security_parameters, + extract_origin_header, +) +from aws_lambda_powertools.shared.cookies import Cookie +from aws_lambda_powertools.shared.functions import powertools_dev_is_set from aws_lambda_powertools.shared.json_encoder import Encoder -from aws_lambda_powertools.utilities.data_classes import ALBEvent, APIGatewayProxyEvent, APIGatewayProxyEventV2 +from aws_lambda_powertools.utilities.data_classes import ( + ALBEvent, + APIGatewayProxyEvent, + APIGatewayProxyEventV2, + BedrockAgentEvent, + LambdaFunctionUrlEvent, + VPCLatticeEvent, + VPCLatticeEventV2, +) from aws_lambda_powertools.utilities.data_classes.common import BaseProxyEvent -from aws_lambda_powertools.utilities.typing import LambdaContext + +if TYPE_CHECKING: + from collections.abc import Callable, Mapping, Sequence logger = logging.getLogger(__name__) _DYNAMIC_ROUTE_PATTERN = r"(<\w+>)" -_SAFE_URI = "-._~()'!*:@,;" # https://www.ietf.org/rfc/rfc3986.txt +_SAFE_URI = "-._~()'!*:@,;=+&$" # https://www.ietf.org/rfc/rfc3986.txt # API GW/ALB decode non-safe URI chars; we must support them too -_UNSAFE_URI = "%<>\[\]{}|^" # noqa: W605 -_NAMED_GROUP_BOUNDARY_PATTERN = fr"(?P\1[{_SAFE_URI}{_UNSAFE_URI}\\w]+)" +_UNSAFE_URI = r"%<> \[\]{}|^" +_NAMED_GROUP_BOUNDARY_PATTERN = rf"(?P\1[{_SAFE_URI}{_UNSAFE_URI}\\w]+)" +_DEFAULT_OPENAPI_RESPONSE_DESCRIPTION = "Successful Response" +_ROUTE_REGEX = "^{}$" +_JSON_DUMP_CALL = partial(json.dumps, separators=(",", ":"), cls=Encoder) +_DEFAULT_CONTENT_TYPE = "application/json" + +ResponseEventT = TypeVar("ResponseEventT", bound=BaseProxyEvent) +ResponseT = TypeVar("ResponseT") + +if TYPE_CHECKING: + from aws_lambda_powertools.event_handler.openapi.compat import ( + JsonSchemaValue, + ModelField, + ) + from aws_lambda_powertools.event_handler.openapi.models import ( + Contact, + ExternalDocumentation, + License, + OpenAPI, + SecurityScheme, + Server, + Tag, + ) + from aws_lambda_powertools.event_handler.openapi.params import Dependant + from aws_lambda_powertools.event_handler.openapi.swagger_ui.oauth2 import ( + OAuth2Config, + ) + from aws_lambda_powertools.event_handler.openapi.types import ( + TypeModelOrEnum, + ) + from aws_lambda_powertools.shared.cookies import Cookie + from aws_lambda_powertools.shared.types import AnyCallableT + from aws_lambda_powertools.utilities.typing import LambdaContext class ProxyEventType(Enum): @@ -36,6 +110,10 @@ class ProxyEventType(Enum): APIGatewayProxyEvent = "APIGatewayProxyEvent" APIGatewayProxyEventV2 = "APIGatewayProxyEventV2" ALBEvent = "ALBEvent" + BedrockAgentEvent = "BedrockAgentEvent" + VPCLatticeEvent = "VPCLatticeEvent" + VPCLatticeEventV2 = "VPCLatticeEventV2" + LambdaFunctionUrlEvent = "LambdaFunctionUrlEvent" class CORSConfig: @@ -44,34 +122,37 @@ class CORSConfig: Examples -------- - Simple cors example using the default permissive cors, not this should only be used during early prototyping + Simple CORS example using the default permissive CORS, note that this should only be used during early prototyping. ```python - from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver + from aws_lambda_powertools.event_handler.api_gateway import ( + APIGatewayRestResolver, CORSConfig + ) - app = ApiGatewayResolver() + app = APIGatewayRestResolver(cors=CORSConfig()) - @app.get("/my/path", cors=True) + @app.get("/my/path") def with_cors(): return {"message": "Foo"} ``` Using a custom CORSConfig where `with_cors` used the custom provided CORSConfig and `without_cors` - do not include any cors headers. + do not include any CORS headers. ```python from aws_lambda_powertools.event_handler.api_gateway import ( - ApiGatewayResolver, CORSConfig + APIGatewayRestResolver, CORSConfig ) cors_config = CORSConfig( allow_origin="https://wwww.example.com/", + extra_origins=["https://dev.example.com/"], expose_headers=["x-exposed-response-header"], allow_headers=["x-custom-request-header"], max_age=100, allow_credentials=True, ) - app = ApiGatewayResolver(cors=cors_config) + app = APIGatewayRestResolver(cors=cors_config) @app.get("/my/path") def with_cors(): @@ -88,9 +169,10 @@ def without_cors(): def __init__( self, allow_origin: str = "*", - allow_headers: Optional[List[str]] = None, - expose_headers: Optional[List[str]] = None, - max_age: Optional[int] = None, + extra_origins: list[str] | None = None, + allow_headers: list[str] | None = None, + expose_headers: list[str] | None = None, + max_age: int | None = None, allow_credentials: bool = False, ): """ @@ -99,47 +181,122 @@ def __init__( allow_origin: str The value of the `Access-Control-Allow-Origin` to send in the response. Defaults to "*", but should only be used during development. - allow_headers: Optional[List[str]] + extra_origins: list[str] | None + The list of additional allowed origins. + allow_headers: list[str] | None The list of additional allowed headers. This list is added to list of built-in allowed headers: `Authorization`, `Content-Type`, `X-Amz-Date`, `X-Api-Key`, `X-Amz-Security-Token`. - expose_headers: Optional[List[str]] + expose_headers: list[str] | None A list of values to return for the Access-Control-Expose-Headers - max_age: Optional[int] + max_age: int | None The value for the `Access-Control-Max-Age` allow_credentials: bool A boolean value that sets the value of `Access-Control-Allow-Credentials` """ - self.allow_origin = allow_origin + + self._allowed_origins = [allow_origin] + + if extra_origins: + self._allowed_origins.extend(extra_origins) + self.allow_headers = set(self._REQUIRED_HEADERS + (allow_headers or [])) self.expose_headers = expose_headers or [] self.max_age = max_age self.allow_credentials = allow_credentials - def to_dict(self) -> Dict[str, str]: + def to_dict(self, origin: str | None) -> dict[str, str]: """Builds the configured Access-Control http headers""" + + # If there's no Origin, don't add any CORS headers + if not origin: + return {} + + # If the origin doesn't match any of the allowed origins, and we don't allow all origins ("*"), + # don't add any CORS headers + if origin not in self._allowed_origins and "*" not in self._allowed_origins: + return {} + + # The origin matched an allowed origin, so return the CORS headers headers = { - "Access-Control-Allow-Origin": self.allow_origin, - "Access-Control-Allow-Headers": ",".join(sorted(self.allow_headers)), + "Access-Control-Allow-Origin": origin, + "Access-Control-Allow-Headers": CORSConfig.build_allow_methods(self.allow_headers), } + if self.expose_headers: headers["Access-Control-Expose-Headers"] = ",".join(self.expose_headers) if self.max_age is not None: headers["Access-Control-Max-Age"] = str(self.max_age) - if self.allow_credentials is True: + if origin != "*" and self.allow_credentials is True: headers["Access-Control-Allow-Credentials"] = "true" return headers + def allowed_origin(self, extracted_origin: str) -> str | None: + if extracted_origin in self._allowed_origins: + return extracted_origin + if extracted_origin is not None and "*" in self._allowed_origins: + return "*" + + return None + + @staticmethod + def build_allow_methods(methods: set[str]) -> str: + """Build sorted comma delimited methods for Access-Control-Allow-Methods header + + Parameters + ---------- + methods : set[str] + Set of HTTP Methods + + Returns + ------- + set[str] + Formatted string with all HTTP Methods allowed for CORS e.g., `GET, OPTIONS` + + """ + return ",".join(sorted(methods)) + -class Response: +class BedrockResponse(Generic[ResponseT]): + """ + Contains the response body, status code, content type, and optional attributes + for session management and knowledge base configuration. + """ + + def __init__( + self, + body: Any = None, + status_code: int = 200, + content_type: str = _DEFAULT_CONTENT_TYPE, + session_attributes: dict[str, Any] | None = None, + prompt_session_attributes: dict[str, Any] | None = None, + knowledge_bases_configuration: list[dict[str, Any]] | None = None, + ) -> None: + self.body = body + self.status_code = status_code + self.content_type = content_type + self.session_attributes = session_attributes + self.prompt_session_attributes = prompt_session_attributes + self.knowledge_bases_configuration = knowledge_bases_configuration + + def is_json(self) -> bool: + """ + Returns True if the response is JSON, based on the Content-Type. + """ + return True + + +class Response(Generic[ResponseT]): """Response data class that provides greater control over what is returned from the proxy event""" def __init__( self, status_code: int, - content_type: Optional[str], - body: Union[str, bytes, None], - headers: Optional[Dict] = None, + content_type: str | None = None, + body: ResponseT | None = None, + headers: Mapping[str, str | list[str]] | None = None, + cookies: list[Cookie] | None = None, + compress: bool | None = None, ): """ @@ -150,47 +307,630 @@ def __init__( content_type: str Optionally set the Content-Type header, example "application/json". Note this will be merged into any provided http headers - body: Union[str, bytes, None] + body: str | bytes | None Optionally set the response body. Note: bytes body will be automatically base64 encoded - headers: dict - Optionally set specific http headers. Setting "Content-Type" hear would override the `content_type` value. + headers: Mapping[str, str | list[str]] + Optionally set specific http headers. Setting "Content-Type" here would override the `content_type` value. + cookies: list[Cookie] + Optionally set cookies. """ self.status_code = status_code self.body = body self.base64_encoded = False - self.headers: Dict = headers or {} + self.headers: dict[str, str | list[str]] = dict(headers) if headers else {} + self.cookies = cookies or [] + self.compress = compress + self.content_type = content_type if content_type: self.headers.setdefault("Content-Type", content_type) + def is_json(self) -> bool: + """ + Returns True if the response is JSON, based on the Content-Type. + """ + content_type = self.headers.get("Content-Type", "") + if isinstance(content_type, list): + content_type = content_type[0] + return content_type.startswith(_DEFAULT_CONTENT_TYPE) + class Route: """Internally used Route Configuration""" def __init__( - self, method: str, rule: Any, func: Callable, cors: bool, compress: bool, cache_control: Optional[str] + self, + method: str, + path: str, + rule: Pattern, + func: Callable, + cors: bool, + compress: bool, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str | None = None, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: HTTPStatus | None = None, + middlewares: list[Callable[..., Response]] | None = None, ): + """ + Internally used Route Configuration + + Parameters + ---------- + method: str + The HTTP method, example "GET" + path: str + The path of the route + rule: Pattern + The route rule, example "/my/path" + func: Callable + The route handler function + cors: bool + Whether or not to enable CORS for this route + compress: bool + Whether or not to enable gzip compression for this route + cache_control: str | None + The cache control header value, example "max-age=3600" + summary: str | None + The OpenAPI summary for this route + description: str | None + The OpenAPI description for this route + responses: dict[int, OpenAPIResponse] | None + The OpenAPI responses for this route + response_description: str | None + The OpenAPI response description for this route + tags: list[str] | None + The list of OpenAPI tags to be used for this route + operation_id: str | None + The OpenAPI operationId for this route + include_in_schema: bool + Whether or not to include this route in the OpenAPI schema + security: list[dict[str, list[str]]], optional + The OpenAPI security for this route + openapi_extensions: dict[str, Any], optional + Additional OpenAPI extensions as a dictionary. + deprecated: bool + Whether or not to mark this route as deprecated in the OpenAPI schema + custom_response_validation_http_code: int | HTTPStatus | None, optional + Whether to have custom http status code for this route if response validation fails + middlewares: list[Callable[..., Response]] | None + The list of route middlewares to be called in order. + """ self.method = method.upper() + self.path = path if path.strip() else "/" + + # OpenAPI spec only understands paths with { }. So we'll have to convert Powertools' < >. + # https://swagger.io/specification/#path-templating + self.openapi_path = re.sub(r"<(.*?)>", lambda m: f"{{{''.join(m.group(1))}}}", self.path) # type: ignore[arg-type] + self.rule = rule self.func = func + self._middleware_stack = func self.cors = cors self.compress = compress self.cache_control = cache_control + self.summary = summary + self.description = description + self.responses = responses + self.response_description = response_description + self.tags = tags or [] + self.include_in_schema = include_in_schema + self.security = security + self.openapi_extensions = openapi_extensions + self.middlewares = middlewares or [] + self.operation_id = operation_id or self._generate_operation_id() + self.deprecated = deprecated + + # _middleware_stack_built is used to ensure the middleware stack is only built once. + self._middleware_stack_built = False + + # _dependant is used to cache the dependant model for the handler function + self._dependant: Dependant | None = None + + # _body_field is used to cache the dependant model for the body field + self._body_field: ModelField | None = None + + self.custom_response_validation_http_code = custom_response_validation_http_code + + def __call__( + self, + router_middlewares: list[Callable], + app: ApiGatewayResolver, + route_arguments: dict[str, str], + ) -> dict | tuple | Response: + """Calling the Router class instance will trigger the following actions: + 1. If Route Middleware stack has not been built, build it + 2. Call the Route Middleware stack wrapping the original function + handler with the app and route arguments. + + Parameters + ---------- + router_middlewares: list[Callable] + The list of Router Middlewares (assigned to ALL routes) + app: "ApiGatewayResolver" + The ApiGatewayResolver instance to pass into the middleware stack + route_arguments: dict[str, str] + The route arguments to pass to the app function (extracted from the Api Gateway + Lambda Message structure from AWS) + + Returns + ------- + dict | tuple | Response + API Response object in ALL cases, except when the original API route + handler is called which may also return a dict, tuple, or Response. + """ + + # Save CPU cycles by building middleware stack once + if not self._middleware_stack_built: + self._build_middleware_stack(router_middlewares=router_middlewares) + # If debug is turned on then output the middleware stack to the console + if app._debug: + print(f"\nProcessing Route:::{self.func.__name__} ({app.context['_path']})") + # Collect ALL middleware for debug printing - include internal _registered_api_adapter + all_middlewares = router_middlewares + self.middlewares + [_registered_api_adapter] + print("\nMiddleware Stack:") + print("=================") + print("\n".join(getattr(item, "__name__", "Unknown") for item in all_middlewares)) + print("=================") + + # Add Route Arguments to app context + app.append_context(_route_args=route_arguments) + + # Call the Middleware Wrapped _call_stack function handler with the app + return self._middleware_stack(app) + + def _build_middleware_stack(self, router_middlewares: list[Callable[..., Any]]) -> None: + """ + Builds the middleware stack for the handler by wrapping each + handler in an instance of MiddlewareWrapper which is used to contain the state + of each middleware step. + + Middleware is represented by a standard Python Callable construct. Any Middleware + handler wanting to short-circuit the middlware call chain can raise an exception + to force the Python call stack created by the handler call-chain to naturally un-wind. + + This becomes a simple concept for developers to understand and reason with - no additional + gymanstics other than plain old try ... except. + + Notes + ----- + The Route Middleware stack is processed in reverse order. This is so the stack of + middleware handlers is applied in the order of being added to the handler. + """ + all_middlewares = router_middlewares + self.middlewares + logger.debug(f"Building middleware stack: {all_middlewares}") + + # IMPORTANT: + # this must be the last middleware in the stack (tech debt for backward + # compatibility purposes) + # + # This adapter will: + # 1. Call the registered API passing only the expected route arguments extracted from the path + # and not the middleware. + # 2. Adapt the response type of the route handler (dict | tuple | Response) + # and normalise into a Response object so middleware will always have a constant signature + all_middlewares.append(_registered_api_adapter) + + # Wrap the original route handler function in the middleware handlers + # using the MiddlewareWrapper class callable construct in reverse order to + # ensure middleware is applied in the order the user defined. + # + # Start with the route function and wrap from last to the first Middleware handler. + for handler in reversed(all_middlewares): + self._middleware_stack = MiddlewareFrame(current_middleware=handler, next_middleware=self._middleware_stack) + + self._middleware_stack_built = True + + @property + def dependant(self) -> Dependant: + if self._dependant is None: + from aws_lambda_powertools.event_handler.openapi.dependant import get_dependant + + self._dependant = get_dependant(path=self.openapi_path, call=self.func, responses=self.responses) + + return self._dependant + + @property + def body_field(self) -> ModelField | None: + if self._body_field is None: + from aws_lambda_powertools.event_handler.openapi.dependant import get_body_field + + self._body_field = get_body_field(dependant=self.dependant, name=self.operation_id) + + return self._body_field + + def _get_openapi_path( # noqa PLR0912 + self, + *, + dependant: Dependant, + operation_ids: set[str], + model_name_map: dict[TypeModelOrEnum, str], + field_mapping: dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + enable_validation: bool = False, + ) -> tuple[dict[str, Any], dict[str, Any]]: + """ + Returns the OpenAPI path and definitions for the route. + """ + from aws_lambda_powertools.event_handler.openapi.dependant import get_flat_params + + definitions: dict[str, Any] = {} + + # Gather all the route parameters + operation = self._openapi_operation_metadata(operation_ids=operation_ids) + parameters: list[dict[str, Any]] = [] + all_route_params = get_flat_params(dependant) + operation_params = self._openapi_operation_parameters( + all_route_params=all_route_params, + model_name_map=model_name_map, + field_mapping=field_mapping, + ) + parameters.extend(operation_params) + + # Add security if present + if self.security: + operation["security"] = self.security + + # Add OpenAPI extensions if present + if self.openapi_extensions: + operation.update(self.openapi_extensions) + + # Add the parameters to the OpenAPI operation + if parameters: + all_parameters = {(param["in"], param["name"]): param for param in parameters} + required_parameters = {(param["in"], param["name"]): param for param in parameters if param.get("required")} + all_parameters.update(required_parameters) + operation["parameters"] = list(all_parameters.values()) + + # Add the request body to the OpenAPI operation, if applicable + if self.method.upper() in METHODS_WITH_BODY: + request_body_oai = self._openapi_operation_request_body( + body_field=self.body_field, + model_name_map=model_name_map, + field_mapping=field_mapping, + ) + if request_body_oai: + operation["requestBody"] = request_body_oai + + operation_responses: dict[int, OpenAPIResponse] = {} + + if enable_validation: + # Validation failure response (422) is added only if Enable Validation feature is true + operation_responses = { + 422: { + "description": "Validation Error", + "content": { + _DEFAULT_CONTENT_TYPE: {"schema": {"$ref": f"{COMPONENT_REF_PREFIX}HTTPValidationError"}}, + }, + }, + } + + # Add custom response validation response, if exists + if self.custom_response_validation_http_code: + http_code = self.custom_response_validation_http_code.value + operation_responses[http_code] = { + "description": "Response Validation Error", + "content": { + _DEFAULT_CONTENT_TYPE: {"schema": {"$ref": f"{COMPONENT_REF_PREFIX}ResponseValidationError"}}, + }, + } + # Add model definition + definitions["ResponseValidationError"] = response_validation_error_response_definition + + # Add the response to the OpenAPI operation + if self.responses: + for status_code in list(self.responses): + response = self.responses[status_code] + + # Case 1: there is not 'content' key + if "content" not in response: + response["content"] = { + _DEFAULT_CONTENT_TYPE: self._openapi_operation_return( + param=dependant.return_param, + model_name_map=model_name_map, + field_mapping=field_mapping, + ), + } + + # Case 2: there is a 'content' key + else: + # Need to iterate to transform any 'model' into a 'schema' + for content_type, payload in response["content"].items(): + new_payload: OpenAPIResponseContentSchema + + # Case 2.1: the 'content' has a model + if "model" in payload: + # Find the model in the dependant's extra models + return_field = next( + filter( + lambda model: model.type_ is cast(OpenAPIResponseContentModel, payload)["model"], + self.dependant.response_extra_models, + ), + ) + if not return_field: + raise AssertionError("Model declared in custom responses was not found") + + new_payload = self._openapi_operation_return( + param=return_field, + model_name_map=model_name_map, + field_mapping=field_mapping, + ) + + # Case 2.2: the 'content' has a schema + else: + # Do nothing! We already have what we need! + new_payload = payload + + response["content"][content_type] = new_payload + + # Merge the user provided response with the default responses + operation_responses[status_code] = response + else: + # Set the default 200 response + response_schema = self._openapi_operation_return( + param=dependant.return_param, + model_name_map=model_name_map, + field_mapping=field_mapping, + ) + + # Add the response schema to the OpenAPI 200 response + operation_responses[200] = { + "description": self.response_description or _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + "content": {_DEFAULT_CONTENT_TYPE: response_schema}, + } + + operation["responses"] = operation_responses + path = {self.method.lower(): operation} + # Add the validation error schema to the definitions, but only if it hasn't been added yet + if "ValidationError" not in definitions: + definitions.update( + { + "ValidationError": validation_error_definition, + "HTTPValidationError": validation_error_response_definition, + }, + ) + + # Generate the response schema + return path, definitions + + def _openapi_operation_summary(self) -> str: + """ + Returns the OpenAPI operation summary. If the user has not provided a summary, we + generate one based on the route path and method. + """ + return self.summary or f"{self.method.upper()} {self.openapi_path}" + + def _openapi_operation_metadata(self, operation_ids: set[str]) -> dict[str, Any]: + """ + Returns the OpenAPI operation metadata. If the user has not provided a description, we + generate one based on the route path and method. + """ + operation: dict[str, Any] = {} + + # Ensure tags is added to the operation + if self.tags: + operation["tags"] = self.tags + + # Ensure summary is added to the operation + operation["summary"] = self._openapi_operation_summary() + + # Ensure description is added to the operation + if self.description: + operation["description"] = self.description + + # Ensure operationId is unique + if self.operation_id in operation_ids: + message = f"Duplicate Operation ID {self.operation_id} for function {self.func.__name__}" + file_name = getattr(self.func, "__globals__", {}).get("__file__") + if file_name: + message += f" in {file_name}" + warnings.warn(message, stacklevel=1) + + # Adds the operation + operation_ids.add(self.operation_id) + operation["operationId"] = self.operation_id + + # Mark as deprecated if necessary + operation["deprecated"] = self.deprecated or None + + return operation + + @staticmethod + def _openapi_operation_request_body( + *, + body_field: ModelField | None, + model_name_map: dict[TypeModelOrEnum, str], + field_mapping: dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + ) -> dict[str, Any] | None: + """ + Returns the OpenAPI operation request body. + """ + from aws_lambda_powertools.event_handler.openapi.compat import ModelField, get_schema_from_model_field + from aws_lambda_powertools.event_handler.openapi.params import Body + + # Check that there is a body field and it's a Pydantic's model field + if not body_field: + return None + + if not isinstance(body_field, ModelField): + raise AssertionError(f"Expected ModelField, got {body_field}") + + # Generate the request body schema + body_schema = get_schema_from_model_field( + field=body_field, + model_name_map=model_name_map, + field_mapping=field_mapping, + ) + + field_info = cast(Body, body_field.field_info) + request_media_type = field_info.media_type + required = body_field.required + request_body_oai: dict[str, Any] = {} + if required: + request_body_oai["required"] = required + + if field_info.description: + request_body_oai["description"] = field_info.description + + # Generate the request body media type + request_media_content: dict[str, Any] = {"schema": body_schema} + request_body_oai["content"] = {request_media_type: request_media_content} + return request_body_oai + + @staticmethod + def _openapi_operation_parameters( + *, + all_route_params: Sequence[ModelField], + model_name_map: dict[TypeModelOrEnum, str], + field_mapping: dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + ) -> list[dict[str, Any]]: + """ + Returns the OpenAPI operation parameters. + """ + from aws_lambda_powertools.event_handler.openapi.compat import ( + get_schema_from_model_field, + ) + from aws_lambda_powertools.event_handler.openapi.params import Param + + parameters = [] + parameter: dict[str, Any] = {} + + for param in all_route_params: + field_info = param.field_info + field_info = cast(Param, field_info) + if not field_info.include_in_schema: + continue + + param_schema = get_schema_from_model_field( + field=param, + model_name_map=model_name_map, + field_mapping=field_mapping, + ) -class ResponseBuilder: + parameter = { + "name": param.alias, + "in": field_info.in_.value, + "required": param.required, + "schema": param_schema, + } + + if field_info.description: + parameter["description"] = field_info.description + + if field_info.openapi_examples: + parameter["examples"] = field_info.openapi_examples + + if field_info.deprecated: + parameter["deprecated"] = field_info.deprecated + + parameters.append(parameter) + + return parameters + + @staticmethod + def _openapi_operation_return( + *, + param: ModelField | None, + model_name_map: dict[TypeModelOrEnum, str], + field_mapping: dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + ) -> OpenAPIResponseContentSchema: + """ + Returns the OpenAPI operation return. + """ + if param is None: + return {} + + from aws_lambda_powertools.event_handler.openapi.compat import ( + get_schema_from_model_field, + ) + + return_schema = get_schema_from_model_field( + field=param, + model_name_map=model_name_map, + field_mapping=field_mapping, + ) + + return {"schema": return_schema} + + def _generate_operation_id(self) -> str: + operation_id = self.func.__name__ + self.openapi_path + operation_id = re.sub(r"\W", "_", operation_id) + operation_id = operation_id + "_" + self.method.lower() + return operation_id + + +class ResponseBuilder(Generic[ResponseEventT]): """Internally used Response builder""" - def __init__(self, response: Response, route: Optional[Route] = None): + def __init__( + self, + response: Response, + serializer: Callable[[Any], str] = _JSON_DUMP_CALL, + route: Route | None = None, + ): self.response = response + self.serializer = serializer self.route = route - def _add_cors(self, cors: CORSConfig): + def _add_cors(self, event: ResponseEventT, cors: CORSConfig): """Update headers to include the configured Access-Control headers""" - self.response.headers.update(cors.to_dict()) + extracted_origin_header = extract_origin_header(event.resolved_headers_field) + + origin = cors.allowed_origin(extracted_origin_header) + if origin is not None: + self.response.headers.update(cors.to_dict(origin)) def _add_cache_control(self, cache_control: str): """Set the specified cache control headers for 200 http responses. For non-200 `no-cache` is used.""" - self.response.headers["Cache-Control"] = cache_control if self.response.status_code == 200 else "no-cache" + cache_control = cache_control if self.response.status_code == 200 else "no-cache" + self.response.headers["Cache-Control"] = cache_control + + @staticmethod + def _has_compression_enabled( + route_compression: bool, + response_compression: bool | None, + event: ResponseEventT, + ) -> bool: + """ + Checks if compression is enabled. + + NOTE: Response compression takes precedence. + + Parameters + ---------- + route_compression: bool, optional + A boolean indicating whether compression is enabled or not in the route setting. + response_compression: bool, optional + A boolean indicating whether compression is enabled or not in the response setting. + event: ResponseEventT + The event object containing the request details. + + Returns + ------- + bool + True if compression is enabled and the "gzip" encoding is accepted, False otherwise. + """ + encoding = event.resolved_headers_field.get("accept-encoding", "") + gzip_accepted = False + if isinstance(encoding, str): + gzip_accepted = "gzip" in encoding + elif isinstance(encoding, list): + gzip_accepted = "gzip" in ",".join(encoding) + + if gzip_accepted: + if response_compression is not None: + return response_compression # e.g., Response(compress=False/True)) + if route_compression: + return True # e.g., @app.get(compress=True) + + return False def _compress(self): """Compress the response body, but only if `Accept-Encoding` headers includes gzip.""" @@ -201,49 +941,136 @@ def _compress(self): gzip = zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS | 16) self.response.body = gzip.compress(self.response.body) + gzip.flush() - def _route(self, event: BaseProxyEvent, cors: Optional[CORSConfig]): + def _route(self, event: ResponseEventT, cors: CORSConfig | None): """Optionally handle any of the route's configure response handling""" if self.route is None: return if self.route.cors: - self._add_cors(cors or CORSConfig()) + self._add_cors(event, cors or CORSConfig()) if self.route.cache_control: self._add_cache_control(self.route.cache_control) - if self.route.compress and "gzip" in (event.get_header_value("accept-encoding", "") or ""): + if self._has_compression_enabled( + route_compression=self.route.compress, + response_compression=self.response.compress, + event=event, + ): self._compress() - def build(self, event: BaseProxyEvent, cors: Optional[CORSConfig] = None) -> Dict[str, Any]: + def build(self, event: ResponseEventT, cors: CORSConfig | None = None) -> dict[str, Any]: """Build the full response dict to be returned by the lambda""" + + # We only apply the serializer when the content type is JSON and the + # body is not a str, to avoid double encoding + if self.response.is_json() and not isinstance(self.response.body, str): + self.response.body = self.serializer(self.response.body) + self._route(event, cors) if isinstance(self.response.body, bytes): logger.debug("Encoding bytes response with base64") self.response.base64_encoded = True self.response.body = base64.b64encode(self.response.body).decode() + return { "statusCode": self.response.status_code, - "headers": self.response.headers, "body": self.response.body, "isBase64Encoded": self.response.base64_encoded, + **event.header_serializer().serialize(headers=self.response.headers, cookies=self.response.cookies), } class BaseRouter(ABC): + """Base class for Routing""" + current_event: BaseProxyEvent lambda_context: LambdaContext + context: dict + _router_middlewares: list[Callable] = [] + processed_stack_frames: list[str] = [] @abstractmethod def route( self, rule: str, method: Any, - cors: Optional[bool] = None, + cors: bool | None = None, compress: bool = False, - cache_control: Optional[str] = None, - ): + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: raise NotImplementedError() - def get(self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None): + def use(self, middlewares: list[Callable[..., Response]]) -> None: + """ + Add one or more global middlewares that run before/after route specific middleware. + + NOTE: Middlewares are called in insertion order. + + Parameters + ---------- + middlewares: list[Callable[..., Response]] + List of global middlewares to be used + + Examples + -------- + + Add middlewares to be used for every request processed by the Router. + + ```python + from aws_lambda_powertools import Logger + from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response + from aws_lambda_powertools.event_handler.middlewares import NextMiddleware + + logger = Logger() + app = APIGatewayRestResolver() + + def log_request_response(app: APIGatewayRestResolver, next_middleware: NextMiddleware) -> Response: + logger.info("Incoming request", path=app.current_event.path, request=app.current_event.raw_event) + + result = next_middleware(app) + logger.info("Response received", response=result.__dict__) + + return result + + app.use(middlewares=[log_request_response]) + + + def lambda_handler(event, context): + return app.resolve(event, context) + ``` + """ + self._router_middlewares = self._router_middlewares + middlewares + + def get( + self, + rule: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: """Get route decorator with GET `method` Examples @@ -252,10 +1079,10 @@ def get(self, rule: str, cors: Optional[bool] = None, compress: bool = False, ca ```python from aws_lambda_powertools import Tracer - from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver + from aws_lambda_powertools.event_handler import APIGatewayRestResolver tracer = Tracer() - app = ApiGatewayResolver() + app = APIGatewayRestResolver() @app.get("/get-call") def simple_get(): @@ -266,9 +1093,45 @@ def lambda_handler(event, context): return app.resolve(event, context) ``` """ - return self.route(rule, "GET", cors, compress, cache_control) + return self.route( + rule, + "GET", + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) - def post(self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None): + def post( + self, + rule: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: """Post route decorator with POST `method` Examples @@ -277,10 +1140,10 @@ def post(self, rule: str, cors: Optional[bool] = None, compress: bool = False, c ```python from aws_lambda_powertools import Tracer - from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver + from aws_lambda_powertools.event_handler import APIGatewayRestResolver tracer = Tracer() - app = ApiGatewayResolver() + app = APIGatewayRestResolver() @app.post("/post-call") def simple_post(): @@ -292,9 +1155,45 @@ def lambda_handler(event, context): return app.resolve(event, context) ``` """ - return self.route(rule, "POST", cors, compress, cache_control) + return self.route( + rule, + "POST", + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) - def put(self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None): + def put( + self, + rule: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: """Put route decorator with PUT `method` Examples @@ -303,10 +1202,10 @@ def put(self, rule: str, cors: Optional[bool] = None, compress: bool = False, ca ```python from aws_lambda_powertools import Tracer - from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver + from aws_lambda_powertools.event_handler import APIGatewayRestResolver tracer = Tracer() - app = ApiGatewayResolver() + app = APIGatewayRestResolver() @app.put("/put-call") def simple_put(): @@ -318,11 +1217,45 @@ def lambda_handler(event, context): return app.resolve(event, context) ``` """ - return self.route(rule, "PUT", cors, compress, cache_control) + return self.route( + rule, + "PUT", + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) def delete( - self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None - ): + self, + rule: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: """Delete route decorator with DELETE `method` Examples @@ -331,10 +1264,10 @@ def delete( ```python from aws_lambda_powertools import Tracer - from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver + from aws_lambda_powertools.event_handler import APIGatewayRestResolver tracer = Tracer() - app = ApiGatewayResolver() + app = APIGatewayRestResolver() @app.delete("/delete-call") def simple_delete(): @@ -345,11 +1278,45 @@ def lambda_handler(event, context): return app.resolve(event, context) ``` """ - return self.route(rule, "DELETE", cors, compress, cache_control) + return self.route( + rule, + "DELETE", + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) def patch( - self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None - ): + self, + rule: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: """Patch route decorator with PATCH `method` Examples @@ -358,10 +1325,10 @@ def patch( ```python from aws_lambda_powertools import Tracer - from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver + from aws_lambda_powertools.event_handler import APIGatewayRestResolver tracer = Tracer() - app = ApiGatewayResolver() + app = APIGatewayRestResolver() @app.patch("/patch-call") def simple_patch(): @@ -375,11 +1342,214 @@ def lambda_handler(event, context): return app.resolve(event, context) ``` """ - return self.route(rule, "PATCH", cors, compress, cache_control) + return self.route( + rule, + "PATCH", + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + def head( + self, + rule: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: + """Head route decorator with HEAD `method` + + Examples + -------- + Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator + + ```python + from aws_lambda_powertools import Tracer + from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response, content_types + + tracer = Tracer() + app = APIGatewayRestResolver() + + @app.head("/head-call") + def simple_head(): + return Response(status_code=200, + content_type=content_types.APPLICATION_JSON, + headers={"Content-Length": "123"}) + + @tracer.capture_lambda_handler + def lambda_handler(event, context): + return app.resolve(event, context) + ``` + """ + return self.route( + rule, + "HEAD", + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + def _push_processed_stack_frame(self, frame: str): + """ + Add Current Middleware to the Middleware Stack Frames + The stack frames will be used when exceptions are thrown and Powertools + debug is enabled by developers. + """ + self.processed_stack_frames.append(frame) + + def _reset_processed_stack(self): + """Reset the Processed Stack Frames""" + self.processed_stack_frames.clear() + + def append_context(self, **additional_context): + """Append key=value data as routing context""" + self.context.update(**additional_context) + + def clear_context(self): + """Resets routing context""" + self.context.clear() + + +class MiddlewareFrame: + """ + Creates a Middle Stack Wrapper instance to be used as a "Frame" in the overall stack of + middleware functions. Each instance contains the current middleware and the next + middleware function to be called in the stack. + + In this way the middleware stack is constructed in a recursive fashion, with each middleware + calling the next as a simple function call. The actual Python call-stack will contain + each MiddlewareStackWrapper "Frame", meaning any Middleware function can cause the + entire Middleware call chain to be exited early (short-circuited) by raising an exception + or by simply returning early with a custom Response. The decision to short-circuit the middleware + chain is at the user's discretion but instantly available due to the Wrapped nature of the + callable constructs in the Middleware stack and each Middleware function having complete control over + whether the "Next" handler in the stack is called or not. + + Parameters + ---------- + current_middleware : Callable + The current middleware function to be called as a request is processed. + next_middleware : Callable + The next middleware in the middleware stack. + """ + + def __init__( + self, + current_middleware: Callable[..., Any], + next_middleware: Callable[..., Any], + ) -> None: + self.current_middleware: Callable[..., Any] = current_middleware + self.next_middleware: Callable[..., Any] = next_middleware + self._next_middleware_name = next_middleware.__name__ + + @property + def __name__(self) -> str: # noqa: A003 + """Current middleware name + + It ensures backward compatibility with view functions being callable. This + improves debugging since we need both current and next middlewares/callable names. + """ + return self.current_middleware.__name__ + + def __str__(self) -> str: + """Identify current middleware identity and call chain for debugging purposes.""" + middleware_name = self.__name__ + return f"[{middleware_name}] next call chain is {middleware_name} -> {self._next_middleware_name}" + + def __call__(self, app: ApiGatewayResolver) -> dict | tuple | Response: + """ + Call the middleware Frame to process the request. + + Parameters + ---------- + app: BaseRouter + The router instance + + Returns + ------- + dict | tuple | Response + (tech-debt for backward compatibility). The response type should be a + Response object in all cases excepting when the original API route handler + is called which will return one of 3 outputs. + + """ + # Do debug printing and push processed stack frame AFTER calling middleware + # else the stack frame text of `current calling next` is confusing. + logger.debug("MiddlewareFrame: %s", self) + app._push_processed_stack_frame(str(self)) + + return self.current_middleware(app, self.next_middleware) + + +def _registered_api_adapter( + app: ApiGatewayResolver, + next_middleware: Callable[..., Any], +) -> dict | tuple | Response | BedrockResponse: + """ + Calls the registered API using the "_route_args" from the Resolver context to ensure the last call + in the chain will match the API route function signature and ensure that Powertools passes the API + route handler the expected arguments. + + **IMPORTANT: This internal middleware ensures the actual API route is called with the correct call signature + and it MUST be the final frame in the middleware stack. This can only be removed when the API Route + function accepts `app: BaseRouter` as the first argument - which is the breaking change. + + Parameters + ---------- + app: ApiGatewayResolver + The API Gateway resolver + next_middleware: Callable[..., Any] + The function to handle the API + + Returns + ------- + Response + The API Response Object + + """ + route_args: dict = app.context.get("_route_args", {}) + logger.debug(f"Calling API Route Handler: {route_args}") + return app._to_response(next_middleware(**route_args)) class ApiGatewayResolver(BaseRouter): - """API Gateway and ALB proxy resolver + """API Gateway, VPC Laticce, Bedrock and ALB proxy resolver Examples -------- @@ -387,10 +1557,10 @@ class ApiGatewayResolver(BaseRouter): ```python from aws_lambda_powertools import Tracer - from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver + from aws_lambda_powertools.event_handler import APIGatewayRestResolver tracer = Tracer() - app = ApiGatewayResolver() + app = APIGatewayRestResolver() @app.get("/get-call") def simple_get(): @@ -410,10 +1580,13 @@ def lambda_handler(event, context): def __init__( self, proxy_type: Enum = ProxyEventType.APIGatewayProxyEvent, - cors: Optional[CORSConfig] = None, - debug: Optional[bool] = None, - serializer: Optional[Callable[[Dict], str]] = None, - strip_prefixes: Optional[List[str]] = None, + cors: CORSConfig | None = None, + debug: bool | None = None, + serializer: Callable[[dict], str] | None = None, + strip_prefixes: list[str | Pattern] | None = None, + enable_validation: bool = False, + response_validation_error_http_code: HTTPStatus | int | None = None, + json_body_deserializer: Callable[[str], dict] | None = None, ): """ Parameters @@ -422,71 +1595,785 @@ def __init__( Proxy request type, defaults to API Gateway V1 cors: CORSConfig Optionally configure and enabled CORS. Not each route will need to have to cors=True - debug: Optional[bool] - Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_EVENT_HANDLER_DEBUG" + debug: bool | None + Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_DEV" environment variable - serializer : Callable, optional + serializer: Callable, optional function to serialize `obj` to a JSON formatted `str`, by default json.dumps - strip_prefixes: List[str], optional - optional list of prefixes to be removed from the request path before doing the routing. This is often used - with api gateways with multiple custom mappings. + strip_prefixes: list[str | Pattern], optional + optional list of prefixes to be removed from the request path before doing the routing. + This is often used with api gateways with multiple custom mappings. + Each prefix can be a static string or a compiled regex pattern + enable_validation: bool | None + Enables validation of the request body against the route schema, by default False. + response_validation_error_http_code + Sets the returned status code if response is not validated. enable_validation must be True. + json_body_deserializer: Callable[[str], dict], optional + function to deserialize `str`, `bytes`, `bytearray` containing a JSON document to a Python `dict`, + by default json.loads when integrating with EventSource data class """ self._proxy_type = proxy_type - self._routes: List[Route] = [] - self._route_keys: List[str] = [] - self._exception_handlers: Dict[Type, Callable] = {} + self._dynamic_routes: list[Route] = [] + self._static_routes: list[Route] = [] + self._route_keys: list[str] = [] + self._exception_handlers: dict[type, Callable] = {} self._cors = cors self._cors_enabled: bool = cors is not None - self._cors_methods: Set[str] = {"OPTIONS"} - self._debug = resolve_truthy_env_var_choice( - env=os.getenv(constants.EVENT_HANDLER_DEBUG_ENV, "false"), choice=debug - ) + self._cors_methods: set[str] = {"OPTIONS"} + self._debug = self._has_debug(debug) + self._enable_validation = enable_validation self._strip_prefixes = strip_prefixes + self.context: dict = {} # early init as customers might add context before event resolution + self.processed_stack_frames = [] + self._response_builder_class = ResponseBuilder[BaseProxyEvent] + self.openapi_config = OpenAPIConfig() # starting an empty dataclass + self.exception_handler_manager = ExceptionHandlerManager() + self._has_response_validation_error = response_validation_error_http_code is not None + self._response_validation_error_http_code = self._validate_response_validation_error_http_code( + response_validation_error_http_code, + enable_validation, + ) # Allow for a custom serializer or a concise json serialization self._serializer = serializer or partial(json.dumps, separators=(",", ":"), cls=Encoder) + self._json_body_deserializer = json_body_deserializer + + if self._enable_validation: + from aws_lambda_powertools.event_handler.middlewares.openapi_validation import OpenAPIValidationMiddleware + + # Note the serializer argument: only use custom serializer if provided by the caller + # Otherwise, fully rely on the internal Pydantic based mechanism to serialize responses for validation. + self.use( + [ + OpenAPIValidationMiddleware( + validation_serializer=serializer, + has_response_validation_error=self._has_response_validation_error, + ), + ], + ) - if self._debug: - # Always does a pretty print when in debug mode - self._serializer = partial(json.dumps, indent=4, cls=Encoder) + def _validate_response_validation_error_http_code( + self, + response_validation_error_http_code: HTTPStatus | int | None, + enable_validation: bool, + ) -> HTTPStatus: + if response_validation_error_http_code and not enable_validation: + msg = "'response_validation_error_http_code' cannot be set when enable_validation is False." + raise ValueError(msg) + + if ( + not isinstance(response_validation_error_http_code, HTTPStatus) + and response_validation_error_http_code is not None + ): + try: + response_validation_error_http_code = HTTPStatus(response_validation_error_http_code) + except ValueError: + msg = f"'{response_validation_error_http_code}' must be an integer representing an HTTP status code." + raise ValueError(msg) from None + + return response_validation_error_http_code or HTTPStatus.UNPROCESSABLE_ENTITY + + def _add_resolver_response_validation_error_response_to_route( + self, + route_openapi_path: tuple[dict[str, Any], dict[str, Any]], + ) -> tuple[dict[str, Any], dict[str, Any]]: + """Adds resolver response validation error response to route's operations.""" + path, path_definitions = route_openapi_path + if self._has_response_validation_error and "ResponseValidationError" not in path_definitions: + response_validation_error_response = { + "description": "Response Validation Error", + "content": { + _DEFAULT_CONTENT_TYPE: { + "schema": {"$ref": f"{COMPONENT_REF_PREFIX}ResponseValidationError"}, + }, + }, + } + http_code = self._response_validation_error_http_code.value + for operation in path.values(): + operation["responses"][http_code] = response_validation_error_response + return path, path_definitions + + def _generate_schemas(self, definitions: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]: + schemas = {k: definitions[k] for k in sorted(definitions)} + # add response validation error definition + if self._response_validation_error_http_code: + schemas.setdefault("ResponseValidationError", response_validation_error_response_definition) + return schemas + + def get_openapi_schema( + self, + *, + title: str = DEFAULT_OPENAPI_TITLE, + version: str = DEFAULT_API_VERSION, + openapi_version: str = DEFAULT_OPENAPI_VERSION, + summary: str | None = None, + description: str | None = None, + tags: list[Tag | str] | None = None, + servers: list[Server] | None = None, + terms_of_service: str | None = None, + contact: Contact | None = None, + license_info: License | None = None, + security_schemes: dict[str, SecurityScheme] | None = None, + security: list[dict[str, list[str]]] | None = None, + external_documentation: ExternalDocumentation | None = None, + openapi_extensions: dict[str, Any] | None = None, + ) -> OpenAPI: + """ + Returns the OpenAPI schema as a pydantic model. + + Parameters + ---------- + title: str + The title of the application. + version: str + The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API + openapi_version: str, default = "3.0.0" + The version of the OpenAPI Specification (which the document uses). + summary: str, optional + A short summary of what the application does. + description: str, optional + A verbose explanation of the application behavior. + tags: list[Tag | str], optional + A list of tags used by the specification with additional metadata. + servers: list[Server], optional + An array of Server Objects, which provide connectivity information to a target server. + terms_of_service: str, optional + A URL to the Terms of Service for the API. MUST be in the format of a URL. + contact: Contact, optional + The contact information for the exposed API. + license_info: License, optional + The license information for the exposed API. + security_schemes: dict[str, SecurityScheme]], optional + A declaration of the security schemes available to be used in the specification. + security: list[dict[str, list[str]]], optional + A declaration of which security mechanisms are applied globally across the API. + external_documentation: ExternalDocumentation, optional + Additional external documentation for the API. + openapi_extensions: Dict[str, Any], optional + Additional OpenAPI extensions as a dictionary. + + Returns + ------- + OpenAPI: pydantic model + The OpenAPI schema as a pydantic model. + """ + + # DEPRECATION: Will be removed in v4.0.0. Use configure_api() instead. + # Maintained for backwards compatibility. + # See: https://github.com/aws-powertools/powertools-lambda-python/issues/6122 + if title == DEFAULT_OPENAPI_TITLE and self.openapi_config.title: + title = self.openapi_config.title + + if version == DEFAULT_API_VERSION and self.openapi_config.version: + version = self.openapi_config.version + + if openapi_version == DEFAULT_OPENAPI_VERSION and self.openapi_config.openapi_version: + openapi_version = self.openapi_config.openapi_version + + summary = summary or self.openapi_config.summary + description = description or self.openapi_config.description + tags = tags or self.openapi_config.tags + servers = servers or self.openapi_config.servers + terms_of_service = terms_of_service or self.openapi_config.terms_of_service + contact = contact or self.openapi_config.contact + license_info = license_info or self.openapi_config.license_info + security_schemes = security_schemes or self.openapi_config.security_schemes + security = security or self.openapi_config.security + external_documentation = external_documentation or self.openapi_config.external_documentation + openapi_extensions = openapi_extensions or self.openapi_config.openapi_extensions + + from pydantic.json_schema import GenerateJsonSchema + + from aws_lambda_powertools.event_handler.openapi.compat import ( + get_compat_model_name_map, + get_definitions, + ) + from aws_lambda_powertools.event_handler.openapi.models import OpenAPI, PathItem, Tag + from aws_lambda_powertools.event_handler.openapi.types import ( + COMPONENT_REF_TEMPLATE, + ) + + openapi_version = self._determine_openapi_version(openapi_version) + + # Start with the bare minimum required for a valid OpenAPI schema + info: dict[str, Any] = {"title": title, "version": version} + + optional_fields = { + "summary": summary, + "description": description, + "termsOfService": terms_of_service, + "contact": contact, + "license": license_info, + } + + info.update({field: value for field, value in optional_fields.items() if value}) + + if not isinstance(openapi_extensions, dict): + openapi_extensions = {} + + output: dict[str, Any] = { + "openapi": openapi_version, + "info": info, + "servers": self._get_openapi_servers(servers), + "security": self._get_openapi_security(security, security_schemes), + **openapi_extensions, + } + + if external_documentation: + output["externalDocs"] = external_documentation + + components: dict[str, dict[str, Any]] = {} + paths: dict[str, dict[str, Any]] = {} + operation_ids: set[str] = set() + + all_routes = self._dynamic_routes + self._static_routes + all_fields = self._get_fields_from_routes(all_routes) + model_name_map = get_compat_model_name_map(all_fields) + + # Collect all models and definitions + schema_generator = GenerateJsonSchema(ref_template=COMPONENT_REF_TEMPLATE) + field_mapping, definitions = get_definitions( + fields=all_fields, + schema_generator=schema_generator, + model_name_map=model_name_map, + ) + + # Add routes to the OpenAPI schema + for route in all_routes: + if route.security and not _validate_openapi_security_parameters( + security=route.security, + security_schemes=security_schemes, + ): + raise SchemaValidationError( + "Security configuration was not found in security_schemas or security_schema was not defined. " + "See: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#security-schemes", + ) + + if not route.include_in_schema: + continue + + result = route._get_openapi_path( + dependant=route.dependant, + operation_ids=operation_ids, + model_name_map=model_name_map, + field_mapping=field_mapping, + enable_validation=self._enable_validation, + ) + if result: + path, path_definitions = self._add_resolver_response_validation_error_response_to_route(result) + if path: + paths.setdefault(route.openapi_path, {}).update(path) + if path_definitions: + definitions.update(path_definitions) + + if definitions: + components["schemas"] = self._generate_schemas(definitions) + if security_schemes: + components["securitySchemes"] = security_schemes + if components: + output["components"] = components + if tags: + output["tags"] = [Tag(name=tag) if isinstance(tag, str) else tag for tag in tags] + + output["paths"] = {k: PathItem(**v) for k, v in paths.items()} + + return OpenAPI(**output) + + @staticmethod + def _get_openapi_servers(servers: list[Server] | None) -> list[Server]: + from aws_lambda_powertools.event_handler.openapi.models import Server + + # If the 'servers' property is not provided or is an empty array, + # the default behavior is to return a Server Object with a URL value of "/". + return servers or [Server(url="/")] + + @staticmethod + def _get_openapi_security( + security: list[dict[str, list[str]]] | None, + security_schemes: dict[str, SecurityScheme] | None, + ) -> list[dict[str, list[str]]] | None: + if not security: + return None + + if not _validate_openapi_security_parameters(security=security, security_schemes=security_schemes): + raise SchemaValidationError( + "Security configuration was not found in security_schemas or security_schema was not defined. " + "See: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#security-schemes", + ) + + return security + + @staticmethod + def _determine_openapi_version(openapi_version: str): + # Pydantic V2 has no support for OpenAPI schema 3.0 + if not openapi_version.startswith("3.1"): + warnings.warn( + "You are using Pydantic v2, which is incompatible with OpenAPI schema 3.0. Forcing OpenAPI 3.1", + stacklevel=2, + ) + openapi_version = "3.1.0" + return openapi_version + + def get_openapi_json_schema( + self, + *, + title: str = DEFAULT_OPENAPI_TITLE, + version: str = DEFAULT_API_VERSION, + openapi_version: str = DEFAULT_OPENAPI_VERSION, + summary: str | None = None, + description: str | None = None, + tags: list[Tag | str] | None = None, + servers: list[Server] | None = None, + terms_of_service: str | None = None, + contact: Contact | None = None, + license_info: License | None = None, + security_schemes: dict[str, SecurityScheme] | None = None, + security: list[dict[str, list[str]]] | None = None, + external_documentation: ExternalDocumentation | None = None, + openapi_extensions: dict[str, Any] | None = None, + ) -> str: + """ + Returns the OpenAPI schema as a JSON serializable dict + + Parameters + ---------- + title: str + The title of the application. + version: str + The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API + openapi_version: str, default = "3.0.0" + The version of the OpenAPI Specification (which the document uses). + summary: str, optional + A short summary of what the application does. + description: str, optional + A verbose explanation of the application behavior. + tags: list[Tag, str], optional + A list of tags used by the specification with additional metadata. + servers: list[Server], optional + An array of Server Objects, which provide connectivity information to a target server. + terms_of_service: str, optional + A URL to the Terms of Service for the API. MUST be in the format of a URL. + contact: Contact, optional + The contact information for the exposed API. + license_info: License, optional + The license information for the exposed API. + security_schemes: dict[str, SecurityScheme]], optional + A declaration of the security schemes available to be used in the specification. + security: list[dict[str, list[str]]], optional + A declaration of which security mechanisms are applied globally across the API. + external_documentation: ExternalDocumentation, optional + Additional external documentation for the API. + openapi_extensions: Dict[str, Any], optional + Additional OpenAPI extensions as a dictionary. + + Returns + ------- + str + The OpenAPI schema as a JSON serializable dict. + """ + + from aws_lambda_powertools.event_handler.openapi.compat import model_json + + return model_json( + self.get_openapi_schema( + title=title, + version=version, + openapi_version=openapi_version, + summary=summary, + description=description, + tags=tags, + servers=servers, + terms_of_service=terms_of_service, + contact=contact, + license_info=license_info, + security_schemes=security_schemes, + security=security, + external_documentation=external_documentation, + openapi_extensions=openapi_extensions, + ), + by_alias=True, + exclude_none=True, + indent=2, + ) + + def configure_openapi( + self, + title: str = DEFAULT_OPENAPI_TITLE, + version: str = DEFAULT_API_VERSION, + openapi_version: str = DEFAULT_OPENAPI_VERSION, + summary: str | None = None, + description: str | None = None, + tags: list[Tag | str] | None = None, + servers: list[Server] | None = None, + terms_of_service: str | None = None, + contact: Contact | None = None, + license_info: License | None = None, + security_schemes: dict[str, SecurityScheme] | None = None, + security: list[dict[str, list[str]]] | None = None, + external_documentation: ExternalDocumentation | None = None, + openapi_extensions: dict[str, Any] | None = None, + ): + """Configure OpenAPI specification settings for the API. + + Sets up the OpenAPI documentation configuration that can be later used + when enabling Swagger UI or generating OpenAPI specifications. + + Parameters + ---------- + title: str + The title of the application. + version: str + The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API + openapi_version: str, default = "3.0.0" + The version of the OpenAPI Specification (which the document uses). + summary: str, optional + A short summary of what the application does. + description: str, optional + A verbose explanation of the application behavior. + tags: list[Tag, str], optional + A list of tags used by the specification with additional metadata. + servers: list[Server], optional + An array of Server Objects, which provide connectivity information to a target server. + terms_of_service: str, optional + A URL to the Terms of Service for the API. MUST be in the format of a URL. + contact: Contact, optional + The contact information for the exposed API. + license_info: License, optional + The license information for the exposed API. + security_schemes: dict[str, SecurityScheme]], optional + A declaration of the security schemes available to be used in the specification. + security: list[dict[str, list[str]]], optional + A declaration of which security mechanisms are applied globally across the API. + external_documentation: ExternalDocumentation, optional + A link to external documentation for the API. + openapi_extensions: Dict[str, Any], optional + Additional OpenAPI extensions as a dictionary. + + Example + -------- + >>> api.configure_openapi( + ... title="My API", + ... version="1.0.0", + ... description="API for managing resources", + ... contact=Contact( + ... name="API Support", + ... email="support@example.com" + ... ) + ... ) + + See Also + -------- + enable_swagger : Method to enable Swagger UI using these configurations + OpenAPIConfig : Data class containing all OpenAPI configuration options + """ + self.openapi_config = OpenAPIConfig( + title=title, + version=version, + openapi_version=openapi_version, + summary=summary, + description=description, + tags=tags, + servers=servers, + terms_of_service=terms_of_service, + contact=contact, + license_info=license_info, + security_schemes=security_schemes, + security=security, + external_documentation=external_documentation, + openapi_extensions=openapi_extensions, + ) + + def enable_swagger( + self, + *, + path: str = "/swagger", + title: str = DEFAULT_OPENAPI_TITLE, + version: str = DEFAULT_API_VERSION, + openapi_version: str = DEFAULT_OPENAPI_VERSION, + summary: str | None = None, + description: str | None = None, + tags: list[Tag | str] | None = None, + servers: list[Server] | None = None, + terms_of_service: str | None = None, + contact: Contact | None = None, + license_info: License | None = None, + swagger_base_url: str | None = None, + middlewares: list[Callable[..., Response]] | None = None, + compress: bool = False, + security_schemes: dict[str, SecurityScheme] | None = None, + security: list[dict[str, list[str]]] | None = None, + oauth2_config: OAuth2Config | None = None, + persist_authorization: bool = False, + external_documentation: ExternalDocumentation | None = None, + openapi_extensions: dict[str, Any] | None = None, + ): + """ + Returns the OpenAPI schema as a JSON serializable dict + + Parameters + ---------- + path: str, default = "/swagger" + The path to the swagger UI. + title: str + The title of the application. + version: str + The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API + openapi_version: str, default = "3.0.0" + The version of the OpenAPI Specification (which the document uses). + summary: str, optional + A short summary of what the application does. + description: str, optional + A verbose explanation of the application behavior. + tags: list[Tag, str], optional + A list of tags used by the specification with additional metadata. + servers: list[Server], optional + An array of Server Objects, which provide connectivity information to a target server. + terms_of_service: str, optional + A URL to the Terms of Service for the API. MUST be in the format of a URL. + contact: Contact, optional + The contact information for the exposed API. + license_info: License, optional + The license information for the exposed API. + swagger_base_url: str, optional + The base url for the swagger UI. If not provided, we will serve a recent version of the Swagger UI. + middlewares: list[Callable[..., Response]], optional + List of middlewares to be used for the swagger route. + compress: bool, default = False + Whether or not to enable gzip compression swagger route. + security_schemes: dict[str, "SecurityScheme"], optional + A declaration of the security schemes available to be used in the specification. + security: list[dict[str, list[str]]], optional + A declaration of which security mechanisms are applied globally across the API. + oauth2_config: OAuth2Config, optional + The OAuth2 configuration for the Swagger UI. + persist_authorization: bool, optional + Whether to persist authorization data on browser close/refresh. + external_documentation: ExternalDocumentation, optional + A link to external documentation for the API. + openapi_extensions: dict[str, Any], optional + Additional OpenAPI extensions as a dictionary. + """ + + from aws_lambda_powertools.event_handler.openapi.compat import model_json + from aws_lambda_powertools.event_handler.openapi.models import Server + from aws_lambda_powertools.event_handler.openapi.swagger_ui import ( + generate_oauth2_redirect_html, + generate_swagger_html, + ) + + @self.get(path, middlewares=middlewares, include_in_schema=False, compress=compress) + def swagger_handler(): + query_params = self.current_event.query_string_parameters or {} + + # Check for query parameters; if "format" is specified as "oauth2-redirect", + # send the oauth2-redirect HTML stanza so OAuth2 can be used + # Source: https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html + if query_params.get("format") == "oauth2-redirect": + return Response( + status_code=200, + content_type="text/html", + body=generate_oauth2_redirect_html(), + ) + + base_path = self._get_base_path() + + if swagger_base_url: + swagger_js = f"{swagger_base_url}/swagger-ui-bundle.min.js" + swagger_css = f"{swagger_base_url}/swagger-ui.min.css" + else: + # We now inject CSS and JS into the SwaggerUI file + swagger_js = Path.open( + Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui-bundle.min.js", + ).read() + swagger_css = Path.open(Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui.min.css").read() + + openapi_servers = servers or [Server(url=(base_path or "/"))] + + spec = self.get_openapi_schema( + title=title, + version=version, + openapi_version=openapi_version, + summary=summary, + description=description, + tags=tags, + servers=openapi_servers, + terms_of_service=terms_of_service, + contact=contact, + license_info=license_info, + security_schemes=security_schemes, + security=security, + external_documentation=external_documentation, + openapi_extensions=openapi_extensions, + ) + + # The .replace('</', '<\\/') part is necessary to prevent a potential issue where the JSON string contains + # </script> or similar tags. Escaping the forward slash in </ as <\/ ensures that the JSON does not + # inadvertently close the script tag, and the JSON remains a valid string within the JavaScript code. + escaped_spec = model_json( + spec, + by_alias=True, + exclude_none=True, + indent=2, + ).replace("</", "<\\/") + + # Check for query parameters; if "format" is specified as "json", + # respond with the JSON used in the OpenAPI spec + # Example: https://www.example.com/swagger?format=json + if query_params.get("format") == "json": + return Response( + status_code=200, + content_type=_DEFAULT_CONTENT_TYPE, + body=escaped_spec, + ) + + body = generate_swagger_html( + escaped_spec, + swagger_js, + swagger_css, + swagger_base_url, + oauth2_config, + persist_authorization, + ) + + return Response( + status_code=200, + content_type="text/html", + body=body, + ) + + def _validate_route_response_validation_error_http_code( + self, + custom_response_validation_http_code: int | HTTPStatus | None, + ) -> HTTPStatus | None: + if custom_response_validation_http_code and not self._enable_validation: + msg = ( + "'custom_response_validation_http_code' cannot be set for route when enable_validation is False " + "on resolver." + ) + raise ValueError(msg) + + if ( + not isinstance(custom_response_validation_http_code, HTTPStatus) + and custom_response_validation_http_code is not None + ): + try: + custom_response_validation_http_code = HTTPStatus(custom_response_validation_http_code) + except ValueError: + msg = f"'{custom_response_validation_http_code}' must be an integer representing an HTTP status code or an enum of type HTTPStatus." # noqa: E501 + raise ValueError(msg) from None + + return custom_response_validation_http_code def route( self, rule: str, - method: Union[str, Union[List[str], Tuple[str]]], - cors: Optional[bool] = None, + method: str | list[str] | tuple[str], + cors: bool | None = None, compress: bool = False, - cache_control: Optional[str] = None, - ): + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: """Route decorator includes parameter `method`""" - def register_resolver(func: Callable): + custom_response_validation_http_code = self._validate_route_response_validation_error_http_code( + custom_response_validation_http_code, + ) + + def register_resolver(func: AnyCallableT) -> AnyCallableT: methods = (method,) if isinstance(method, str) else method - logger.debug(f"Adding route using rule {rule} and methods: {','.join((m.upper() for m in methods))}") - if cors is None: - cors_enabled = self._cors_enabled - else: - cors_enabled = cors + logger.debug(f"Adding route using rule {rule} and methods: {','.join(m.upper() for m in methods)}") + + cors_enabled = self._cors_enabled if cors is None else cors for item in methods: - self._routes.append(Route(item, self._compile_regex(rule), func, cors_enabled, compress, cache_control)) - route_key = item + rule - if route_key in self._route_keys: - warnings.warn(f"A route like this was already registered. method: '{item}' rule: '{rule}'") - self._route_keys.append(route_key) + _route = Route( + item, + rule, + self._compile_regex(rule), + func, + cors_enabled, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + # The more specific route wins. + # We store dynamic (/studies/{studyid}) and static routes (/studies/fetch) separately. + # Then attempt a match for static routes before dynamic routes. + # This ensures that the most specific route is prioritized and processed first (studies/fetch). + if _route.rule.groups > 0: + self._dynamic_routes.append(_route) + else: + self._static_routes.append(_route) + + self._create_route_key(item, rule) + if cors_enabled: logger.debug(f"Registering method {item.upper()} to Allow Methods in CORS") self._cors_methods.add(item.upper()) + return func return register_resolver - def resolve(self, event, context) -> Dict[str, Any]: + def resolve(self, event: dict[str, Any], context: LambdaContext) -> dict[str, Any]: """Resolves the response based on the provide event and decorator routes + ## Internals + + Request processing chain is triggered by a Route object being called _(`_call_route` -> `__call__`)_: + + 1. **When a route is matched** + 1.1. Exception handlers _(if any exception bubbled up and caught)_ + 1.2. Global middlewares _(before, and after on the way back)_ + 1.3. Path level middleware _(before, and after on the way back)_ + 1.4. Middleware adapter to ensure Response is homogenous (_registered_api_adapter) + 1.5. Run actual route + 2. **When a route is NOT matched** + 2.1. Exception handlers _(if any exception bubbled up and caught)_ + 2.2. Global middlewares _(before, and after on the way back)_ + 2.3. Path level middleware _(before, and after on the way back)_ + 2.4. Middleware adapter to ensure Response is homogenous (_registered_api_adapter) + 2.5. Run 404 route handler + 3. **When a route is a pre-flight CORS (often not matched)** + 3.1. Exception handlers _(if any exception bubbled up and caught)_ + 3.2. Global middlewares _(before, and after on the way back)_ + 3.3. Path level middleware _(before, and after on the way back)_ + 3.4. Middleware adapter to ensure Response is homogenous (_registered_api_adapter) + 3.5. Return 204 with appropriate CORS headers + 4. **When a route is matched with Data Validation enabled** + 4.1. Exception handlers _(if any exception bubbled up and caught)_ + 4.2. Data Validation middleware _(before, and after on the way back)_ + 4.3. Global middlewares _(before, and after on the way back)_ + 4.4. Path level middleware _(before, and after on the way back)_ + 4.5. Middleware adapter to ensure Response is homogenous (_registered_api_adapter) + 4.6. Run actual route + Parameters ---------- - event: Dict[str, Any] + event: dict[str, Any] Event context: LambdaContext Lambda context @@ -495,17 +2382,56 @@ def resolve(self, event, context) -> Dict[str, Any]: dict Returns the dict response """ + if isinstance(event, BaseProxyEvent): + warnings.warn( + "You don't need to serialize event to Event Source Data Class when using Event Handler; " + "see issue #1152", + stacklevel=2, + ) + event = event.raw_event + if self._debug: - print(self._json_dump(event)) + print(self._serializer(event)) + + # Populate router(s) dependencies without keeping a reference to each registered router BaseRouter.current_event = self._to_proxy_event(event) BaseRouter.lambda_context = context - return self._resolve().build(self.current_event, self._cors) + + response = self._resolve().build(self.current_event, self._cors) + + # Debug print Processed Middlewares + if self._debug: + print("\nProcessed Middlewares:") + print("======================") + print("\n".join(self.processed_stack_frames)) + print("======================") + + self.clear_context() + + return response def __call__(self, event, context) -> Any: return self.resolve(event, context) + def _create_route_key(self, item: str, rule: str): + route_key = item + rule + if route_key in self._route_keys: + warnings.warn( + f"A route like this was already registered. method: '{item}' rule: '{rule}'", + stacklevel=2, + ) + self._route_keys.append(route_key) + + def _get_base_path(self) -> str: + raise NotImplementedError() + + @staticmethod + def _has_debug(debug: bool | None = None) -> bool: + # It might have been explicitly switched off (debug=False) + return debug if debug is not None else powertools_dev_is_set() + @staticmethod - def _compile_regex(rule: str): + def _compile_regex(rule: str, base_regex: str = _ROUTE_REGEX): """Precompile regex pattern Logic @@ -535,33 +2461,51 @@ def _compile_regex(rule: str): NOTE: See #520 for context """ rule_regex: str = re.sub(_DYNAMIC_ROUTE_PATTERN, _NAMED_GROUP_BOUNDARY_PATTERN, rule) - return re.compile("^{}$".format(rule_regex)) + return re.compile(base_regex.format(rule_regex)) - def _to_proxy_event(self, event: Dict) -> BaseProxyEvent: + def _to_proxy_event(self, event: dict) -> BaseProxyEvent: # noqa: PLR0911 # ignore many returns """Convert the event dict to the corresponding data class""" if self._proxy_type == ProxyEventType.APIGatewayProxyEvent: logger.debug("Converting event to API Gateway REST API contract") - return APIGatewayProxyEvent(event) + return APIGatewayProxyEvent(event, self._json_body_deserializer) if self._proxy_type == ProxyEventType.APIGatewayProxyEventV2: logger.debug("Converting event to API Gateway HTTP API contract") - return APIGatewayProxyEventV2(event) + return APIGatewayProxyEventV2(event, self._json_body_deserializer) + if self._proxy_type == ProxyEventType.BedrockAgentEvent: + logger.debug("Converting event to Bedrock Agent contract") + return BedrockAgentEvent(event, self._json_body_deserializer) + if self._proxy_type == ProxyEventType.LambdaFunctionUrlEvent: + logger.debug("Converting event to Lambda Function URL contract") + return LambdaFunctionUrlEvent(event, self._json_body_deserializer) + if self._proxy_type == ProxyEventType.VPCLatticeEvent: + logger.debug("Converting event to VPC Lattice contract") + return VPCLatticeEvent(event, self._json_body_deserializer) + if self._proxy_type == ProxyEventType.VPCLatticeEventV2: + logger.debug("Converting event to VPC LatticeV2 contract") + return VPCLatticeEventV2(event, self._json_body_deserializer) logger.debug("Converting event to ALB contract") - return ALBEvent(event) + return ALBEvent(event, self._json_body_deserializer) def _resolve(self) -> ResponseBuilder: """Resolves the response or return the not found response""" method = self.current_event.http_method.upper() path = self._remove_prefix(self.current_event.path) - for route in self._routes: + + registered_routes = self._static_routes + self._dynamic_routes + + for route in registered_routes: if method != route.method: continue - match_results: Optional[re.Match] = route.rule.match(path) + match_results: Match | None = route.rule.match(path) if match_results: logger.debug("Found a registered route. Calling function") - return self._call_route(route, match_results.groupdict()) # pass fn args + # Add matched Route reference into the Resolver context + self.append_context(_route=route, _path=path) - logger.debug(f"No match found for path {path} and method {method}") - return self._not_found(method) + route_keys = self._convert_matches_into_route_keys(match_results) + return self._call_route(route, route_keys) # pass fn args + + return self._handle_not_found(method=method, path=path) def _remove_prefix(self, path: str) -> str: """Remove the configured prefix from the path""" @@ -569,130 +2513,217 @@ def _remove_prefix(self, path: str) -> str: return path for prefix in self._strip_prefixes: - if path == prefix: - return "/" - if self._path_starts_with(path, prefix): - return path[len(prefix) :] + if isinstance(prefix, str): + if path == prefix: + return "/" + + if self._path_starts_with(path, prefix): + return path[len(prefix) :] + + if isinstance(prefix, Pattern): + path = re.sub(prefix, "", path) + + # When using regexes, we might get into a point where everything is removed + # from the string, so we check if it's empty and return /, since there's nothing + # else to strip anymore. + if not path: + return "/" return path + def _convert_matches_into_route_keys(self, match: Match) -> dict[str, str]: + """Converts the regex match into a dict of route keys""" + return match.groupdict() + @staticmethod def _path_starts_with(path: str, prefix: str): """Returns true if the `path` starts with a prefix plus a `/`""" if not isinstance(prefix, str) or prefix == "": return False - return path.startswith(prefix + "/") + return path.startswith(f"{prefix}/") - def _not_found(self, method: str) -> ResponseBuilder: + def _handle_not_found(self, method: str, path: str) -> ResponseBuilder: """Called when no matching route was found and includes support for the cors preflight response""" - headers = {} - if self._cors: - logger.debug("CORS is enabled, updating headers.") - headers.update(self._cors.to_dict()) + logger.debug(f"No match found for path {path} and method {method}") - if method == "OPTIONS": - logger.debug("Pre-flight request detected. Returning CORS with null response") - headers["Access-Control-Allow-Methods"] = ",".join(sorted(self._cors_methods)) - return ResponseBuilder(Response(status_code=204, content_type=None, headers=headers, body=None)) + def not_found_handler(): + """Route handler for 404s - handler = self._lookup_exception_handler(NotFoundError) - if handler: - return ResponseBuilder(handler(NotFoundError())) + It handles in the following order: + + 1. Pre-flight CORS requests (OPTIONS) + 2. Detects and calls custom HTTP 404 handler + 3. Returns standard 404 along with CORS headers + + Returns + ------- + Response + HTTP 404 response + """ + _headers: dict[str, Any] = {} - return ResponseBuilder( - Response( + # Pre-flight request? Return immediately to avoid browser error + if self._cors and method == "OPTIONS": + logger.debug("Pre-flight request detected. Returning CORS with empty response") + _headers["Access-Control-Allow-Methods"] = CORSConfig.build_allow_methods(self._cors_methods) + + return Response(status_code=204, content_type=None, headers=_headers, body="") + + # Customer registered 404 route? Call it. + custom_not_found_handler = self.exception_handler_manager.lookup_exception_handler(NotFoundError) + if custom_not_found_handler: + return custom_not_found_handler(NotFoundError()) + + # No CORS and no custom 404 fn? Default response + return Response( status_code=HTTPStatus.NOT_FOUND.value, content_type=content_types.APPLICATION_JSON, - headers=headers, - body=self._json_dump({"statusCode": HTTPStatus.NOT_FOUND.value, "message": "Not found"}), + headers=_headers, + body={"statusCode": HTTPStatus.NOT_FOUND.value, "message": "Not found"}, ) + + # We create a route to trigger entire request chain (middleware+exception handlers) + route = Route( + rule=self._compile_regex(r".*"), + method=method, + path=path, + func=not_found_handler, + cors=self._cors_enabled, + compress=False, ) - def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder: + # Add matched Route reference into the Resolver context + self.append_context(_route=route, _path=path) + + # Kick-off request chain: + # -> exception_handlers() + # --> middlewares() + # ---> not_found_route() + return self._call_route(route=route, route_arguments={}) + + def _call_route(self, route: Route, route_arguments: dict[str, str]) -> ResponseBuilder: """Actually call the matching route with any provided keyword arguments.""" try: - return ResponseBuilder(self._to_response(route.func(**args)), route) + # Reset Processed stack for Middleware (for debugging purposes) + self._reset_processed_stack() + + return self._response_builder_class( + response=self._to_response( # type: ignore[arg-type] + route(router_middlewares=self._router_middlewares, app=self, route_arguments=route_arguments), + ), + serializer=self._serializer, + route=route, + ) except Exception as exc: + # If exception is handled then return the response builder to reduce noise response_builder = self._call_exception_handler(exc, route) if response_builder: return response_builder + logger.exception(exc) if self._debug: # If the user has turned on debug mode, - # we'll let the original exception propagate so + # we'll let the original exception propagate, so # they get more information about what went wrong. - return ResponseBuilder( - Response( + return self._response_builder_class( + response=Response( status_code=500, content_type=content_types.TEXT_PLAIN, body="".join(traceback.format_exc()), ), - route, + serializer=self._serializer, + route=route, ) raise - def not_found(self, func: Optional[Callable] = None): + def not_found(self, func: Callable | None = None): if func is None: return self.exception_handler(NotFoundError) return self.exception_handler(NotFoundError)(func) - def exception_handler(self, exc_class: Type[Exception]): - def register_exception_handler(func: Callable): - self._exception_handlers[exc_class] = func - - return register_exception_handler - - def _lookup_exception_handler(self, exp_type: Type) -> Optional[Callable]: - # Use "Method Resolution Order" to allow for matching against a base class - # of an exception - for cls in exp_type.__mro__: - if cls in self._exception_handlers: - return self._exception_handlers[cls] - return None + def exception_handler(self, exc_class: type[Exception] | list[type[Exception]]): + return self.exception_handler_manager.exception_handler(exc_class=exc_class) - def _call_exception_handler(self, exp: Exception, route: Route) -> Optional[ResponseBuilder]: - handler = self._lookup_exception_handler(type(exp)) + def _call_exception_handler(self, exp: Exception, route: Route) -> ResponseBuilder | None: + handler = self.exception_handler_manager.lookup_exception_handler(type(exp)) if handler: - return ResponseBuilder(handler(exp), route) + try: + return self._response_builder_class(response=handler(exp), serializer=self._serializer, route=route) + except ServiceError as service_error: + exp = service_error + + if isinstance(exp, RequestValidationError): + # For security reasons, we hide msg details (don't leak Python, Pydantic or file names) + errors = [{"loc": e["loc"], "type": e["type"]} for e in exp.errors()] + + return self._response_builder_class( + response=Response( + status_code=HTTPStatus.UNPROCESSABLE_ENTITY, + content_type=content_types.APPLICATION_JSON, + body={"statusCode": HTTPStatus.UNPROCESSABLE_ENTITY, "detail": errors}, + ), + serializer=self._serializer, + route=route, + ) + + # OpenAPIValidationMiddleware will only raise ResponseValidationError when + # 'self._response_validation_error_http_code' is not None or + # when route has custom_response_validation_http_code + if isinstance(exp, ResponseValidationError): + # route validation must take precedence over app validation + http_code = route.custom_response_validation_http_code or self._response_validation_error_http_code + errors = [{"loc": e["loc"], "type": e["type"]} for e in exp.errors()] + return self._response_builder_class( + response=Response( + status_code=http_code.value, + content_type=content_types.APPLICATION_JSON, + body={"statusCode": http_code, "detail": errors}, + ), + serializer=self._serializer, + route=route, + ) if isinstance(exp, ServiceError): - return ResponseBuilder( - Response( + return self._response_builder_class( + response=Response( status_code=exp.status_code, content_type=content_types.APPLICATION_JSON, - body=self._json_dump({"statusCode": exp.status_code, "message": exp.msg}), + body={"statusCode": exp.status_code, "message": exp.msg}, ), - route, + serializer=self._serializer, + route=route, ) return None - def _to_response(self, result: Union[Dict, Response]) -> Response: + def _to_response(self, result: dict | tuple | Response | BedrockResponse) -> Response | BedrockResponse: """Convert the route's result to a Response - 2 main result types are supported: + 3 main result types are supported: - - Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to + - dict[str, Any]: Rest api response with just the dict to json stringify and content-type is set to application/json + - tuple[dict, int]: Same dict handling as above but with the option of including a status code - Response: returned as is, and allows for more flexibility """ - if isinstance(result, Response): + status_code = HTTPStatus.OK + if isinstance(result, (Response, BedrockResponse)): return result + elif isinstance(result, tuple) and len(result) == 2: + # Unpack result dict and status code from tuple + result, status_code = result logger.debug("Simple response detected, serializing return before constructing final response") return Response( - status_code=200, + status_code=status_code, content_type=content_types.APPLICATION_JSON, - body=self._json_dump(result), + body=result, ) - def _json_dump(self, obj: Any) -> str: - return self._serializer(obj) - - def include_router(self, router: "Router", prefix: Optional[str] = None) -> None: - """Adds all routes defined in a router + def include_router(self, router: Router, prefix: str | None = None) -> None: + """Adds all routes and context defined in a router Parameters ---------- @@ -705,33 +2736,336 @@ def include_router(self, router: "Router", prefix: Optional[str] = None) -> None # Add reference to parent ApiGatewayResolver to support use cases where people subclass it to add custom logic router.api_resolver = self + logger.debug("Merging App context with Router context") + self.context.update(**router.context) + + logger.debug("Appending Router middlewares into App middlewares.") + self._router_middlewares = self._router_middlewares + router._router_middlewares + + logger.debug("Appending Router exception_handler into App exception_handler.") + self.exception_handler_manager.update_exception_handlers(router._exception_handlers) + + # use pointer to allow context clearance after event is processed e.g., resolve(evt, ctx) + router.context = self.context + + # Iterate through the routes defined in the router to configure and apply middlewares for each route for route, func in router._routes.items(): + new_route = route + if prefix: rule = route[0] rule = prefix if rule == "/" else f"{prefix}{rule}" - route = (rule, *route[1:]) + new_route = (rule, *route[1:]) + + # Middlewares are stored by route separately - must grab them to include + # Middleware store the route without prefix, so we must not include prefix when grabbing + middlewares = router._routes_with_middleware.get(route) + + # Need to use "type: ignore" here since mypy does not like a named parameter after + # tuple expansion since may cause duplicate named parameters in the function signature. + # In this case this is not possible since the tuple expansion is from a hashable source + # and the `middlewares` list is a non-hashable structure so will never be included. + # Still need to ignore for mypy checks or will cause failures (false-positive) + self.route(*new_route, middlewares=middlewares)(func) # type: ignore + + @staticmethod + def _get_fields_from_routes(routes: Sequence[Route]) -> list[ModelField]: + """ + Returns a list of fields from the routes + """ + + from aws_lambda_powertools.event_handler.openapi.compat import ModelField + from aws_lambda_powertools.event_handler.openapi.dependant import ( + get_flat_params, + ) + + body_fields_from_routes: list[ModelField] = [] + responses_from_routes: list[ModelField] = [] + request_fields_from_routes: list[ModelField] = [] + + for route in routes: + if route.body_field: + if not isinstance(route.body_field, ModelField): + raise AssertionError("A request body myst be a Pydantic Field") + body_fields_from_routes.append(route.body_field) - self.route(*route)(func) + params = get_flat_params(route.dependant) + request_fields_from_routes.extend(params) + + if route.dependant.return_param: + responses_from_routes.append(route.dependant.return_param) + + if route.dependant.response_extra_models: + responses_from_routes.extend(route.dependant.response_extra_models) + + return list( + responses_from_routes + request_fields_from_routes + body_fields_from_routes, + ) class Router(BaseRouter): """Router helper class to allow splitting ApiGatewayResolver into multiple files""" def __init__(self): - self._routes: Dict[tuple, Callable] = {} - self.api_resolver: Optional[BaseRouter] = None + self._routes: dict[tuple, Callable] = {} + self._routes_with_middleware: dict[tuple, list[Callable]] = {} + self.api_resolver: BaseRouter | None = None + self.context = {} # early init as customers might add context before event resolution + self._exception_handlers: dict[type, Callable] = {} def route( self, rule: str, - method: Union[str, Union[List[str], Tuple[str]]], - cors: Optional[bool] = None, + method: str | list[str] | tuple[str], + cors: bool | None = None, compress: bool = False, - cache_control: Optional[str] = None, - ): - def register_route(func: Callable): - # Convert methods to tuple. It needs to be hashable as its part of the self._routes dict key + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str | None = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: + def register_route(func: AnyCallableT) -> AnyCallableT: + # All dict keys needs to be hashable. So we'll need to do some conversions: methods = (method,) if isinstance(method, str) else tuple(method) - self._routes[(rule, methods, cors, compress, cache_control)] = func + frozen_responses = _FrozenDict(responses) if responses else None + frozen_tags = frozenset(tags) if tags else None + frozen_security = _FrozenListDict(security) if security else None + frozen_openapi_extensions = _FrozenDict(openapi_extensions) if openapi_extensions else None + + route_key = ( + rule, + methods, + cors, + compress, + cache_control, + summary, + description, + frozen_responses, + response_description, + frozen_tags, + operation_id, + include_in_schema, + frozen_security, + frozen_openapi_extensions, + deprecated, + custom_response_validation_http_code, + ) + + # Collate Middleware for routes + if middlewares is not None: + for handler in middlewares: + if self._routes_with_middleware.get(route_key) is None: + self._routes_with_middleware[route_key] = [handler] + else: + self._routes_with_middleware[route_key].append(handler) + else: + self._routes_with_middleware[route_key] = [] + + self._routes[route_key] = func + + return func return register_route + + def exception_handler(self, exc_class: type[Exception] | list[type[Exception]]): + def register_exception_handler(func: Callable): + if isinstance(exc_class, list): + for exp in exc_class: + self._exception_handlers[exp] = func + else: + self._exception_handlers[exc_class] = func + return func + + return register_exception_handler + + +class APIGatewayRestResolver(ApiGatewayResolver): + """Amazon API Gateway REST and HTTP API v1 payload resolver""" + + current_event: APIGatewayProxyEvent + + def __init__( + self, + cors: CORSConfig | None = None, + debug: bool | None = None, + serializer: Callable[[dict], str] | None = None, + strip_prefixes: list[str | Pattern] | None = None, + enable_validation: bool = False, + response_validation_error_http_code: HTTPStatus | int | None = None, + json_body_deserializer: Callable[[str], dict] | None = None, + ): + """Amazon API Gateway REST and HTTP API v1 payload resolver""" + super().__init__( + ProxyEventType.APIGatewayProxyEvent, + cors, + debug, + serializer, + strip_prefixes, + enable_validation, + response_validation_error_http_code, + json_body_deserializer=json_body_deserializer, + ) + + def _get_base_path(self) -> str: + # 3 different scenarios: + # + # 1. SAM local: even though a stage variable is sent to the Lambda function, it's not used in the path + # 2. API Gateway REST API: stage variable is used in the path + # 3. API Gateway REST Custom Domain: stage variable is not used in the path + # + # To solve the 3 scenarios, we try to match the beginning of the path with the stage variable + stage = self.current_event.request_context.stage + if stage and stage != "$default" and self.current_event.request_context.path.startswith(f"/{stage}"): + return f"/{stage}" + return "" + + # override route to ignore trailing "/" in routes for REST API + def route( + self, + rule: str, + method: str | list[str] | tuple[str], + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + description: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[AnyCallableT], AnyCallableT]: + # NOTE: see #1552 for more context. + return super().route( + rule.rstrip("/"), + method, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + # Override _compile_regex to exclude trailing slashes for route resolution + @staticmethod + def _compile_regex(rule: str, base_regex: str = _ROUTE_REGEX): + return super(APIGatewayRestResolver, APIGatewayRestResolver)._compile_regex(rule, "^{}/*$") + + +class APIGatewayHttpResolver(ApiGatewayResolver): + """Amazon API Gateway HTTP API v2 payload resolver""" + + current_event: APIGatewayProxyEventV2 + + def __init__( + self, + cors: CORSConfig | None = None, + debug: bool | None = None, + serializer: Callable[[dict], str] | None = None, + strip_prefixes: list[str | Pattern] | None = None, + enable_validation: bool = False, + response_validation_error_http_code: HTTPStatus | int | None = None, + json_body_deserializer: Callable[[str], dict] | None = None, + ): + """Amazon API Gateway HTTP API v2 payload resolver""" + super().__init__( + ProxyEventType.APIGatewayProxyEventV2, + cors, + debug, + serializer, + strip_prefixes, + enable_validation, + response_validation_error_http_code, + json_body_deserializer=json_body_deserializer, + ) + + def _get_base_path(self) -> str: + # 3 different scenarios: + # + # 1. SAM local: even though a stage variable is sent to the Lambda function, it's not used in the path + # 2. API Gateway HTTP API: stage variable is used in the path + # 3. API Gateway HTTP Custom Domain: stage variable is not used in the path + # + # To solve the 3 scenarios, we try to match the beginning of the path with the stage variable + stage = self.current_event.request_context.stage + if stage and stage != "$default" and self.current_event.request_context.http.path.startswith(f"/{stage}"): + return f"/{stage}" + return "" + + +class ALBResolver(ApiGatewayResolver): + """Amazon Application Load Balancer (ALB) resolver""" + + current_event: ALBEvent + + def __init__( + self, + cors: CORSConfig | None = None, + debug: bool | None = None, + serializer: Callable[[dict], str] | None = None, + strip_prefixes: list[str | Pattern] | None = None, + enable_validation: bool = False, + response_validation_error_http_code: HTTPStatus | int | None = None, + json_body_deserializer: Callable[[str], dict] | None = None, + ): + """Amazon Application Load Balancer (ALB) resolver""" + super().__init__( + ProxyEventType.ALBEvent, + cors, + debug, + serializer, + strip_prefixes, + enable_validation, + response_validation_error_http_code, + json_body_deserializer=json_body_deserializer, + ) + + def _get_base_path(self) -> str: + # ALB doesn't have a stage variable, so we just return an empty string + return "" + + # BedrockResponse is not used here but adding the same signature to keep strong typing + @override + def _to_response(self, result: dict | tuple | Response | BedrockResponse) -> Response | BedrockResponse: + """Convert the route's result to a Response + + ALB requires a non-null body otherwise it converts as HTTP 5xx + + 3 main result types are supported: + + - Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to + application/json + - Tuple[dict, int]: Same dict handling as above but with the option of including a status code + - Response: returned as is, and allows for more flexibility + """ + + # NOTE: Minor override for early return on Response with null body for ALB + if isinstance(result, Response) and result.body is None: + logger.debug("ALB doesn't allow None responses; converting to empty string") + result.body = "" + + return super()._to_response(result) diff --git a/aws_lambda_powertools/event_handler/appsync.py b/aws_lambda_powertools/event_handler/appsync.py index 6a4bf989169..29c48d71cb1 100644 --- a/aws_lambda_powertools/event_handler/appsync.py +++ b/aws_lambda_powertools/event_handler/appsync.py @@ -1,84 +1,85 @@ +from __future__ import annotations + +import asyncio import logging -from abc import ABC -from typing import Any, Callable, Optional, Type, TypeVar +import warnings +from typing import TYPE_CHECKING, Any +from aws_lambda_powertools.event_handler.exception_handling import ExceptionHandlerManager +from aws_lambda_powertools.event_handler.graphql_appsync.exceptions import InvalidBatchResponse, ResolverNotFoundError +from aws_lambda_powertools.event_handler.graphql_appsync.router import Router from aws_lambda_powertools.utilities.data_classes import AppSyncResolverEvent -from aws_lambda_powertools.utilities.typing import LambdaContext - -logger = logging.getLogger(__name__) - -AppSyncResolverEventT = TypeVar("AppSyncResolverEventT", bound=AppSyncResolverEvent) +if TYPE_CHECKING: + from collections.abc import Callable -class BaseRouter(ABC): - current_event: AppSyncResolverEventT # type: ignore[valid-type] - lambda_context: LambdaContext + from aws_lambda_powertools.utilities.typing import LambdaContext - def __init__(self): - self._resolvers: dict = {} - - def resolver(self, type_name: str = "*", field_name: Optional[str] = None): - """Registers the resolver for field_name +from aws_lambda_powertools.warnings import PowertoolsUserWarning - Parameters - ---------- - type_name : str - Type name - field_name : str - Field name - """ - - def register_resolver(func): - logger.debug(f"Adding resolver `{func.__name__}` for field `{type_name}.{field_name}`") - self._resolvers[f"{type_name}.{field_name}"] = {"func": func} - return func - - return register_resolver +logger = logging.getLogger(__name__) -class AppSyncResolver(BaseRouter): +class AppSyncResolver(Router): """ - AppSync resolver decorator + AppSync GraphQL API Resolver Example ------- - - **Sample usage** - - from aws_lambda_powertools.event_handler import AppSyncResolver - - app = AppSyncResolver() - - @app.resolver(type_name="Query", field_name="listLocations") - def list_locations(page: int = 0, size: int = 10) -> list: - # Your logic to fetch locations with arguments passed in - return [{"id": 100, "name": "Smooth Grooves"}] - - @app.resolver(type_name="Merchant", field_name="extraInfo") - def get_extra_info() -> dict: - # Can use "app.current_event.source" to filter within the parent context - account_type = app.current_event.source["accountType"] - method = "BTC" if account_type == "NEW" else "USD" - return {"preferredPaymentMethod": method} - - @app.resolver(field_name="commonField") - def common_field() -> str: - # Would match all fieldNames matching 'commonField' - return str(uuid.uuid4()) + ```python + from aws_lambda_powertools.event_handler import AppSyncResolver + + app = AppSyncResolver() + + @app.resolver(type_name="Query", field_name="listLocations") + def list_locations(page: int = 0, size: int = 10) -> list: + # Your logic to fetch locations with arguments passed in + return [{"id": 100, "name": "Smooth Grooves"}] + + @app.resolver(type_name="Merchant", field_name="extraInfo") + def get_extra_info() -> dict: + # Can use "app.current_event.source" to filter within the parent context + account_type = app.current_event.source["accountType"] + method = "BTC" if account_type == "NEW" else "USD" + return {"preferredPaymentMethod": method} + + @app.resolver(field_name="commonField") + def common_field() -> str: + # Would match all fieldNames matching 'commonField' + return str(uuid.uuid4()) + ``` """ def __init__(self): + """ + Initialize a new instance of the AppSyncResolver. + """ super().__init__() + self.context = {} # early init as customers might add context before event resolution + self.exception_handler_manager = ExceptionHandlerManager() + self._exception_handlers: dict[type, Callable] = {} + + def __call__( + self, + event: dict, + context: LambdaContext, + data_model: type[AppSyncResolverEvent] = AppSyncResolverEvent, + ) -> Any: + """Implicit lambda handler which internally calls `resolve`""" + return self.resolve(event, context, data_model) def resolve( - self, event: dict, context: LambdaContext, data_model: Type[AppSyncResolverEvent] = AppSyncResolverEvent + self, + event: dict | list[dict], + context: LambdaContext, + data_model: type[AppSyncResolverEvent] = AppSyncResolverEvent, ) -> Any: - """Resolve field_name + """Resolves the response based on the provide event and decorator routes Parameters ---------- - event : dict - Lambda event + event : dict | list[Dict] + Lambda event either coming from batch processing endpoint or from standard processing endpoint context : LambdaContext Lambda context data_model: @@ -115,7 +116,7 @@ def handler(event, context: LambdaContext): class MyCustomModel(AppSyncResolverEvent): @property def country_viewer(self) -> str: - return self.request_headers.get("cloudfront-viewer-country") + return self.request_headers.get("cloudfront-viewer-country", "") @app.resolver(field_name="listLocations") @@ -142,39 +143,228 @@ def lambda_handler(event, context): ValueError If we could not find a field resolver """ - BaseRouter.current_event = data_model(event) - BaseRouter.lambda_context = context - resolver = self._get_resolver(BaseRouter.current_event.type_name, BaseRouter.current_event.field_name) - return resolver(**BaseRouter.current_event.arguments) - def _get_resolver(self, type_name: str, field_name: str) -> Callable: - """Get resolver for field_name + self.lambda_context = context + Router.lambda_context = context + + try: + if isinstance(event, list): + Router.current_batch_event = [data_model(e) for e in event] + response = self._call_batch_resolver(event=event, data_model=data_model) + else: + Router.current_event = data_model(event) + response = self._call_single_resolver(event=event, data_model=data_model) + except Exception as exp: + response_builder = self.exception_handler_manager.lookup_exception_handler(type(exp)) + if response_builder: + return response_builder(exp) + raise + + # We don't clear the context for coroutines because we don't have control over the event loop. + # If we clean the context immediately, it might not be available when the coroutine is actually executed. + # For single async operations, the context should be cleaned up manually after the coroutine completes. + # See: https://github.com/aws-powertools/powertools-lambda-python/issues/5290 + # REVIEW: Review this support in Powertools V4 + if not asyncio.iscoroutine(response): + self.clear_context() + + return response + + def _call_single_resolver(self, event: dict, data_model: type[AppSyncResolverEvent]) -> Any: + """Call single event resolver Parameters ---------- - type_name : str - Type name - field_name : str - Field name + event : dict + Event + data_model : type[AppSyncResolverEvent] + Data_model to decode AppSync event, by default it is of AppSyncResolverEvent type or subclass of it + """ + + logger.debug("Processing direct resolver event") + + self.current_event = data_model(event) + resolver = self._resolver_registry.find_resolver(self.current_event.type_name, self.current_event.field_name) + if not resolver: + raise ValueError(f"No resolver found for '{self.current_event.type_name}.{self.current_event.field_name}'") + return resolver["func"](**self.current_event.arguments) + + def _call_sync_batch_resolver( + self, + resolver: Callable, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> list[Any]: + """ + Calls a synchronous batch resolver function for each event in the current batch. + + Parameters + ---------- + resolver: Callable + The callable function to resolve events. + raise_on_error: bool + A flag indicating whether to raise an error when processing batches + with failed items. Defaults to False, which means errors are handled without raising exceptions. + aggregate: bool + A flag indicating whether the batch items should be processed at once or individually. + If True (default), the batch resolver will process all items in the batch as a single event. + If False, the batch resolver will process each item in the batch individually. Returns ------- - Callable - callable function and configuration + list[Any] + A list of results corresponding to the resolved events. """ - full_name = f"{type_name}.{field_name}" - resolver = self._resolvers.get(full_name, self._resolvers.get(f"*.{field_name}")) - if not resolver: - raise ValueError(f"No resolver found for '{full_name}'") - return resolver["func"] - def __call__( - self, event: dict, context: LambdaContext, data_model: Type[AppSyncResolverEvent] = AppSyncResolverEvent - ) -> Any: - """Implicit lambda handler which internally calls `resolve`""" - return self.resolve(event, context, data_model) + logger.debug(f"Graceful error handling flag {raise_on_error=}") + + # Checks whether the entire batch should be processed at once + if aggregate: + # Process the entire batch + response = resolver(event=self.current_batch_event) + + if not isinstance(response, list): + raise InvalidBatchResponse("The response must be a List when using batch resolvers") + + return response + + # Non aggregated events, so we call this event list x times + # Stop on first exception we encounter + if raise_on_error: + return [ + resolver(event=appconfig_event, **appconfig_event.arguments) + for appconfig_event in self.current_batch_event + ] + + # By default, we gracefully append `None` for any records that failed processing + results = [] + for idx, event in enumerate(self.current_batch_event): + try: + results.append(resolver(event=event, **event.arguments)) + except Exception: + logger.debug(f"Failed to process event number {idx} from field '{event.info.field_name}'") + results.append(None) + + return results + + async def _call_async_batch_resolver( + self, + resolver: Callable, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> list[Any]: + """ + Asynchronously call a batch resolver for each event in the current batch. + + Parameters + ---------- + resolver: Callable + The asynchronous resolver function. + raise_on_error: bool + A flag indicating whether to raise an error when processing batches + with failed items. Defaults to False, which means errors are handled without raising exceptions. + aggregate: bool + A flag indicating whether the batch items should be processed at once or individually. + If True (default), the batch resolver will process all items in the batch as a single event. + If False, the batch resolver will process each item in the batch individually. + + Returns + ------- + list[Any] + A list of results corresponding to the resolved events. + """ + + logger.debug(f"Graceful error handling flag {raise_on_error=}") + + # Checks whether the entire batch should be processed at once + if aggregate: + # Process the entire batch + ret = await resolver(event=self.current_batch_event) + if not isinstance(ret, list): + raise InvalidBatchResponse("The response must be a List when using batch resolvers") + + return ret + + response: list = [] + + # Prime coroutines + tasks = [resolver(event=e, **e.arguments) for e in self.current_batch_event] + + # Aggregate results or raise at first error + if raise_on_error: + response.extend(await asyncio.gather(*tasks)) + return response + + # Aggregate results and exceptions, then filter them out + # Use `None` upon exception for graceful error handling at GraphQL engine level + # + # NOTE: asyncio.gather(return_exceptions=True) catches and includes exceptions in the results + # this will become useful when we support exception handling in AppSync resolver + results = await asyncio.gather(*tasks, return_exceptions=True) + response.extend(None if isinstance(ret, Exception) else ret for ret in results) + + return response - def include_router(self, router: "Router") -> None: + def _call_batch_resolver(self, event: list[dict], data_model: type[AppSyncResolverEvent]) -> list[Any]: + """Call batch event resolver for sync and async methods + + Parameters + ---------- + event : list[dict] + Batch event + data_model : type[AppSyncResolverEvent] + Data_model to decode AppSync event, by default AppSyncResolverEvent or a subclass + + Returns + ------- + list[Any] + Results of the resolver execution. + + Raises + ------ + InconsistentPayloadError: + When all events in the batch do not have the same fieldName. + + ResolverNotFoundError: + When no resolver is found for the specified type and field. + """ + logger.debug("Processing batch resolver event") + + self.current_batch_event = [data_model(e) for e in event] + type_name, field_name = self.current_batch_event[0].type_name, self.current_batch_event[0].field_name + + resolver = self._batch_resolver_registry.find_resolver(type_name, field_name) + async_resolver = self._async_batch_resolver_registry.find_resolver(type_name, field_name) + + if resolver and async_resolver: + warnings.warn( + f"Both synchronous and asynchronous resolvers found for the same event and field." + f"The synchronous resolver takes precedence. Executing: {resolver['func'].__name__}", + stacklevel=2, + category=PowertoolsUserWarning, + ) + + if resolver: + logger.debug(f"Found sync resolver. {resolver=}, {field_name=}") + return self._call_sync_batch_resolver( + resolver=resolver["func"], + raise_on_error=resolver["raise_on_error"], + aggregate=resolver["aggregate"], + ) + + if async_resolver: + logger.debug(f"Found async resolver. {resolver=}, {field_name=}") + return asyncio.run( + self._call_async_batch_resolver( + resolver=async_resolver["func"], + raise_on_error=async_resolver["raise_on_error"], + aggregate=async_resolver["aggregate"], + ), + ) + + raise ResolverNotFoundError(f"No resolver found for '{type_name}.{field_name}'") + + def include_router(self, router: Router) -> None: """Adds all resolvers defined in a router Parameters @@ -182,9 +372,129 @@ def include_router(self, router: "Router") -> None: router : Router A router containing a dict of field resolvers """ - self._resolvers.update(router._resolvers) + # Merge app and router context + logger.debug("Merging router and app context") + self.context.update(**router.context) -class Router(BaseRouter): - def __init__(self): - super().__init__() + # use pointer to allow context clearance after event is processed e.g., resolve(evt, ctx) + router.context = self.context + + logger.debug("Merging router resolver registries") + self._resolver_registry.merge(router._resolver_registry) + self._batch_resolver_registry.merge(router._batch_resolver_registry) + self._async_batch_resolver_registry.merge(router._async_batch_resolver_registry) + + def resolver(self, type_name: str = "*", field_name: str | None = None) -> Callable: + """Registers direct resolver function for GraphQL type and field name. + + Parameters + ---------- + type_name : str, optional + GraphQL type e.g., Query, Mutation, by default "*" meaning any + field_name : Optional[str], optional + GraphQL field e.g., getTodo, createTodo, by default None + + Returns + ------- + Callable + Registered resolver + + Example + ------- + + ```python + from aws_lambda_powertools.event_handler import AppSyncResolver + + from typing import TypedDict + + app = AppSyncResolver() + + class Todo(TypedDict, total=False): + id: str + userId: str + title: str + completed: bool + + # resolve any GraphQL `getTodo` queries + # arguments are injected as function arguments as-is + @app.resolver(type_name="Query", field_name="getTodo") + def get_todo(id: str = "", status: str = "open") -> Todo: + todos: Response = requests.get(f"https://jsonplaceholder.typicode.com/todos/{id}") + todos.raise_for_status() + + return todos.json() + + def lambda_handler(event, context): + return app.resolve(event, context) + ``` + """ + return self._resolver_registry.register(field_name=field_name, type_name=type_name) + + def batch_resolver( + self, + type_name: str = "*", + field_name: str | None = None, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> Callable: + """Registers batch resolver function for GraphQL type and field name. + + By default, we handle errors gracefully by returning `None`. If you want + to short-circuit and fail the entire batch use `raise_on_error=True`. + + Parameters + ---------- + type_name : str, optional + GraphQL type e.g., Query, Mutation, by default "*" meaning any + field_name : Optional[str], optional + GraphQL field e.g., getTodo, createTodo, by default None + raise_on_error : bool, optional + Whether to fail entire batch upon error, or handle errors gracefully (None), by default False + aggregate: bool + A flag indicating whether the batch items should be processed at once or individually. + If True (default), the batch resolver will process all items in the batch as a single event. + If False, the batch resolver will process each item in the batch individually. + + Returns + ------- + Callable + Registered resolver + """ + return self._batch_resolver_registry.register( + field_name=field_name, + type_name=type_name, + raise_on_error=raise_on_error, + aggregate=aggregate, + ) + + def async_batch_resolver( + self, + type_name: str = "*", + field_name: str | None = None, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> Callable: + return self._async_batch_resolver_registry.register( + field_name=field_name, + type_name=type_name, + raise_on_error=raise_on_error, + aggregate=aggregate, + ) + + def exception_handler(self, exc_class: type[Exception] | list[type[Exception]]): + """ + A decorator function that registers a handler for one or more exception types. + + Parameters + ---------- + exc_class (type[Exception] | list[type[Exception]]) + A single exception type or a list of exception types. + + Returns + ------- + Callable: + A decorator function that registers the exception handler. + """ + + return self.exception_handler_manager.exception_handler(exc_class=exc_class) diff --git a/aws_lambda_powertools/event_handler/bedrock_agent.py b/aws_lambda_powertools/event_handler/bedrock_agent.py new file mode 100644 index 00000000000..008aeb0ccdd --- /dev/null +++ b/aws_lambda_powertools/event_handler/bedrock_agent.py @@ -0,0 +1,428 @@ +from __future__ import annotations + +import json +from typing import TYPE_CHECKING, Any + +from typing_extensions import override + +from aws_lambda_powertools.event_handler import ApiGatewayResolver +from aws_lambda_powertools.event_handler.api_gateway import ( + _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + BedrockResponse, + ProxyEventType, + ResponseBuilder, +) +from aws_lambda_powertools.event_handler.openapi.constants import DEFAULT_API_VERSION, DEFAULT_OPENAPI_VERSION + +if TYPE_CHECKING: + from collections.abc import Callable + from http import HTTPStatus + from re import Match + + from aws_lambda_powertools.event_handler.openapi.models import Contact, License, SecurityScheme, Server, Tag + from aws_lambda_powertools.event_handler.openapi.types import OpenAPIResponse + from aws_lambda_powertools.utilities.data_classes import BedrockAgentEvent + + +class BedrockResponseBuilder(ResponseBuilder): + """ + Bedrock Response Builder. This builds the response dict to be returned by Lambda when using Bedrock Agents. + + Since the payload format is different from the standard API Gateway Proxy event, we override the build method. + """ + + @override + def build(self, event: BedrockAgentEvent, *args) -> dict[str, Any]: + body = self.response.body + if self.response.is_json() and not isinstance(self.response.body, str): + body = self.serializer(self.response.body) + + response = { + "messageVersion": "1.0", + "response": { + "actionGroup": event.action_group, + "apiPath": event.api_path, + "httpMethod": event.http_method, + "httpStatusCode": self.response.status_code, + "responseBody": { + self.response.content_type: { + "body": body, + }, + }, + }, + } + + # Add Bedrock-specific attributes + if isinstance(self.response, BedrockResponse): + if self.response.session_attributes: + response["sessionAttributes"] = self.response.session_attributes + + if self.response.prompt_session_attributes: + response["promptSessionAttributes"] = self.response.prompt_session_attributes + + if self.response.knowledge_bases_configuration: + response["knowledgeBasesConfiguration"] = self.response.knowledge_bases_configuration + + return response + + +class BedrockAgentResolver(ApiGatewayResolver): + """Bedrock Agent Resolver + + See https://aws.amazon.com/bedrock/agents/ for more information. + + Examples + -------- + Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator + + ```python + from aws_lambda_powertools import Tracer + from aws_lambda_powertools.event_handler import BedrockAgentResolver + + tracer = Tracer() + app = BedrockAgentResolver() + + @app.get("/claims") + def simple_get(): + return "You have 3 claims" + + @tracer.capture_lambda_handler + def lambda_handler(event, context): + return app.resolve(event, context) + ``` + + """ + + current_event: BedrockAgentEvent + + def __init__(self, debug: bool = False, enable_validation: bool = True): + super().__init__( + proxy_type=ProxyEventType.BedrockAgentEvent, + cors=None, + debug=debug, + serializer=None, + strip_prefixes=None, + enable_validation=enable_validation, + json_body_deserializer=None, + ) + self._response_builder_class = BedrockResponseBuilder + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def get( # type: ignore[override] + self, + rule: str, + description: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + security = None + + return super().get( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def post( # type: ignore[override] + self, + rule: str, + description: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ): + security = None + + return super().post( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def put( # type: ignore[override] + self, + rule: str, + description: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ): + security = None + + return super().put( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def patch( # type: ignore[override] + self, + rule: str, + description: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable] | None = None, + ): + security = None + + return super().patch( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def delete( # type: ignore[override] + self, + rule: str, + description: str, + cors: bool | None = None, + compress: bool = False, + cache_control: str | None = None, + summary: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: list[str] | None = None, + operation_id: str | None = None, + include_in_schema: bool = True, + openapi_extensions: dict[str, Any] | None = None, + deprecated: bool = False, + custom_response_validation_http_code: int | HTTPStatus | None = None, + middlewares: list[Callable[..., Any]] | None = None, + ): + security = None + + return super().delete( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + security, + openapi_extensions, + deprecated, + custom_response_validation_http_code, + middlewares, + ) + + @override + def _convert_matches_into_route_keys(self, match: Match) -> dict[str, str]: + # In Bedrock Agents, all the parameters come inside the "parameters" key, not on the apiPath + # So we have to search for route parameters in the parameters key + parameters: dict[str, str] = {} + if match.groupdict() and self.current_event.parameters: + parameters = {parameter["name"]: parameter["value"] for parameter in self.current_event.parameters} + return parameters + + @override + def get_openapi_json_schema( # type: ignore[override] + self, + *, + title: str = "Powertools API", + version: str = DEFAULT_API_VERSION, + openapi_version: str = DEFAULT_OPENAPI_VERSION, + summary: str | None = None, + description: str | None = None, + tags: list[Tag | str] | None = None, + servers: list[Server] | None = None, + terms_of_service: str | None = None, + contact: Contact | None = None, + license_info: License | None = None, + security_schemes: dict[str, SecurityScheme] | None = None, + security: list[dict[str, list[str]]] | None = None, + openapi_extensions: dict[str, Any] | None = None, + ) -> str: + """ + Returns the OpenAPI schema as a JSON serializable dict. + Since Bedrock Agents only support OpenAPI 3.0.0, we convert OpenAPI 3.1.0 schemas + and enforce 3.0.0 compatibility for seamless integration. + + Parameters + ---------- + title: str + The title of the application. + version: str + The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API + openapi_version: str, default = "3.0.0" + The version of the OpenAPI Specification (which the document uses). + summary: str, optional + A short summary of what the application does. + description: str, optional + A verbose explanation of the application behavior. + tags: list[Tag, str], optional + A list of tags used by the specification with additional metadata. + servers: list[Server], optional + An array of Server Objects, which provide connectivity information to a target server. + terms_of_service: str, optional + A URL to the Terms of Service for the API. MUST be in the format of a URL. + contact: Contact, optional + The contact information for the exposed API. + license_info: License, optional + The license information for the exposed API. + security_schemes: dict[str, SecurityScheme]], optional + A declaration of the security schemes available to be used in the specification. + security: list[dict[str, list[str]]], optional + A declaration of which security mechanisms are applied globally across the API. + + Returns + ------- + str + The OpenAPI schema as a JSON serializable dict. + """ + from aws_lambda_powertools.event_handler.openapi.compat import model_json + + schema = super().get_openapi_schema( + title=title, + version=version, + openapi_version=openapi_version, + summary=summary, + description=description, + tags=tags, + servers=servers, + terms_of_service=terms_of_service, + contact=contact, + license_info=license_info, + security_schemes=security_schemes, + security=security, + openapi_extensions=openapi_extensions, + ) + schema.openapi = "3.0.3" + + # Transform OpenAPI 3.1 into 3.0 + def inner(yaml_dict): + if isinstance(yaml_dict, dict): + if "anyOf" in yaml_dict and isinstance((anyOf := yaml_dict["anyOf"]), list): + for i, item in enumerate(anyOf): + if isinstance(item, dict) and item.get("type") == "null": + anyOf.pop(i) + yaml_dict["nullable"] = True + if "examples" in yaml_dict: + examples = yaml_dict["examples"] + del yaml_dict["examples"] + if isinstance(examples, list) and len(examples): + yaml_dict["example"] = examples[0] + for value in yaml_dict.values(): + inner(value) + elif isinstance(yaml_dict, list): + for item in yaml_dict: + inner(item) + + model = json.loads( + model_json( + schema, + by_alias=True, + exclude_none=True, + indent=2, + ), + ) + + inner(model) + + return json.dumps(model) diff --git a/aws_lambda_powertools/event_handler/bedrock_agent_function.py b/aws_lambda_powertools/event_handler/bedrock_agent_function.py new file mode 100644 index 00000000000..d6b96222744 --- /dev/null +++ b/aws_lambda_powertools/event_handler/bedrock_agent_function.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +import inspect +import json +import logging +import warnings +from collections.abc import Callable +from typing import Any, Literal, TypeVar + +from aws_lambda_powertools.utilities.data_classes import BedrockAgentFunctionEvent +from aws_lambda_powertools.warnings import PowertoolsUserWarning + +# Define a generic type for the function +T = TypeVar("T", bound=Callable[..., Any]) + +logger = logging.getLogger(__name__) + + +class BedrockFunctionResponse: + """Response class for Bedrock Agent Functions. + + Parameters + ---------- + body : Any, optional + Response body to be returned to the caller. + session_attributes : dict[str, str] or None, optional + Session attributes to include in the response for maintaining state. + prompt_session_attributes : dict[str, str] or None, optional + Prompt session attributes to include in the response. + knowledge_bases : list[dict[str, Any]] or None, optional + Knowledge bases to include in the response. + response_state : {"FAILURE", "REPROMPT"} or None, optional + Response state indicating if the function failed or needs reprompting. + + Examples + -------- + >>> @app.tool(description="Function that uses session attributes") + >>> def test_function(): + ... return BedrockFunctionResponse( + ... body="Hello", + ... session_attributes={"userId": "123"}, + ... prompt_session_attributes={"lastAction": "login"} + ... ) + + Notes + ----- + The `response_state` parameter can only be set to "FAILURE" or "REPROMPT". + """ + + def __init__( + self, + body: Any = None, + session_attributes: dict[str, str] | None = None, + prompt_session_attributes: dict[str, str] | None = None, + knowledge_bases: list[dict[str, Any]] | None = None, + response_state: Literal["FAILURE", "REPROMPT"] | None = None, + ) -> None: + if response_state and response_state not in ["FAILURE", "REPROMPT"]: + raise ValueError("responseState must be 'FAILURE' or 'REPROMPT'") + + self.body = body + self.session_attributes = session_attributes + self.prompt_session_attributes = prompt_session_attributes + self.knowledge_bases = knowledge_bases + self.response_state = response_state + + +class BedrockFunctionsResponseBuilder: + """ + Bedrock Functions Response Builder. This builds the response dict to be returned by Lambda + when using Bedrock Agent Functions. + """ + + def __init__(self, result: BedrockFunctionResponse | Any) -> None: + self.result = result + + def build(self, event: BedrockAgentFunctionEvent, serializer: Callable) -> dict[str, Any]: + result_obj = self.result + + # Extract attributes from BedrockFunctionResponse or use defaults + body = getattr(result_obj, "body", result_obj) + session_attributes = getattr(result_obj, "session_attributes", None) + prompt_session_attributes = getattr(result_obj, "prompt_session_attributes", None) + knowledge_bases = getattr(result_obj, "knowledge_bases", None) + response_state = getattr(result_obj, "response_state", None) + + # Build base response structure + # Per AWS Bedrock documentation, currently only "TEXT" is supported as the responseBody content type + # https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html + response: dict[str, Any] = { + "messageVersion": "1.0", + "response": { + "actionGroup": event.action_group, + "function": event.function, + "functionResponse": { + "responseBody": {"TEXT": {"body": serializer(body if body is not None else "")}}, + }, + }, + "sessionAttributes": session_attributes or event.session_attributes or {}, + "promptSessionAttributes": prompt_session_attributes or event.prompt_session_attributes or {}, + } + + # Add optional fields when present + if response_state: + response["response"]["functionResponse"]["responseState"] = response_state + + if knowledge_bases: + response["knowledgeBasesConfiguration"] = knowledge_bases + + return response + + +class BedrockAgentFunctionResolver: + """Bedrock Agent Function resolver that handles function definitions + + Examples + -------- + ```python + from aws_lambda_powertools.event_handler import BedrockAgentFunctionResolver + + app = BedrockAgentFunctionResolver() + + @app.tool(name="get_current_time", description="Gets the current UTC time") + def get_current_time(): + from datetime import datetime + return datetime.utcnow().isoformat() + + def lambda_handler(event, context): + return app.resolve(event, context) + ``` + """ + + context: dict + + def __init__(self, serializer: Callable | None = None) -> None: + self._tools: dict[str, dict[str, Any]] = {} + self.current_event: BedrockAgentFunctionEvent | None = None + self.context = {} + self._response_builder_class = BedrockFunctionsResponseBuilder + self.serializer = serializer or json.dumps + + def tool( + self, + name: str | None = None, + description: str | None = None, + ) -> Callable[[T], T]: + """Decorator to register a tool function + + Parameters + ---------- + name : str | None + Custom name for the tool. If not provided, uses the function name + description : str | None + Description of what the tool does + + Returns + ------- + Callable + Decorator function that registers and returns the original function + """ + + def decorator(func: T) -> T: + function_name = name or func.__name__ + + logger.debug(f"Registering {function_name} tool") + + if function_name in self._tools: + warnings.warn( + f"Tool '{function_name}' already registered. Overwriting with new definition.", + PowertoolsUserWarning, + stacklevel=2, + ) + + self._tools[function_name] = { + "function": func, + "description": description, + } + return func + + return decorator + + def resolve(self, event: dict[str, Any], context: Any) -> dict[str, Any]: + """Resolves the function call from Bedrock Agent event""" + try: + self.current_event = BedrockAgentFunctionEvent(event) + return self._resolve() + except KeyError as e: + raise ValueError(f"Missing required field: {str(e)}") from e + + def _resolve(self) -> dict[str, Any]: + """Internal resolution logic""" + if self.current_event is None: + raise ValueError("No event to process") + + function_name = self.current_event.function + + logger.debug(f"Resolving {function_name} tool") + + try: + parameters: dict[str, Any] = {} + # Extract parameters from the event + for param in getattr(self.current_event, "parameters", []): + param_type = getattr(param, "type", None) + if param_type == "string": + parameters[param.name] = str(param.value) + elif param_type == "integer": + try: + parameters[param.name] = int(param.value) + except (ValueError, TypeError): + parameters[param.name] = param.value + elif param_type == "number": + try: + parameters[param.name] = float(param.value) + except (ValueError, TypeError): + parameters[param.name] = param.value + elif param_type == "boolean": + if isinstance(param.value, str): + parameters[param.name] = param.value.lower() == "true" + else: + parameters[param.name] = bool(param.value) + else: # "array" or any other type + parameters[param.name] = param.value + + func = self._tools[function_name]["function"] + # Filter parameters to only include those expected by the function + sig = inspect.signature(func) + valid_params = {name: value for name, value in parameters.items() if name in sig.parameters} + + # Call the function with the filtered parameters + result = func(**valid_params) + + self.clear_context() + + # Build and return the response + return BedrockFunctionsResponseBuilder(result).build(self.current_event, serializer=self.serializer) + except Exception as error: + # Return a formatted error response + logger.error(f"Error processing function: {function_name}", exc_info=True) + error_response = BedrockFunctionResponse(body=f"Error: {error.__class__.__name__}: {str(error)}") + return BedrockFunctionsResponseBuilder(error_response).build(self.current_event, serializer=self.serializer) + + def append_context(self, **additional_context): + """Append key=value data as routing context""" + self.context.update(**additional_context) + + def clear_context(self): + """Resets routing context""" + self.context.clear() diff --git a/aws_lambda_powertools/event_handler/events_appsync/__init__.py b/aws_lambda_powertools/event_handler/events_appsync/__init__.py new file mode 100644 index 00000000000..64387723526 --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/__init__.py @@ -0,0 +1,5 @@ +from aws_lambda_powertools.event_handler.events_appsync.appsync_events import AppSyncEventsResolver + +__all__ = [ + "AppSyncEventsResolver", +] diff --git a/aws_lambda_powertools/event_handler/events_appsync/_registry.py b/aws_lambda_powertools/event_handler/events_appsync/_registry.py new file mode 100644 index 00000000000..8c682327706 --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/_registry.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import logging +import warnings +from typing import TYPE_CHECKING + +from aws_lambda_powertools.event_handler.events_appsync.functions import find_best_route, is_valid_path +from aws_lambda_powertools.warnings import PowertoolsUserWarning + +if TYPE_CHECKING: + from collections.abc import Callable + + from aws_lambda_powertools.event_handler.events_appsync.types import ResolverTypeDef + + +logger = logging.getLogger(__name__) + + +class ResolverEventsRegistry: + def __init__(self, kind_resolver: str): + self.resolvers: dict[str, ResolverTypeDef] = {} + self.kind_resolver = kind_resolver + + def register( + self, + path: str = "/default/*", + aggregate: bool = False, + ) -> Callable | None: + """Registers the resolver for path that includes namespace + channel + + Parameters + ---------- + path : str + Path including namespace + channel + aggregate: bool + A flag indicating whether the batch items should be processed at once or individually. + If True, the resolver will process all items as a single event. + If False (default), the resolver will process each item individually. + + Return + ---------- + Callable + A Callable + """ + + def _register(func) -> Callable | None: + if not is_valid_path(path): + warnings.warn( + f"The path `{path}` registered for `{self.kind_resolver}` is not valid and will be skipped." + f"A path should always have a namespace starting with '/'" + "A path can have multiple namespaces, all separated by '/'." + "Wildcards are allowed only at the end of the path.", + stacklevel=2, + category=PowertoolsUserWarning, + ) + return None + + logger.debug( + f"Adding resolver `{func.__name__}` for path `{path}` and kind_resolver `{self.kind_resolver}`", + ) + self.resolvers[f"{path}"] = { + "func": func, + "aggregate": aggregate, + } + return func + + return _register + + def find_resolver(self, path: str) -> ResolverTypeDef | None: + """Find resolver based on type_name and field_name + + Parameters + ---------- + path : str + Type name + Return + ---------- + dict | None + A dictionary with the resolver and if this is aggregated or not + """ + logger.debug(f"Looking for resolver for path `{path}` and kind_resolver `{self.kind_resolver}`") + return self.resolvers.get(find_best_route(self.resolvers, path)) + + def merge(self, other_registry: ResolverEventsRegistry): + """Update current registry with incoming registry + + Parameters + ---------- + other_registry : ResolverRegistry + Registry to merge from + """ + self.resolvers.update(**other_registry.resolvers) diff --git a/aws_lambda_powertools/event_handler/events_appsync/appsync_events.py b/aws_lambda_powertools/event_handler/events_appsync/appsync_events.py new file mode 100644 index 00000000000..ee03db5c625 --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/appsync_events.py @@ -0,0 +1,422 @@ +from __future__ import annotations + +import asyncio +import logging +import warnings +from typing import TYPE_CHECKING, Any + +from aws_lambda_powertools.event_handler.events_appsync.exceptions import UnauthorizedException +from aws_lambda_powertools.event_handler.events_appsync.router import Router +from aws_lambda_powertools.utilities.data_classes.appsync_resolver_events_event import AppSyncResolverEventsEvent +from aws_lambda_powertools.warnings import PowertoolsUserWarning + +if TYPE_CHECKING: + from collections.abc import Callable + + from aws_lambda_powertools.event_handler.events_appsync.types import ResolverTypeDef + from aws_lambda_powertools.utilities.typing.lambda_context import LambdaContext + + +logger = logging.getLogger(__name__) + + +class AppSyncEventsResolver(Router): + """ + AppSync Events API Resolver for handling publish and subscribe operations. + + This class extends the Router to process AppSync real-time API events, managing + both synchronous and asynchronous resolvers for event publishing and subscribing. + + Attributes + ---------- + context: dict + Dictionary to store context information accessible across resolvers + lambda_context: LambdaContext + Lambda context from the AWS Lambda function + current_event: AppSyncResolverEventsEvent + Current event being processed + + Examples + -------- + Define a simple AppSync events resolver for a chat application: + + >>> from aws_lambda_powertools.event_handler import AppSyncEventsResolver + >>> app = AppSyncEventsResolver() + >>> + >>> # Using aggregate mode to process multiple messages at once + >>> @app.on_publish(channel_path="/default/*", aggregate=True) + >>> def handle_batch_messages(payload): + >>> processed_messages = [] + >>> for message in payload: + >>> # Process each message + >>> processed_messages.append({ + >>> "messageId": f"msg-{message.get('id')}", + >>> "processed": True + >>> }) + >>> return processed_messages + >>> + >>> # Asynchronous resolver + >>> @app.async_on_publish(channel_path="/default/*") + >>> async def handle_async_messages(event): + >>> # Perform async operations (e.g., DB queries, HTTP calls) + >>> await asyncio.sleep(0.1) # Simulate async work + >>> return { + >>> "messageId": f"async-{event.get('id')}", + >>> "processed": True + >>> } + >>> + >>> # Lambda handler + >>> def lambda_handler(event, context): + >>> return events.resolve(event, context) + """ + + def __init__(self): + """Initialize the AppSyncEventsResolver.""" + super().__init__() + self.context = {} # early init as customers might add context before event resolution + self._exception_handlers: dict[type, Callable] = {} + + def __call__( + self, + event: dict | AppSyncResolverEventsEvent, + context: LambdaContext, + ) -> Any: + """ + Implicit lambda handler which internally calls `resolve`. + + Parameters + ---------- + event: dict or AppSyncResolverEventsEvent + The AppSync event to process + context: LambdaContext + The Lambda context + + Returns + ------- + Any + The resolver's response + """ + return self.resolve(event, context) + + def resolve( + self, + event: dict | AppSyncResolverEventsEvent, + context: LambdaContext, + ) -> Any: + """ + Resolves the response based on the provided event and decorator operation. + + Parameters + ---------- + event: dict or AppSyncResolverEventsEvent + The AppSync event to process + context: LambdaContext + The Lambda context + + Returns + ------- + Any + The resolver's response based on the operation type + + Examples + -------- + >>> events = AppSyncEventsResolver() + >>> + >>> # Explicit call to resolve in Lambda handler + >>> def lambda_handler(event, context): + >>> return events.resolve(event, context) + """ + + self._setup_context(event, context) + + if self.current_event.info.operation == "PUBLISH": + response = self._publish_events(payload=self.current_event.events) + else: + response = self._subscribe_events() + + self.clear_context() + + return response + + def _subscribe_events(self) -> Any: + """ + Handle subscribe events. + + Returns + ------- + Any + Any response + """ + channel_path = self.current_event.info.channel_path + logger.debug(f"Processing subscribe events for path {channel_path}") + + resolver = self._subscribe_registry.find_resolver(channel_path) + if resolver: + try: + resolver["func"]() + return None # Must return None in subscribe events + except UnauthorizedException: + raise + except Exception as error: + return {"error": self._format_error_response(error)} + + self._warn_no_resolver("subscribe", channel_path) + return None + + def _publish_events(self, payload: list[dict[str, Any]]) -> list[dict[str, Any]] | dict[str, Any]: + """ + Handle publish events. + + Parameters + ---------- + payload: list[dict[str, Any]] + The events payload to process + + Returns + ------- + list[dict[str, Any]] or dict[str, Any] + Processed events or error response + """ + + channel_path = self.current_event.info.channel_path + + logger.debug(f"Processing publish events for path {channel_path}") + + resolver = self._publish_registry.find_resolver(channel_path) + async_resolver = self._async_publish_registry.find_resolver(channel_path) + + if resolver and async_resolver: + warnings.warn( + f"Both synchronous and asynchronous resolvers found for the same event and field." + f"The synchronous resolver takes precedence. Executing: {resolver['func'].__name__}", + stacklevel=2, + category=PowertoolsUserWarning, + ) + + if resolver: + logger.debug(f"Found sync resolver: {resolver}") + return self._process_publish_event_sync_resolver(resolver) + + if async_resolver: + logger.debug(f"Found async resolver: {async_resolver}") + return asyncio.run(self._call_publish_event_async_resolver(async_resolver)) + + # No resolver found + # Warning and returning AS IS + self._warn_no_resolver("publish", channel_path, return_as_is=True) + return {"events": payload} + + def _process_publish_event_sync_resolver( + self, + resolver: ResolverTypeDef, + ) -> list[dict[str, Any]] | dict[str, Any]: + """ + Process events using a synchronous resolver. + + Parameters + ---------- + resolver : ResolverTypeDef + The resolver to use for processing events + + Returns + ------- + list[dict[str, Any]] or dict[str, Any] + Processed events or error response + + Notes + ----- + If the resolver is configured with aggregate=True, all events are processed + as a batch. Otherwise, each event is processed individually. + """ + + # Checks whether the entire batch should be processed at once + if resolver["aggregate"]: + try: + # Process the entire batch + response = resolver["func"](payload=self.current_event.events) + + if not isinstance(response, list): + warnings.warn( + "Response must be a list when using aggregate, AppSync will drop those events.", + stacklevel=2, + category=PowertoolsUserWarning, + ) + + return {"events": response} + except UnauthorizedException: + raise + except Exception as error: + return {"error": self._format_error_response(error)} + + # By default, we gracefully append `None` for any records that failed processing + results = [] + for idx, event in enumerate(self.current_event.events): + try: + result_return = resolver["func"](payload=event.get("payload")) + results.append({"id": event.get("id"), "payload": result_return}) + except Exception as error: + logger.debug(f"Failed to process event number {idx}") + error_return = {"id": event.get("id"), "error": self._format_error_response(error)} + results.append(error_return) + + return {"events": results} + + async def _call_publish_event_async_resolver( + self, + resolver: ResolverTypeDef, + ) -> list[dict[str, Any]] | dict[str, Any]: + """ + Process events using an asynchronous resolver. + + Parameters + ---------- + resolver: ResolverTypeDef + The async resolver to use for processing events + + Returns + ------- + list[Any] + Processed events or error responses + + Notes + ----- + If the resolver is configured with aggregate=True, all events are processed + as a batch. Otherwise, each event is processed individually and in parallel. + """ + + # Checks whether the entire batch should be processed at once + if resolver["aggregate"]: + try: + # Process the entire batch + response = await resolver["func"](payload=self.current_event.events) + if not isinstance(response, list): + warnings.warn( + "Response must be a list when using aggregate, AppSync will drop those events.", + stacklevel=2, + category=PowertoolsUserWarning, + ) + + return {"events": response} + except UnauthorizedException: + raise + except Exception as error: + return {"error": self._format_error_response(error)} + + response_async: list = [] + + # Prime coroutines + tasks = [resolver["func"](payload=e.get("payload")) for e in self.current_event.events] + + # Aggregate results and exceptions, then filter them out + # Use `None` upon exception for graceful error handling at GraphQL engine level + # + # NOTE: asyncio.gather(return_exceptions=True) catches and includes exceptions in the results + # this will become useful when we support exception handling in AppSync resolver + # Aggregate results and exceptions, then filter them out + results = await asyncio.gather(*tasks, return_exceptions=True) + response_async.extend( + [ + ( + {"id": e.get("id"), "error": self._format_error_response(ret)} + if isinstance(ret, Exception) + else {"id": e.get("id"), "payload": ret} + ) + for e, ret in zip(self.current_event.events, results) + ], + ) + + return {"events": response_async} + + def include_router(self, router: Router) -> None: + """ + Add all resolvers defined in a router to this resolver. + + Parameters + ---------- + router : Router + A router containing resolvers to include + + Examples + -------- + >>> # Create main resolver and a router + >>> app = AppSyncEventsResolver() + >>> router = Router() + >>> + >>> # Define resolvers in the router + >>> @router.publish(path="/chat/message") + >>> def handle_chat_message(payload): + >>> return {"processed": True, "messageId": payload.get("id")} + >>> + >>> # Include the router in the main resolver + >>> app.include_router(chat_router) + >>> + >>> # Now events can handle "/chat/message" channel_path + """ + + # Merge app and router context + logger.debug("Merging router and app context") + self.context.update(**router.context) + + # use pointer to allow context clearance after event is processed e.g., resolve(evt, ctx) + router.context = self.context + + logger.debug("Merging router resolver registries") + self._publish_registry.merge(router._publish_registry) + self._async_publish_registry.merge(router._async_publish_registry) + self._subscribe_registry.merge(router._subscribe_registry) + + def _format_error_response(self, error=None) -> str: + """ + Format error responses consistently. + + Parameters + ---------- + error: Exception or None + The error to format + + Returns + ------- + str + Formatted error message + """ + if isinstance(error, Exception): + return f"{error.__class__.__name__} - {str(error)}" + return "An unknown error occurred" + + def _warn_no_resolver(self, operation_type: str, path: str, return_as_is: bool = False) -> None: + """ + Generate consistent warning messages for missing resolvers. + + Parameters + ---------- + operation_type : str + Type of operation (e.g., "publish", "subscribe") + path : str + The channel path that's missing a resolver + return_as_is : bool, optional + Whether payload will be returned as is, by default False + """ + message = ( + f"No resolvers were found for {operation_type} operations with path {path}" + f"{'. We will return the entire payload as is' if return_as_is else ''}" + ) + warnings.warn(message, stacklevel=3, category=PowertoolsUserWarning) + + def _setup_context(self, event: dict | AppSyncResolverEventsEvent, context: LambdaContext) -> None: + """ + Set up the context and event for processing. + + Parameters + ---------- + event : dict or AppSyncResolverEventsEvent + The AppSync event to process + context : LambdaContext + The Lambda context + """ + self.lambda_context = context + Router.lambda_context = context + + Router.current_event = ( + event if isinstance(event, AppSyncResolverEventsEvent) else AppSyncResolverEventsEvent(event) + ) + self.current_event = Router.current_event diff --git a/aws_lambda_powertools/event_handler/events_appsync/base.py b/aws_lambda_powertools/event_handler/events_appsync/base.py new file mode 100644 index 00000000000..86a1e140d5d --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/base.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Callable + +DEFAULT_ROUTE = "/default/*" + + +class BaseRouter(ABC): + """Abstract base class for Router (resolvers)""" + + @abstractmethod + def on_publish( + self, + path: str = DEFAULT_ROUTE, + aggregate: bool = True, + ) -> Callable: + raise NotImplementedError + + @abstractmethod + def async_on_publish( + self, + path: str = DEFAULT_ROUTE, + aggregate: bool = True, + ) -> Callable: + raise NotImplementedError + + @abstractmethod + def on_subscribe( + self, + path: str = DEFAULT_ROUTE, + ) -> Callable: + raise NotImplementedError + + def append_context(self, **additional_context) -> None: + """ + Appends context information available under any route. + + Parameters + ----------- + **additional_context: dict + Additional context key-value pairs to append. + """ + raise NotImplementedError diff --git a/aws_lambda_powertools/event_handler/events_appsync/exceptions.py b/aws_lambda_powertools/event_handler/events_appsync/exceptions.py new file mode 100644 index 00000000000..5093c68c603 --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/exceptions.py @@ -0,0 +1,25 @@ +from __future__ import annotations + + +class UnauthorizedException(Exception): + """ + Error to be thrown to communicate the subscription is unauthorized. + + When this error is raised, the client will receive a 40x error code + and the subscription will be closed. + + Attributes: + message (str): The error message describing the unauthorized access. + """ + + def __init__(self, message: str | None = None, *args, **kwargs): + """ + Initialize the UnauthorizedException. + + Args: + message (str): A descriptive error message. + *args: Variable positional arguments. + **kwargs: Variable keyword arguments. + """ + super().__init__(message, *args, **kwargs) + self.name = "UnauthorizedException" diff --git a/aws_lambda_powertools/event_handler/events_appsync/functions.py b/aws_lambda_powertools/event_handler/events_appsync/functions.py new file mode 100644 index 00000000000..0d7ddf2518f --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/functions.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +import re +from functools import lru_cache +from typing import Any + +PATH_REGEX = re.compile(r"^\/([^\/\*]+)(\/[^\/\*]+)*(\/\*)?$") + + +def is_valid_path(path: str) -> bool: + """ + Checks if a given path is valid based on specific rules. + + Parameters + ---------- + path: str + The path to validate + + Returns: + -------- + bool: + True if the path is valid, False otherwise + + Examples: + >>> is_valid_path('/*') + True + >>> is_valid_path('/users') + True + >>> is_valid_path('/users/profile') + True + >>> is_valid_path('/users/*/details') + False + >>> is_valid_path('/users/*') + True + >>> is_valid_path('users') + False + """ + return True if path == "/*" else bool(PATH_REGEX.fullmatch(path)) + + +def find_best_route(routes: dict[str, Any], path: str): + """ + Find the most specific matching route for a given path. + + Examples of matches: + Route: /default/v1/* Path: /default/v1/users -> MATCH + Route: /default/v1/* Path: /default/v1/users/students -> MATCH + Route: /default/v1/users/* Path: /default/v1/users/123 -> MATCH (this wins over /default/v1/*) + Route: /* Path: /anything/here -> MATCH (lowest priority) + + Parameters + ---------- + routes: dict[str, Any] + Dictionary containing routes and their handlers + Format: { + 'resolvers': { + '/path/*': {'func': callable, 'aggregate': bool}, + '/path/specific/*': {'func': callable, 'aggregate': bool} + } + } + path: str + Actual path to match (e.g., '/default/v1/users') + + Returns + ------- + str: Most specific matching route or None if no match + """ + + @lru_cache(maxsize=1024) + def pattern_to_regex(route): + """ + Convert a route pattern to a regex pattern with caching. + Examples: + /default/v1/* -> ^/default/v1/[^/]+$ + /default/v1/users/* -> ^/default/v1/users/.*$ + + Parameters + ---------- + route: str + Route pattern with wildcards + + Returns + ------- + Pattern: + Compiled regex pattern + """ + # Escape special regex chars but convert * to regex pattern + pattern = re.escape(route).replace("\\*", "[^/]+") + + # If pattern ends with [^/]+, replace with .* for multi-segment match + if pattern.endswith("[^/]+"): + pattern = pattern[:-6] + ".*" + + # Compile and return the regex pattern + return re.compile(f"^{pattern}$") + + # Find all matching routes + matches = [route for route in routes.keys() if pattern_to_regex(route).match(path)] + + # Return the most specific route (longest length minus wildcards) + # Examples of specificity: + # - '/default/v1/users' -> score: 14 (len=14, wildcards=0) + # - '/default/v1/users/*' -> score: 14 (len=15, wildcards=1) + # - '/default/v1/*' -> score: 8 (len=9, wildcards=1) + # - '/*' -> score: 0 (len=2, wildcards=1) + return max(matches, key=lambda x: len(x) - x.count("*"), default=None) diff --git a/aws_lambda_powertools/event_handler/events_appsync/router.py b/aws_lambda_powertools/event_handler/events_appsync/router.py new file mode 100644 index 00000000000..167403e30fe --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/router.py @@ -0,0 +1,199 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from aws_lambda_powertools.event_handler.events_appsync._registry import ResolverEventsRegistry +from aws_lambda_powertools.event_handler.events_appsync.base import DEFAULT_ROUTE, BaseRouter + +if TYPE_CHECKING: + from collections.abc import Callable + + from aws_lambda_powertools.utilities.data_classes.appsync_resolver_events_event import AppSyncResolverEventsEvent + from aws_lambda_powertools.utilities.typing.lambda_context import LambdaContext + + +class Router(BaseRouter): + """ + Router for AppSync real-time API event handling. + + This class provides decorators to register resolver functions for publish and subscribe + operations in AppSync real-time APIs. + + Parameters + ---------- + context : dict + Dictionary to store context information accessible across resolvers + current_event : AppSyncResolverEventsEvent + Current event being processed + lambda_context : LambdaContext + Lambda context from the AWS Lambda function + + Examples + -------- + Create a router and define resolvers: + + >>> chat_router = Router() + >>> + >>> # Register a resolver for publish operations + >>> @chat_router.on_publish(path="/chat/message") + >>> def handle_message(payload): + >>> # Process message + >>> return {"success": True, "messageId": payload.get("id")} + >>> + >>> # Register an async resolver for publish operations + >>> @chat_router.async_on_publish(path="/chat/typing") + >>> async def handle_typing(event): + >>> # Process typing indicator + >>> await some_async_operation() + >>> return {"processed": True} + >>> + >>> # Register a resolver for subscribe operations + >>> @chat_router.on_subscribe(path="/chat/room/*") + >>> def handle_subscribe(event): + >>> # Handle subscription setup + >>> return {"allowed": True} + """ + + context: dict + current_event: AppSyncResolverEventsEvent + lambda_context: LambdaContext + + def __init__(self): + """ + Initialize a new Router instance. + + Sets up empty context and registry containers for different types of resolvers. + """ + self.context = {} # early init as customers might add context before event resolution + self._publish_registry = ResolverEventsRegistry(kind_resolver="on_publish") + self._async_publish_registry = ResolverEventsRegistry(kind_resolver="async_on_publish") + self._subscribe_registry = ResolverEventsRegistry(kind_resolver="on_subscribe") + + def on_publish( + self, + path: str = DEFAULT_ROUTE, + aggregate: bool = False, + ) -> Callable: + """ + Register a resolver function for publish operations. + + Parameters + ---------- + path : str, optional + The channel path pattern to match for this resolver, by default "/default/*" + aggregate : bool, optional + Whether to process events in aggregate (batch) mode, by default False + + Returns + ------- + Callable + Decorator function that registers the resolver + + Examples + -------- + >>> router = Router() + >>> + >>> # Basic usage + >>> @router.on_publish(path="/notifications/new") + >>> def handle_notification(payload): + >>> # Process a single notification + >>> return {"processed": True, "notificationId": payload.get("id")} + >>> + >>> # Aggregate mode for batch processing + >>> @router.on_publish(path="/notifications/batch", aggregate=True) + >>> def handle_batch_notifications(payload): + >>> # Process multiple notifications at once + >>> results = [] + >>> for item in payload: + >>> # Process each item + >>> results.append({"processed": True, "id": item.get("id")}) + >>> return results + """ + return self._publish_registry.register(path=path, aggregate=aggregate) + + def async_on_publish( + self, + path: str = DEFAULT_ROUTE, + aggregate: bool = False, + ) -> Callable: + """ + Register an asynchronous resolver function for publish operations. + + Parameters + ---------- + path : str, optional + The channel path pattern to match for this resolver, by default "/default/*" + aggregate : bool, optional + Whether to process events in aggregate (batch) mode, by default False + + Returns + ------- + Callable + Decorator function that registers the async resolver + + Examples + -------- + >>> router = Router() + >>> + >>> # Basic async usage + >>> @router.async_on_publish(path="/messages/send") + >>> async def handle_message(event): + >>> # Perform async operations + >>> result = await database.save_message(event) + >>> return {"saved": True, "messageId": result.id} + >>> + >>> # Aggregate mode for batch processing + >>> @router.async_on_publish(path="/messages/batch", aggregate=True) + >>> async def handle_batch_messages(events): + >>> # Process multiple messages asynchronously + >>> tasks = [database.save_message(e) for e in events] + >>> results = await asyncio.gather(*tasks) + >>> return [{"saved": True, "id": r.id} for r in results] + """ + return self._async_publish_registry.register(path=path, aggregate=aggregate) + + def on_subscribe( + self, + path: str = DEFAULT_ROUTE, + ) -> Callable: + """ + Register a resolver function for subscribe operations. + + Parameters + ---------- + path : str, optional + The channel path pattern to match for this resolver, by default "/default/*" + + Returns + ------- + Callable + Decorator function that registers the resolver + + Examples + -------- + >>> router = Router() + >>> + >>> # Handle subscription request + >>> @router.on_subscribe(path="/chat/room/*") + >>> def authorize_subscription(event): + >>> # Verify if the client can subscribe to this room + >>> room_id = event.info.channel_path.split('/')[-1] + >>> user_id = event.identity.username + >>> + >>> # Check if user is allowed in this room + >>> is_allowed = check_permission(user_id, room_id) + >>> + >>> return { + >>> "allowed": is_allowed, + >>> "roomId": room_id + >>> } + """ + return self._subscribe_registry.register(path=path) + + def append_context(self, **additional_context): + """Append key=value data as routing context""" + self.context.update(**additional_context) + + def clear_context(self): + """Resets routing context""" + self.context.clear() diff --git a/aws_lambda_powertools/event_handler/events_appsync/types.py b/aws_lambda_powertools/event_handler/events_appsync/types.py new file mode 100644 index 00000000000..708e8df8a8c --- /dev/null +++ b/aws_lambda_powertools/event_handler/events_appsync/types.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, TypedDict + +if TYPE_CHECKING: + from collections.abc import Callable + + +class ResolverTypeDef(TypedDict): + """ + Type definition for resolver dictionary + Parameters + ---------- + func: Callable[..., Any] + Resolver function + aggregate: bool + Aggregation flag or method + """ + + func: Callable[..., Any] + aggregate: bool diff --git a/aws_lambda_powertools/event_handler/exception_handling.py b/aws_lambda_powertools/event_handler/exception_handling.py new file mode 100644 index 00000000000..acd8eb95bc6 --- /dev/null +++ b/aws_lambda_powertools/event_handler/exception_handling.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Mapping + +if TYPE_CHECKING: + from collections.abc import Callable + + +class ExceptionHandlerManager: + """ + A class to manage exception handlers for different exception types. + This class allows registering handler functions for specific exception types + and looking up the appropriate handler when an exception occurs. + Example usage: + ------------- + handler_manager = ExceptionHandlerManager() + @handler_manager.exception_handler(ValueError) + def handle_value_error(e): + print(f"Handling ValueError: {e}") + return "Error handled" + # To handle multiple exception types with the same handler: + @handler_manager.exception_handler([KeyError, TypeError]) + def handle_multiple_errors(e): + print(f"Handling {type(e).__name__}: {e}") + return "Multiple error types handled" + # To find and execute a handler: + try: + # some code that might raise an exception + raise ValueError("Invalid value") + except Exception as e: + handler = handler_manager.lookup_exception_handler(type(e)) + if handler: + result = handler(e) + """ + + def __init__(self): + """Initialize an empty dictionary to store exception handlers.""" + self._exception_handlers: dict[type[Exception], Callable] = {} + + def exception_handler(self, exc_class: type[Exception] | list[type[Exception]]): + """ + A decorator function that registers a handler for one or more exception types. + Parameters + ---------- + exc_class : type[Exception] | list[type[Exception]] + A single exception type or a list of exception types. + Returns + ------- + Callable + A decorator function that registers the exception handler. + """ + + def register_exception_handler(func: Callable): + if isinstance(exc_class, list): + for exp in exc_class: + self._exception_handlers[exp] = func + else: + self._exception_handlers[exc_class] = func + return func + + return register_exception_handler + + def lookup_exception_handler(self, exp_type: type) -> Callable | None: + """ + Looks up the registered exception handler for the given exception type or its base classes. + Parameters + ---------- + exp_type : type + The exception type to look up the handler for. + Returns + ------- + Callable | None + The registered exception handler function if found, otherwise None. + """ + for cls in exp_type.__mro__: + if cls in self._exception_handlers: + return self._exception_handlers[cls] + return None + + def update_exception_handlers(self, handlers: Mapping[type[Exception], Callable]) -> None: + """ + Updates the exception handlers dictionary with new handler mappings. + This method allows bulk updates of exception handlers by providing a dictionary + mapping exception types to handler functions. + Parameters + ---------- + handlers : Mapping[Type[Exception], Callable] + A dictionary mapping exception types to handler functions. + Example + ------- + >>> def handle_value_error(e): + ... print(f"Value error: {e}") + ... + >>> def handle_key_error(e): + ... print(f"Key error: {e}") + ... + >>> handler_manager.update_exception_handlers({ + ... ValueError: handle_value_error, + ... KeyError: handle_key_error + ... }) + """ + self._exception_handlers.update(handlers) + + def get_registered_handlers(self) -> dict[type[Exception], Callable]: + """ + Returns all registered exception handlers. + Returns + ------- + Dict[Type[Exception], Callable] + A dictionary mapping exception types to their handler functions. + """ + return self._exception_handlers.copy() + + def clear_handlers(self) -> None: + """ + Clears all registered exception handlers. + """ + self._exception_handlers.clear() diff --git a/aws_lambda_powertools/event_handler/exceptions.py b/aws_lambda_powertools/event_handler/exceptions.py index 4a2838275b1..e524d8a0eae 100644 --- a/aws_lambda_powertools/event_handler/exceptions.py +++ b/aws_lambda_powertools/event_handler/exceptions.py @@ -2,7 +2,7 @@ class ServiceError(Exception): - """API Gateway and ALB HTTP Service Error""" + """Powertools class HTTP Service Error""" def __init__(self, status_code: int, msg: str): """ @@ -18,28 +18,56 @@ def __init__(self, status_code: int, msg: str): class BadRequestError(ServiceError): - """API Gateway and ALB Bad Request Error (400)""" + """Powertools class Bad Request Error (400)""" def __init__(self, msg: str): super().__init__(HTTPStatus.BAD_REQUEST, msg) class UnauthorizedError(ServiceError): - """API Gateway and ALB Unauthorized Error (401)""" + """Powertools class Unauthorized Error (401)""" def __init__(self, msg: str): super().__init__(HTTPStatus.UNAUTHORIZED, msg) +class ForbiddenError(ServiceError): + """Powertools class Forbidden Error (403)""" + + def __init__(self, msg: str): + super().__init__(HTTPStatus.FORBIDDEN, msg) + + class NotFoundError(ServiceError): - """API Gateway and ALB Not Found Error (404)""" + """Powertools class Not Found Error (404)""" def __init__(self, msg: str = "Not found"): super().__init__(HTTPStatus.NOT_FOUND, msg) +class RequestTimeoutError(ServiceError): + """Powertools class Request Timeout Error (408)""" + + def __init__(self, msg: str): + super().__init__(HTTPStatus.REQUEST_TIMEOUT, msg) + + +class RequestEntityTooLargeError(ServiceError): + """Powertools class Request Entity Too Large Error (413)""" + + def __init__(self, msg: str): + super().__init__(HTTPStatus.REQUEST_ENTITY_TOO_LARGE, msg) + + class InternalServerError(ServiceError): - """API Gateway and ALB Not Found Internal Server Error (500)""" + """Powertools class Internal Server Error (500)""" def __init__(self, message: str): super().__init__(HTTPStatus.INTERNAL_SERVER_ERROR, message) + + +class ServiceUnavailableError(ServiceError): + """Powertools class Service Unavailable Error (503)""" + + def __init__(self, msg: str): + super().__init__(HTTPStatus.SERVICE_UNAVAILABLE, msg) diff --git a/tests/functional/feature_flags/__init__.py b/aws_lambda_powertools/event_handler/graphql_appsync/__init__.py similarity index 100% rename from tests/functional/feature_flags/__init__.py rename to aws_lambda_powertools/event_handler/graphql_appsync/__init__.py diff --git a/aws_lambda_powertools/event_handler/graphql_appsync/_registry.py b/aws_lambda_powertools/event_handler/graphql_appsync/_registry.py new file mode 100644 index 00000000000..dc88d904c25 --- /dev/null +++ b/aws_lambda_powertools/event_handler/graphql_appsync/_registry.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from collections.abc import Callable + +logger = logging.getLogger(__name__) + + +class ResolverRegistry: + def __init__(self): + self.resolvers: dict[str, dict[str, Any]] = {} + + def register( + self, + type_name: str = "*", + field_name: str | None = None, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> Callable: + """Registers the resolver for field_name + + Parameters + ---------- + type_name : str + Type name + field_name : str + Field name + raise_on_error: bool + A flag indicating whether to raise an error when processing batches + with failed items. Defaults to False, which means errors are handled without raising exceptions. + aggregate: bool + A flag indicating whether the batch items should be processed at once or individually. + If True (default), the batch resolver will process all items in the batch as a single event. + If False, the batch resolver will process each item in the batch individually. + + Return + ---------- + Callable + A Callable + """ + + def _register(func) -> Callable: + logger.debug(f"Adding resolver `{func.__name__}` for field `{type_name}.{field_name}`") + self.resolvers[f"{type_name}.{field_name}"] = { + "func": func, + "raise_on_error": raise_on_error, + "aggregate": aggregate, + } + return func + + return _register + + def find_resolver(self, type_name: str, field_name: str) -> dict | None: + """Find resolver based on type_name and field_name + + Parameters + ---------- + type_name : str + Type name + field_name : str + Field name + Return + ---------- + Optional[Dict] + A dictionary with the resolver and if raise exception on error + """ + logger.debug(f"Looking for resolver for type={type_name}, field={field_name}.") + return self.resolvers.get(f"{type_name}.{field_name}", self.resolvers.get(f"*.{field_name}")) + + def merge(self, other_registry: ResolverRegistry): + """Update current registry with incoming registry + + Parameters + ---------- + other_registry : ResolverRegistry + Registry to merge from + """ + self.resolvers.update(**other_registry.resolvers) diff --git a/aws_lambda_powertools/event_handler/graphql_appsync/base.py b/aws_lambda_powertools/event_handler/graphql_appsync/base.py new file mode 100644 index 00000000000..ea03c44a3b0 --- /dev/null +++ b/aws_lambda_powertools/event_handler/graphql_appsync/base.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Callable + + +class BaseRouter(ABC): + """Abstract base class for Router (resolvers)""" + + @abstractmethod + def resolver(self, type_name: str = "*", field_name: str | None = None) -> Callable: + """ + Retrieve a resolver function for a specific type and field. + + Parameters + ----------- + type_name: str + The name of the type. + field_name: str, optional + The name of the field (default is None). + + Examples + -------- + ```python + from typing import Optional + + from aws_lambda_powertools.event_handler import AppSyncResolver + from aws_lambda_powertools.utilities.data_classes import AppSyncResolverEvent + from aws_lambda_powertools.utilities.typing import LambdaContext + + app = AppSyncResolver() + + @app.resolver(type_name="Query", field_name="getPost") + def related_posts(event: AppSyncResolverEvent) -> Optional[list]: + return {"success": "ok"} + + def lambda_handler(event, context: LambdaContext) -> dict: + return app.resolve(event, context) + ``` + + Returns + ------- + Callable + The resolver function. + """ + raise NotImplementedError + + @abstractmethod + def batch_resolver( + self, + type_name: str = "*", + field_name: str | None = None, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> Callable: + """ + Retrieve a batch resolver function for a specific type and field. + + Parameters + ----------- + type_name: str + The name of the type. + field_name: str, optional + The name of the field (default is None). + raise_on_error: bool + A flag indicating whether to raise an error when processing batches + with failed items. Defaults to False, which means errors are handled without raising exceptions. + aggregate: bool + A flag indicating whether the batch items should be processed at once or individually. + If True (default), the batch resolver will process all items in the batch as a single event. + If False, the batch resolver will process each item in the batch individually. + + Examples + -------- + ```python + from typing import Optional + + from aws_lambda_powertools.event_handler import AppSyncResolver + from aws_lambda_powertools.utilities.data_classes import AppSyncResolverEvent + from aws_lambda_powertools.utilities.typing import LambdaContext + + app = AppSyncResolver() + + @app.batch_resolver(type_name="Query", field_name="getPost") + def related_posts(event: AppSyncResolverEvent, id) -> Optional[list]: + return {"post_id": id} + + def lambda_handler(event, context: LambdaContext) -> dict: + return app.resolve(event, context) + ``` + + Returns + ------- + Callable + The batch resolver function. + """ + raise NotImplementedError + + @abstractmethod + def async_batch_resolver( + self, + type_name: str = "*", + field_name: str | None = None, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> Callable: + """ + Retrieve a batch resolver function for a specific type and field and runs async. + + Parameters + ----------- + type_name: str + The name of the type. + field_name: str, optional + The name of the field (default is None). + raise_on_error: bool + A flag indicating whether to raise an error when processing batches + with failed items. Defaults to False, which means errors are handled without raising exceptions. + aggregate: bool + A flag indicating whether the batch items should be processed at once or individually. + If True (default), the batch resolver will process all items in the batch as a single event. + If False, the batch resolver will process each item in the batch individually. + + Examples + -------- + ```python + from typing import Optional + + from aws_lambda_powertools.event_handler import AppSyncResolver + from aws_lambda_powertools.utilities.data_classes import AppSyncResolverEvent + from aws_lambda_powertools.utilities.typing import LambdaContext + + app = AppSyncResolver() + + @app.async_batch_resolver(type_name="Query", field_name="getPost") + async def related_posts(event: AppSyncResolverEvent, id) -> Optional[list]: + return {"post_id": id} + + def lambda_handler(event, context: LambdaContext) -> dict: + return app.resolve(event, context) + ``` + + Returns + ------- + Callable + The batch resolver function. + """ + raise NotImplementedError + + @abstractmethod + def append_context(self, **additional_context) -> None: + """ + Appends context information available under any route. + + Parameters + ----------- + **additional_context: dict + Additional context key-value pairs to append. + """ + raise NotImplementedError diff --git a/aws_lambda_powertools/event_handler/graphql_appsync/exceptions.py b/aws_lambda_powertools/event_handler/graphql_appsync/exceptions.py new file mode 100644 index 00000000000..f98a75b6f17 --- /dev/null +++ b/aws_lambda_powertools/event_handler/graphql_appsync/exceptions.py @@ -0,0 +1,10 @@ +class ResolverNotFoundError(Exception): + """ + When a resolver is not found during a lookup. + """ + + +class InvalidBatchResponse(Exception): + """ + When a batch response something different from a List + """ diff --git a/aws_lambda_powertools/event_handler/graphql_appsync/router.py b/aws_lambda_powertools/event_handler/graphql_appsync/router.py new file mode 100644 index 00000000000..b05e6f276f5 --- /dev/null +++ b/aws_lambda_powertools/event_handler/graphql_appsync/router.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from aws_lambda_powertools.event_handler.graphql_appsync._registry import ResolverRegistry +from aws_lambda_powertools.event_handler.graphql_appsync.base import BaseRouter + +if TYPE_CHECKING: + from collections.abc import Callable + + from aws_lambda_powertools.utilities.data_classes.appsync_resolver_event import AppSyncResolverEvent + from aws_lambda_powertools.utilities.typing.lambda_context import LambdaContext + + +class Router(BaseRouter): + context: dict + current_batch_event: list[AppSyncResolverEvent] = [] + current_event: AppSyncResolverEvent | None = None + lambda_context: LambdaContext | None = None + + def __init__(self): + self.context = {} # early init as customers might add context before event resolution + self._resolver_registry = ResolverRegistry() + self._batch_resolver_registry = ResolverRegistry() + self._async_batch_resolver_registry = ResolverRegistry() + + def resolver(self, type_name: str = "*", field_name: str | None = None) -> Callable: + return self._resolver_registry.register(field_name=field_name, type_name=type_name) + + def batch_resolver( + self, + type_name: str = "*", + field_name: str | None = None, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> Callable: + return self._batch_resolver_registry.register( + field_name=field_name, + type_name=type_name, + raise_on_error=raise_on_error, + aggregate=aggregate, + ) + + def async_batch_resolver( + self, + type_name: str = "*", + field_name: str | None = None, + raise_on_error: bool = False, + aggregate: bool = True, + ) -> Callable: + return self._async_batch_resolver_registry.register( + field_name=field_name, + type_name=type_name, + raise_on_error=raise_on_error, + aggregate=aggregate, + ) + + def append_context(self, **additional_context): + """Append key=value data as routing context""" + self.context.update(**additional_context) + + def clear_context(self): + """Resets routing context""" + self.context.clear() diff --git a/aws_lambda_powertools/event_handler/lambda_function_url.py b/aws_lambda_powertools/event_handler/lambda_function_url.py new file mode 100644 index 00000000000..279899b645e --- /dev/null +++ b/aws_lambda_powertools/event_handler/lambda_function_url.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Pattern + +from aws_lambda_powertools.event_handler.api_gateway import ( + ApiGatewayResolver, + ProxyEventType, +) + +if TYPE_CHECKING: + from collections.abc import Callable + from http import HTTPStatus + + from aws_lambda_powertools.event_handler import CORSConfig + from aws_lambda_powertools.utilities.data_classes import LambdaFunctionUrlEvent + + +class LambdaFunctionUrlResolver(ApiGatewayResolver): + """AWS Lambda Function URL resolver + + Notes: + ----- + Lambda Function URL follows the API Gateway HTTP APIs Payload Format Version 2.0. + + Documentation: + - https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html + - https://docs.aws.amazon.com/lambda/latest/dg/urls-invocation.html#urls-payloads + + Examples + -------- + Simple example integrating with Tracer + + ```python + from aws_lambda_powertools import Tracer + from aws_lambda_powertools.event_handler import LambdaFunctionUrlResolver + + tracer = Tracer() + app = LambdaFunctionUrlResolver() + + @app.get("/get-call") + def simple_get(): + return {"message": "Foo"} + + @app.post("/post-call") + def simple_post(): + post_data: dict = app.current_event.json_body + return {"message": post_data} + + @tracer.capture_lambda_handler + def lambda_handler(event, context): + return app.resolve(event, context) + """ + + current_event: LambdaFunctionUrlEvent + + def __init__( + self, + cors: CORSConfig | None = None, + debug: bool | None = None, + serializer: Callable[[dict], str] | None = None, + strip_prefixes: list[str | Pattern] | None = None, + enable_validation: bool = False, + response_validation_error_http_code: HTTPStatus | int | None = None, + json_body_deserializer: Callable[[str], dict] | None = None, + ): + super().__init__( + ProxyEventType.LambdaFunctionUrlEvent, + cors, + debug, + serializer, + strip_prefixes, + enable_validation, + response_validation_error_http_code, + json_body_deserializer=json_body_deserializer, + ) + + def _get_base_path(self) -> str: + stage = self.current_event.request_context.stage + if stage and stage != "$default" and self.current_event.request_context.http.method.startswith(f"/{stage}"): + return f"/{stage}" + return "" diff --git a/aws_lambda_powertools/event_handler/middlewares/__init__.py b/aws_lambda_powertools/event_handler/middlewares/__init__.py new file mode 100644 index 00000000000..068ce9c04b7 --- /dev/null +++ b/aws_lambda_powertools/event_handler/middlewares/__init__.py @@ -0,0 +1,3 @@ +from aws_lambda_powertools.event_handler.middlewares.base import BaseMiddlewareHandler, NextMiddleware + +__all__ = ["BaseMiddlewareHandler", "NextMiddleware"] diff --git a/aws_lambda_powertools/event_handler/middlewares/base.py b/aws_lambda_powertools/event_handler/middlewares/base.py new file mode 100644 index 00000000000..5b4f82b405f --- /dev/null +++ b/aws_lambda_powertools/event_handler/middlewares/base.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Generic, Protocol + +from aws_lambda_powertools.event_handler.types import EventHandlerInstance + +if TYPE_CHECKING: + from aws_lambda_powertools.event_handler.api_gateway import Response + + +class NextMiddleware(Protocol): + def __call__(self, app: EventHandlerInstance) -> Response: + """Protocol for callback regardless of next_middleware(app), get_response(app) etc""" + ... + + def __name__(self) -> str: # noqa A003 + """Protocol for name of the Middleware""" + ... + + +class BaseMiddlewareHandler(Generic[EventHandlerInstance], ABC): + """Base implementation for Middlewares to run code before and after in a chain. + + + This is the middleware handler function where middleware logic is implemented. + The next middleware handler is represented by `next_middleware`, returning a Response object. + + Example + -------- + + **Correlation ID Middleware** + + ```python + import requests + + from aws_lambda_powertools import Logger + from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response + from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware + + app = APIGatewayRestResolver() + logger = Logger() + + + class CorrelationIdMiddleware(BaseMiddlewareHandler): + def __init__(self, header: str): + super().__init__() + self.header = header + + def handler(self, app: APIGatewayRestResolver, next_middleware: NextMiddleware) -> Response: + # BEFORE logic + request_id = app.current_event.request_context.request_id + correlation_id = app.current_event.headers.get(self.header, request_id) + + # Call next middleware or route handler ('/todos') + response = next_middleware(app) + + # AFTER logic + response.headers[self.header] = correlation_id + + return response + + + @app.get("/todos", middlewares=[CorrelationIdMiddleware(header="x-correlation-id")]) + def get_todos(): + todos: requests.Response = requests.get("https://jsonplaceholder.typicode.com/todos") + todos.raise_for_status() + + # for brevity, we'll limit to the first 10 only + return {"todos": todos.json()[:10]} + + + @logger.inject_lambda_context + def lambda_handler(event, context): + return app.resolve(event, context) + + ``` + + """ + + @abstractmethod + def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response: + """ + The Middleware Handler + + Parameters + ---------- + app: EventHandlerInstance + An instance of an Event Handler that implements ApiGatewayResolver + next_middleware: NextMiddleware + The next middleware handler in the chain + + Returns + ------- + Response + The response from the next middleware handler in the chain + + """ + raise NotImplementedError() + + @property + def __name__(self) -> str: # noqa A003 + return str(self.__class__.__name__) + + def __call__(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response: + """ + The Middleware handler function. + + Parameters + ---------- + app: ApiGatewayResolver + An instance of an Event Handler that implements ApiGatewayResolver + next_middleware: NextMiddleware + The next middleware handler in the chain + + Returns + ------- + Response + The response from the next middleware handler in the chain + """ + return self.handler(app, next_middleware) diff --git a/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py b/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py new file mode 100644 index 00000000000..63baf9fe644 --- /dev/null +++ b/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py @@ -0,0 +1,449 @@ +from __future__ import annotations + +import dataclasses +import json +import logging +from copy import deepcopy +from typing import TYPE_CHECKING, Any, Callable, Mapping, MutableMapping, Sequence + +from pydantic import BaseModel + +from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler +from aws_lambda_powertools.event_handler.openapi.compat import ( + _model_dump, + _normalize_errors, + _regenerate_error_with_loc, + get_missing_field_error, +) +from aws_lambda_powertools.event_handler.openapi.dependant import is_scalar_field +from aws_lambda_powertools.event_handler.openapi.encoders import jsonable_encoder +from aws_lambda_powertools.event_handler.openapi.exceptions import RequestValidationError, ResponseValidationError +from aws_lambda_powertools.event_handler.openapi.params import Param + +if TYPE_CHECKING: + from aws_lambda_powertools.event_handler import Response + from aws_lambda_powertools.event_handler.api_gateway import Route + from aws_lambda_powertools.event_handler.middlewares import NextMiddleware + from aws_lambda_powertools.event_handler.openapi.compat import ModelField + from aws_lambda_powertools.event_handler.openapi.types import IncEx + from aws_lambda_powertools.event_handler.types import EventHandlerInstance + +logger = logging.getLogger(__name__) + + +class OpenAPIValidationMiddleware(BaseMiddlewareHandler): + """ + OpenAPIValidationMiddleware is a middleware that validates the request against the OpenAPI schema defined by the + Lambda handler. It also validates the response against the OpenAPI schema defined by the Lambda handler. It + should not be used directly, but rather through the `enable_validation` parameter of the `ApiGatewayResolver`. + + Example + -------- + + ```python + from pydantic import BaseModel + + from aws_lambda_powertools.event_handler.api_gateway import ( + APIGatewayRestResolver, + ) + + class Todo(BaseModel): + name: str + + app = APIGatewayRestResolver(enable_validation=True) + + @app.get("/todos") + def get_todos(): list[Todo]: + return [Todo(name="hello world")] + ``` + """ + + def __init__( + self, + validation_serializer: Callable[[Any], str] | None = None, + has_response_validation_error: bool = False, + ): + """ + Initialize the OpenAPIValidationMiddleware. + + Parameters + ---------- + validation_serializer : Callable, optional + Optional serializer to use when serializing the response for validation. + Use it when you have a custom type that cannot be serialized by the default jsonable_encoder. + + has_response_validation_error: bool, optional + Optional flag used to distinguish between payload and validation errors. + By setting this flag to True, ResponseValidationError will be raised if response could not be validated. + """ + self._validation_serializer = validation_serializer + self._has_response_validation_error = has_response_validation_error + + def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response: + logger.debug("OpenAPIValidationMiddleware handler") + + route: Route = app.context["_route"] + + values: dict[str, Any] = {} + errors: list[Any] = [] + + # Process path values, which can be found on the route_args + path_values, path_errors = _request_params_to_args( + route.dependant.path_params, + app.context["_route_args"], + ) + + # Normalize query values before validate this + query_string = _normalize_multi_query_string_with_param( + app.current_event.resolved_query_string_parameters, + route.dependant.query_params, + ) + + # Process query values + query_values, query_errors = _request_params_to_args( + route.dependant.query_params, + query_string, + ) + + # Normalize header values before validate this + headers = _normalize_multi_header_values_with_param( + app.current_event.resolved_headers_field, + route.dependant.header_params, + ) + + # Process header values + header_values, header_errors = _request_params_to_args( + route.dependant.header_params, + headers, + ) + + values.update(path_values) + values.update(query_values) + values.update(header_values) + errors += path_errors + query_errors + header_errors + + # Process the request body, if it exists + if route.dependant.body_params: + (body_values, body_errors) = _request_body_to_args( + required_params=route.dependant.body_params, + received_body=self._get_body(app), + ) + values.update(body_values) + errors.extend(body_errors) + + if errors: + # Raise the validation errors + raise RequestValidationError(_normalize_errors(errors)) + else: + # Re-write the route_args with the validated values, and call the next middleware + app.context["_route_args"] = values + + # Call the handler by calling the next middleware + response = next_middleware(app) + + # Process the response + return self._handle_response(route=route, response=response) + + def _handle_response(self, *, route: Route, response: Response): + # Process the response body if it exists + if response.body and response.is_json(): + response.body = self._serialize_response( + field=route.dependant.return_param, + response_content=response.body, + has_route_custom_response_validation=route.custom_response_validation_http_code is not None, + ) + + return response + + def _serialize_response( + self, + *, + field: ModelField | None = None, + response_content: Any, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + has_route_custom_response_validation: bool = False, + ) -> Any: + """ + Serialize the response content according to the field type. + """ + if field: + errors: list[dict[str, Any]] = [] + value = _validate_field(field=field, value=response_content, loc=("response",), existing_errors=errors) + if errors: + # route-level validation must take precedence over app-level + if has_route_custom_response_validation: + raise ResponseValidationError( + errors=_normalize_errors(errors), + body=response_content, + source="route", + ) + if self._has_response_validation_error: + raise ResponseValidationError(errors=_normalize_errors(errors), body=response_content, source="app") + + raise RequestValidationError(errors=_normalize_errors(errors), body=response_content) + + if hasattr(field, "serialize"): + return field.serialize( + value, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + return jsonable_encoder( + value, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + custom_serializer=self._validation_serializer, + ) + else: + # Just serialize the response content returned from the handler. + return jsonable_encoder(response_content, custom_serializer=self._validation_serializer) + + def _prepare_response_content( + self, + res: Any, + *, + exclude_unset: bool, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> Any: + """ + Prepares the response content for serialization. + """ + if isinstance(res, BaseModel): + return _model_dump( + res, + by_alias=True, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + elif isinstance(res, list): + return [ + self._prepare_response_content(item, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults) + for item in res + ] + elif isinstance(res, dict): + return { + k: self._prepare_response_content(v, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults) + for k, v in res.items() + } + elif dataclasses.is_dataclass(res): + return dataclasses.asdict(res) # type: ignore[arg-type] + return res + + def _get_body(self, app: EventHandlerInstance) -> dict[str, Any]: + """ + Get the request body from the event, and parse it as JSON. + """ + + content_type = app.current_event.headers.get("content-type") + if not content_type or content_type.strip().startswith("application/json"): + try: + return app.current_event.json_body + except json.JSONDecodeError as e: + raise RequestValidationError( + [ + { + "type": "json_invalid", + "loc": ("body", e.pos), + "msg": "JSON decode error", + "input": {}, + "ctx": {"error": e.msg}, + }, + ], + body=e.doc, + ) from e + else: + raise NotImplementedError("Only JSON body is supported") + + +def _request_params_to_args( + required_params: Sequence[ModelField], + received_params: Mapping[str, Any], +) -> tuple[dict[str, Any], list[Any]]: + """ + Convert the request params to a dictionary of values using validation, and returns a list of errors. + """ + values = {} + errors = [] + + for field in required_params: + field_info = field.field_info + + # To ensure early failure, we check if it's not an instance of Param. + if not isinstance(field_info, Param): + raise AssertionError(f"Expected Param field_info, got {field_info}") + + value = received_params.get(field.alias) + + loc = (field_info.in_.value, field.alias) + + # If we don't have a value, see if it's required or has a default + if value is None: + if field.required: + errors.append(get_missing_field_error(loc=loc)) + else: + values[field.name] = deepcopy(field.default) + continue + + # Finally, validate the value + values[field.name] = _validate_field(field=field, value=value, loc=loc, existing_errors=errors) + + return values, errors + + +def _request_body_to_args( + required_params: list[ModelField], + received_body: dict[str, Any] | None, +) -> tuple[dict[str, Any], list[dict[str, Any]]]: + """ + Convert the request body to a dictionary of values using validation, and returns a list of errors. + """ + values: dict[str, Any] = {} + errors: list[dict[str, Any]] = [] + + received_body, field_alias_omitted = _get_embed_body( + field=required_params[0], + required_params=required_params, + received_body=received_body, + ) + + for field in required_params: + # This sets the location to: + # { "user": { object } } if field.alias == user + # { { object } if field_alias is omitted + loc: tuple[str, ...] = ("body", field.alias) + if field_alias_omitted: + loc = ("body",) + + value: Any | None = None + + # Now that we know what to look for, try to get the value from the received body + if received_body is not None: + try: + value = received_body.get(field.alias) + except AttributeError: + errors.append(get_missing_field_error(loc)) + continue + + # Determine if the field is required + if value is None: + if field.required: + errors.append(get_missing_field_error(loc)) + else: + values[field.name] = deepcopy(field.default) + continue + + # MAINTENANCE: Handle byte and file fields + + # Finally, validate the value + values[field.name] = _validate_field(field=field, value=value, loc=loc, existing_errors=errors) + + return values, errors + + +def _validate_field( + *, + field: ModelField, + value: Any, + loc: tuple[str, ...], + existing_errors: list[dict[str, Any]], +): + """ + Validate a field, and append any errors to the existing_errors list. + """ + validated_value, errors = field.validate(value=value, loc=loc) + + if isinstance(errors, list): + processed_errors = _regenerate_error_with_loc(errors=errors, loc_prefix=()) + existing_errors.extend(processed_errors) + elif errors: + existing_errors.append(errors) + + return validated_value + + +def _get_embed_body( + *, + field: ModelField, + required_params: list[ModelField], + received_body: dict[str, Any] | None, +) -> tuple[dict[str, Any] | None, bool]: + field_info = field.field_info + embed = getattr(field_info, "embed", None) + + # If the field is an embed, and the field alias is omitted, we need to wrap the received body in the field alias. + field_alias_omitted = len(required_params) == 1 and not embed + if field_alias_omitted: + received_body = {field.alias: received_body} + + return received_body, field_alias_omitted + + +def _normalize_multi_query_string_with_param( + query_string: dict[str, list[str]], + params: Sequence[ModelField], +) -> dict[str, Any]: + """ + Extract and normalize resolved_query_string_parameters + + Parameters + ---------- + query_string: dict + A dictionary containing the initial query string parameters. + params: Sequence[ModelField] + A sequence of ModelField objects representing parameters. + + Returns + ------- + A dictionary containing the processed multi_query_string_parameters. + """ + resolved_query_string: dict[str, Any] = query_string + for param in filter(is_scalar_field, params): + try: + # if the target parameter is a scalar, we keep the first value of the query string + # regardless if there are more in the payload + resolved_query_string[param.alias] = query_string[param.alias][0] + except KeyError: + pass + return resolved_query_string + + +def _normalize_multi_header_values_with_param(headers: MutableMapping[str, Any], params: Sequence[ModelField]): + """ + Extract and normalize resolved_headers_field + + Parameters + ---------- + headers: MutableMapping[str, Any] + A dictionary containing the initial header parameters. + params: Sequence[ModelField] + A sequence of ModelField objects representing parameters. + + Returns + ------- + A dictionary containing the processed headers. + """ + if headers: + for param in filter(is_scalar_field, params): + try: + if len(headers[param.alias]) == 1: + # if the target parameter is a scalar and the list contains only 1 element + # we keep the first value of the headers regardless if there are more in the payload + headers[param.alias] = headers[param.alias][0] + except KeyError: + pass + return headers diff --git a/aws_lambda_powertools/event_handler/middlewares/schema_validation.py b/aws_lambda_powertools/event_handler/middlewares/schema_validation.py new file mode 100644 index 00000000000..c24fff0cbe0 --- /dev/null +++ b/aws_lambda_powertools/event_handler/middlewares/schema_validation.py @@ -0,0 +1,128 @@ +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING + +from aws_lambda_powertools.event_handler.exceptions import BadRequestError, InternalServerError +from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware +from aws_lambda_powertools.utilities.validation import validate +from aws_lambda_powertools.utilities.validation.exceptions import InvalidSchemaFormatError, SchemaValidationError + +if TYPE_CHECKING: + from aws_lambda_powertools.event_handler.api_gateway import Response + from aws_lambda_powertools.event_handler.types import EventHandlerInstance + +logger = logging.getLogger(__name__) + + +class SchemaValidationMiddleware(BaseMiddlewareHandler): + """Middleware to validate API request and response against JSON Schema using the [Validation utility](https://docs.powertools.aws.dev/lambda/python/latest/utilities/validation/). + + Example + -------- + **Validating incoming event** + + ```python + import requests + + from aws_lambda_powertools import Logger + from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response + from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware + from aws_lambda_powertools.event_handler.middlewares.schema_validation import SchemaValidationMiddleware + + app = APIGatewayRestResolver() + logger = Logger() + json_schema_validation = SchemaValidationMiddleware(inbound_schema=INCOMING_JSON_SCHEMA) + + + @app.get("/todos", middlewares=[json_schema_validation]) + def get_todos(): + todos: requests.Response = requests.get("https://jsonplaceholder.typicode.com/todos") + todos.raise_for_status() + + # for brevity, we'll limit to the first 10 only + return {"todos": todos.json()[:10]} + + + @logger.inject_lambda_context + def lambda_handler(event, context): + return app.resolve(event, context) + ``` + """ + + def __init__( + self, + inbound_schema: dict, + inbound_formats: dict | None = None, + outbound_schema: dict | None = None, + outbound_formats: dict | None = None, + ): + """See [Validation utility](https://docs.powertools.aws.dev/lambda/python/latest/utilities/validation/) docs for examples on all parameters. + + Parameters + ---------- + inbound_schema : dict + JSON Schema to validate incoming event + inbound_formats : dict | None, optional + Custom formats containing a key (e.g. int64) and a value expressed as regex or callback returning bool, by default None + JSON Schema to validate outbound event, by default None + outbound_formats : dict | None, optional + Custom formats containing a key (e.g. int64) and a value expressed as regex or callback returning bool, by default None + """ # noqa: E501 + super().__init__() + self.inbound_schema = inbound_schema + self.inbound_formats = inbound_formats + self.outbound_schema = outbound_schema + self.outbound_formats = outbound_formats + + def bad_response(self, error: SchemaValidationError) -> Response: + message: str = f"Bad Response: {error.message}" + logger.debug(message) + raise BadRequestError(message) + + def bad_request(self, error: SchemaValidationError) -> Response: + message: str = f"Bad Request: {error.message}" + logger.debug(message) + raise BadRequestError(message) + + def bad_config(self, error: InvalidSchemaFormatError) -> Response: + logger.debug(f"Invalid Schema Format: {error}") + raise InternalServerError("Internal Server Error") + + def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response: + """Validates incoming JSON payload (body) against JSON Schema provided. + + Parameters + ---------- + app : EventHandlerInstance + An instance of an Event Handler + next_middleware : NextMiddleware + Callable to get response from the next middleware or route handler in the chain + + Returns + ------- + Response + It can return three types of response objects + + - Original response: Propagates HTTP response returned from the next middleware if validation succeeds + - HTTP 400: Payload or response failed JSON Schema validation + - HTTP 500: JSON Schema provided has incorrect format + """ + try: + validate(event=app.current_event.json_body, schema=self.inbound_schema, formats=self.inbound_formats) + except SchemaValidationError as error: + return self.bad_request(error) + except InvalidSchemaFormatError as error: + return self.bad_config(error) + + result = next_middleware(app) + + if self.outbound_formats is not None: + try: + validate(event=result.body, schema=self.inbound_schema, formats=self.inbound_formats) + except SchemaValidationError as error: + return self.bad_response(error) + except InvalidSchemaFormatError as error: + return self.bad_config(error) + + return result diff --git a/tests/functional/parser/__init__.py b/aws_lambda_powertools/event_handler/openapi/__init__.py similarity index 100% rename from tests/functional/parser/__init__.py rename to aws_lambda_powertools/event_handler/openapi/__init__.py diff --git a/aws_lambda_powertools/event_handler/openapi/compat.py b/aws_lambda_powertools/event_handler/openapi/compat.py new file mode 100644 index 00000000000..d3340f34e4b --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/compat.py @@ -0,0 +1,326 @@ +# mypy: ignore-errors +from __future__ import annotations + +from collections import deque +from collections.abc import Mapping, Sequence + +# MAINTENANCE: remove when deprecating Pydantic v1. Mypy doesn't handle two different code paths that import different +# versions of a module, so we need to ignore errors here. +from dataclasses import dataclass, is_dataclass +from typing import TYPE_CHECKING, Any, Deque, FrozenSet, List, Set, Tuple, Union + +from pydantic import BaseModel, TypeAdapter, ValidationError, create_model + +# Importing from internal libraries in Pydantic may introduce potential risks, as these internal libraries +# are not part of the public API and may change without notice in future releases. +# We use this for forward reference, as it allows us to handle forward references in type annotations. +from pydantic._internal._typing_extra import eval_type_lenient +from pydantic._internal._utils import lenient_issubclass +from pydantic_core import PydanticUndefined, PydanticUndefinedType +from typing_extensions import Annotated, Literal, get_args, get_origin + +from aws_lambda_powertools.event_handler.openapi.types import UnionType + +if TYPE_CHECKING: + from pydantic.fields import FieldInfo + from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue + + from aws_lambda_powertools.event_handler.openapi.types import IncEx, ModelNameMap + +Undefined = PydanticUndefined +Required = PydanticUndefined +UndefinedType = PydanticUndefinedType + +evaluate_forwardref = eval_type_lenient + +sequence_annotation_to_type = { + Sequence: list, + List: list, + list: list, + Tuple: tuple, + tuple: tuple, + Set: set, + set: set, + FrozenSet: frozenset, + frozenset: frozenset, + Deque: deque, + deque: deque, +} + +sequence_types = tuple(sequence_annotation_to_type.keys()) + +RequestErrorModel: type[BaseModel] = create_model("Request") + + +class ErrorWrapper(Exception): + pass + + +@dataclass +class ModelField: + field_info: FieldInfo + name: str + mode: Literal["validation", "serialization"] = "validation" + + @property + def alias(self) -> str: + value = self.field_info.alias + return value if value is not None else self.name + + @property + def required(self) -> bool: + return self.field_info.is_required() + + @property + def default(self) -> Any: + return self.get_default() + + @property + def type_(self) -> Any: + return self.field_info.annotation + + def __post_init__(self) -> None: + self._type_adapter: TypeAdapter[Any] = TypeAdapter( + Annotated[self.field_info.annotation, self.field_info], + ) + + def get_default(self) -> Any: + if self.field_info.is_required(): + return Undefined + return self.field_info.get_default(call_default_factory=True) + + def serialize( + self, + value: Any, + *, + mode: Literal["json", "python"] = "json", + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> Any: + return self._type_adapter.dump_python( + value, + mode=mode, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + def validate( + self, + value: Any, + *, + loc: tuple[int | str, ...] = (), + ) -> tuple[Any, list[dict[str, Any]] | None]: + try: + return (self._type_adapter.validate_python(value, from_attributes=True), None) + except ValidationError as exc: + return None, _regenerate_error_with_loc(errors=exc.errors(), loc_prefix=loc) + + def __hash__(self) -> int: + # Each ModelField is unique for our purposes + return id(self) + + +def get_schema_from_model_field( + *, + field: ModelField, + model_name_map: ModelNameMap, + field_mapping: dict[ + tuple[ModelField, Literal["validation", "serialization"]], + JsonSchemaValue, + ], +) -> dict[str, Any]: + json_schema = field_mapping[(field, field.mode)] + if "$ref" not in json_schema: + # MAINTENANCE: remove when deprecating Pydantic v1 + # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207 + json_schema["title"] = field.field_info.title or field.alias.title().replace("_", " ") + return json_schema + + +def get_definitions( + *, + fields: list[ModelField], + schema_generator: GenerateJsonSchema, + model_name_map: ModelNameMap, +) -> tuple[ + dict[ + tuple[ModelField, Literal["validation", "serialization"]], + dict[str, Any], + ], + dict[str, dict[str, Any]], +]: + inputs = [(field, field.mode, field._type_adapter.core_schema) for field in fields] + field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs) + + return field_mapping, definitions + + +def get_compat_model_name_map(fields: list[ModelField]) -> ModelNameMap: + return {} + + +def get_annotation_from_field_info(annotation: Any, field_info: FieldInfo, field_name: str) -> Any: + return annotation + + +def model_rebuild(model: type[BaseModel]) -> None: + model.model_rebuild() + + +def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo: + return type(field_info).from_annotation(annotation) + + +def get_missing_field_error(loc: tuple[str, ...]) -> dict[str, Any]: + error = ValidationError.from_exception_data( + "Field required", + [{"type": "missing", "loc": loc, "input": {}}], + ).errors()[0] + error["input"] = None + return error + + +def is_scalar_field(field: ModelField) -> bool: + from aws_lambda_powertools.event_handler.openapi.params import Body + + return field_annotation_is_scalar(field.field_info.annotation) and not isinstance(field.field_info, Body) + + +def is_scalar_sequence_field(field: ModelField) -> bool: + return field_annotation_is_scalar_sequence(field.field_info.annotation) + + +def is_sequence_field(field: ModelField) -> bool: + return field_annotation_is_sequence(field.field_info.annotation) + + +def is_bytes_field(field: ModelField) -> bool: + return is_bytes_or_nonable_bytes_annotation(field.type_) + + +def is_bytes_sequence_field(field: ModelField) -> bool: + return is_bytes_sequence_annotation(field.type_) + + +def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]: + origin_type = get_origin(field.field_info.annotation) or field.field_info.annotation + if not issubclass(origin_type, sequence_types): # type: ignore[arg-type] + raise AssertionError(f"Expected sequence type, got {origin_type}") + return sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return] + + +def _normalize_errors(errors: Sequence[Any]) -> list[dict[str, Any]]: + return errors # type: ignore[return-value] + + +def create_body_model(*, fields: Sequence[ModelField], model_name: str) -> type[BaseModel]: + field_params = {f.name: (f.field_info.annotation, f.field_info) for f in fields} + model: type[BaseModel] = create_model(model_name, **field_params) + return model + + +def _model_dump(model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any) -> Any: + return model.model_dump(mode=mode, **kwargs) + + +def model_json(model: BaseModel, **kwargs: Any) -> Any: + return model.model_dump_json(**kwargs) + + +# Common code for both versions + + +def field_annotation_is_complex(annotation: type[Any] | None) -> bool: + origin = get_origin(annotation) + if origin is Union or origin is UnionType: + return any(field_annotation_is_complex(arg) for arg in get_args(annotation)) + + return ( + _annotation_is_complex(annotation) + or _annotation_is_complex(origin) + or hasattr(origin, "__pydantic_core_schema__") + or hasattr(origin, "__get_pydantic_core_schema__") + ) + + +def field_annotation_is_scalar(annotation: Any) -> bool: + return annotation is Ellipsis or not field_annotation_is_complex(annotation) + + +def field_annotation_is_sequence(annotation: type[Any] | None) -> bool: + return _annotation_is_sequence(annotation) or _annotation_is_sequence(get_origin(annotation)) + + +def field_annotation_is_scalar_sequence(annotation: type[Any] | None) -> bool: + origin = get_origin(annotation) + if origin is Union or origin is UnionType: + at_least_one_scalar_sequence = False + for arg in get_args(annotation): + if field_annotation_is_scalar_sequence(arg): + at_least_one_scalar_sequence = True + continue + elif not field_annotation_is_scalar(arg): + return False + return at_least_one_scalar_sequence + return field_annotation_is_sequence(annotation) and all( + field_annotation_is_scalar(sub_annotation) for sub_annotation in get_args(annotation) + ) + + +def is_bytes_or_nonable_bytes_annotation(annotation: Any) -> bool: + if lenient_issubclass(annotation, bytes): + return True + origin = get_origin(annotation) + if origin is Union or origin is UnionType: + for arg in get_args(annotation): + if lenient_issubclass(arg, bytes): + return True + return False + + +def is_bytes_sequence_annotation(annotation: Any) -> bool: + origin = get_origin(annotation) + if origin is Union or origin is UnionType: + at_least_one = False + for arg in get_args(annotation): + if is_bytes_sequence_annotation(arg): + at_least_one = True + break + return at_least_one + return field_annotation_is_sequence(annotation) and all( + is_bytes_or_nonable_bytes_annotation(sub_annotation) for sub_annotation in get_args(annotation) + ) + + +def value_is_sequence(value: Any) -> bool: + return isinstance(value, sequence_types) and not isinstance(value, (str, bytes)) # type: ignore[arg-type] + + +def _annotation_is_complex(annotation: type[Any] | None) -> bool: + return ( + lenient_issubclass(annotation, (BaseModel, Mapping)) # Keep it to UploadFile + or _annotation_is_sequence(annotation) + or is_dataclass(annotation) + ) + + +def _annotation_is_sequence(annotation: type[Any] | None) -> bool: + if lenient_issubclass(annotation, (str, bytes)): + return False + return lenient_issubclass(annotation, sequence_types) + + +def _regenerate_error_with_loc(*, errors: Sequence[Any], loc_prefix: tuple[str | int, ...]) -> list[dict[str, Any]]: + updated_loc_errors: list[Any] = [ + {**err, "loc": loc_prefix + err.get("loc", ())} for err in _normalize_errors(errors) + ] + + return updated_loc_errors diff --git a/aws_lambda_powertools/event_handler/openapi/config.py b/aws_lambda_powertools/event_handler/openapi/config.py new file mode 100644 index 00000000000..83fe2156a57 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/config.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any + +from aws_lambda_powertools.event_handler.openapi.constants import ( + DEFAULT_API_VERSION, + DEFAULT_OPENAPI_TITLE, + DEFAULT_OPENAPI_VERSION, +) + +if TYPE_CHECKING: + from aws_lambda_powertools.event_handler.openapi.models import ( + Contact, + ExternalDocumentation, + License, + SecurityScheme, + Server, + Tag, + ) + + +@dataclass +class OpenAPIConfig: + """Configuration class for OpenAPI specification. + + This class holds all the necessary configuration parameters to generate an OpenAPI specification. + + Parameters + ---------- + title: str + The title of the application. + version: str + The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API + openapi_version: str, default = "3.0.0" + The version of the OpenAPI Specification (which the document uses). + summary: str, optional + A short summary of what the application does. + description: str, optional + A verbose explanation of the application behavior. + tags: list[Tag, str], optional + A list of tags used by the specification with additional metadata. + servers: list[Server], optional + An array of Server Objects, which provide connectivity information to a target server. + terms_of_service: str, optional + A URL to the Terms of Service for the API. MUST be in the format of a URL. + contact: Contact, optional + The contact information for the exposed API. + license_info: License, optional + The license information for the exposed API. + security_schemes: dict[str, SecurityScheme]], optional + A declaration of the security schemes available to be used in the specification. + security: list[dict[str, list[str]]], optional + A declaration of which security mechanisms are applied globally across the API. + external_documentation: ExternalDocumentation, optional + A link to external documentation for the API. + openapi_extensions: Dict[str, Any], optional + Additional OpenAPI extensions as a dictionary. + + Example + -------- + >>> config = OpenAPIConfig( + ... title="My API", + ... version="1.0.0", + ... description="This is my API description", + ... contact=Contact(name="API Support", email="support@example.com"), + ... servers=[Server(url="https://api.example.com/v1")] + ... ) + """ + + title: str = DEFAULT_OPENAPI_TITLE + version: str = DEFAULT_API_VERSION + openapi_version: str = DEFAULT_OPENAPI_VERSION + summary: str | None = None + description: str | None = None + tags: list[Tag | str] | None = None + servers: list[Server] | None = None + terms_of_service: str | None = None + contact: Contact | None = None + license_info: License | None = None + security_schemes: dict[str, SecurityScheme] | None = None + security: list[dict[str, list[str]]] | None = None + external_documentation: ExternalDocumentation | None = None + openapi_extensions: dict[str, Any] | None = None diff --git a/aws_lambda_powertools/event_handler/openapi/constants.py b/aws_lambda_powertools/event_handler/openapi/constants.py new file mode 100644 index 00000000000..debe1d56736 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/constants.py @@ -0,0 +1,3 @@ +DEFAULT_API_VERSION = "1.0.0" +DEFAULT_OPENAPI_VERSION = "3.1.0" +DEFAULT_OPENAPI_TITLE = "Powertools for AWS Lambda (Python) API" diff --git a/aws_lambda_powertools/event_handler/openapi/dependant.py b/aws_lambda_powertools/event_handler/openapi/dependant.py new file mode 100644 index 00000000000..976ce9f0454 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/dependant.py @@ -0,0 +1,384 @@ +from __future__ import annotations + +import inspect +import re +from typing import TYPE_CHECKING, Any, ForwardRef, cast + +from aws_lambda_powertools.event_handler.openapi.compat import ( + ModelField, + create_body_model, + evaluate_forwardref, + is_scalar_field, + is_scalar_sequence_field, +) +from aws_lambda_powertools.event_handler.openapi.params import ( + Body, + Dependant, + Header, + Param, + ParamTypes, + Query, + _File, + _Form, + analyze_param, + create_response_field, + get_flat_dependant, +) +from aws_lambda_powertools.event_handler.openapi.types import OpenAPIResponse, OpenAPIResponseContentModel + +if TYPE_CHECKING: + from collections.abc import Callable + + from pydantic import BaseModel + +""" +This turns the opaque function signature into typed, validated models. + +It relies on Pydantic's typing and validation to achieve this in a declarative way. +This enables traits like autocompletion, validation, and declarative structure vs imperative parsing. + +This code parses an OpenAPI operation handler function signature into Pydantic models. It uses inspect to get the +signature and regex to parse path parameters. Each parameter is analyzed to extract its type annotation and generate +a corresponding Pydantic field, which are added to a Dependant model. Return values are handled similarly. + +This modeling allows for type checking, automatic parameter name/location/type extraction, and input validation - +turning the opaque signature into validated models. It relies on Pydantic's typing and validation for a declarative +approach over imperative parsing, enabling autocompletion, validation and structure. +""" + + +def add_param_to_fields( + *, + field: ModelField, + dependant: Dependant, +) -> None: + """ + Adds a parameter to the list of parameters in the dependant model. + + Parameters + ---------- + field: ModelField + The field to add + dependant: Dependant + The dependant model to add the field to + + """ + field_info = cast(Param, field.field_info) + + # Dictionary to map ParamTypes to their corresponding lists in dependant + param_type_map = { + ParamTypes.path: dependant.path_params, + ParamTypes.query: dependant.query_params, + ParamTypes.header: dependant.header_params, + ParamTypes.cookie: dependant.cookie_params, + } + + # Check if field_info.in_ is a valid key in param_type_map and append the field to the corresponding list + # or raise an exception if it's not a valid key. + if field_info.in_ in param_type_map: + param_type_map[field_info.in_].append(field) + else: + raise AssertionError(f"Unsupported param type: {field_info.in_}") + + +def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any: + """ + Evaluates a type annotation, which can be a string or a ForwardRef. + """ + if isinstance(annotation, str): + annotation = ForwardRef(annotation) + annotation = evaluate_forwardref(annotation, globalns, globalns) + return annotation + + +def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature: + """ + Returns a typed signature for a callable, resolving forward references. + + Parameters + ---------- + call: Callable[..., Any] + The callable to get the signature for + + Returns + ------- + inspect.Signature + The typed signature + """ + signature = inspect.signature(call) + + # Gets the global namespace for the call. This is used to resolve forward references. + globalns = getattr(call, "__globals__", {}) + + typed_params = [ + inspect.Parameter( + name=param.name, + kind=param.kind, + default=param.default, + annotation=get_typed_annotation(param.annotation, globalns), + ) + for param in signature.parameters.values() + ] + + # If the return annotation is not empty, add it to the signature. + if signature.return_annotation is not inspect.Signature.empty: + return_param = inspect.Parameter( + name="Return", + kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, + default=None, + annotation=get_typed_annotation(signature.return_annotation, globalns), + ) + return inspect.Signature(typed_params, return_annotation=return_param.annotation) + else: + return inspect.Signature(typed_params) + + +def get_path_param_names(path: str) -> set[str]: + """ + Returns the path parameter names from a path template. Those are the strings between { and }. + + Parameters + ---------- + path: str + The path template + + Returns + ------- + set[str] + The path parameter names + + """ + return set(re.findall("{(.*?)}", path)) + + +def get_dependant( + *, + path: str, + call: Callable[..., Any], + name: str | None = None, + responses: dict[int, OpenAPIResponse] | None = None, +) -> Dependant: + """ + Returns a dependant model for a handler function. A dependant model is a model that contains + the parameters and return value of a handler function. + + Parameters + ---------- + path: str + The path template + call: Callable[..., Any] + The handler function + name: str, optional + The name of the handler function + responses: list[dict[int, OpenAPIResponse]], optional + The list of extra responses for the handler function + + Returns + ------- + Dependant + The dependant model for the handler function + """ + path_param_names = get_path_param_names(path) + endpoint_signature = get_typed_signature(call) + signature_params = endpoint_signature.parameters + + dependant = Dependant( + call=call, + name=name, + path=path, + ) + + # Add each parameter to the dependant model + for param_name, param in signature_params.items(): + # If the parameter is a path parameter, we need to set the in_ field to "path". + is_path_param = param_name in path_param_names + + # Analyze the parameter to get the Pydantic field. + param_field = analyze_param( + param_name=param_name, + annotation=param.annotation, + value=param.default, + is_path_param=is_path_param, + is_response_param=False, + ) + if param_field is None: + raise AssertionError(f"Parameter field is None for param: {param_name}") + + if is_body_param(param_field=param_field, is_path_param=is_path_param): + dependant.body_params.append(param_field) + else: + add_param_to_fields(field=param_field, dependant=dependant) + + _add_return_annotation(dependant, endpoint_signature) + _add_extra_responses(dependant, responses) + + return dependant + + +def _add_extra_responses(dependant: Dependant, responses: dict[int, OpenAPIResponse] | None): + # Also add the optional extra responses to the dependant model. + if not responses: + return + + for response in responses.values(): + for schema in response.get("content", {}).values(): + if "model" in schema: + response_field = analyze_param( + param_name="return", + annotation=cast(OpenAPIResponseContentModel, schema)["model"], + value=None, + is_path_param=False, + is_response_param=True, + ) + if response_field is None: + raise AssertionError("Response field is None for response model") + + dependant.response_extra_models.append(response_field) + + +def _add_return_annotation(dependant: Dependant, endpoint_signature: inspect.Signature): + # If the return annotation is not empty, add it to the dependant model. + return_annotation = endpoint_signature.return_annotation + if return_annotation is not inspect.Signature.empty: + param_field = analyze_param( + param_name="return", + annotation=return_annotation, + value=None, + is_path_param=False, + is_response_param=True, + ) + if param_field is None: + raise AssertionError("Param field is None for return annotation") + + dependant.return_param = param_field + + +def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool: + """ + Returns whether a parameter is a request body parameter, by checking if it is a scalar field or a body field. + + Parameters + ---------- + param_field: ModelField + The parameter field + is_path_param: bool + Whether the parameter is a path parameter + + Returns + ------- + bool + Whether the parameter is a request body parameter + """ + if is_path_param: + if not is_scalar_field(field=param_field): + raise AssertionError("Path params must be of one of the supported types") + return False + elif is_scalar_field(field=param_field): + return False + elif isinstance(param_field.field_info, (Query, Header)) and is_scalar_sequence_field(param_field): + return False + else: + if not isinstance(param_field.field_info, Body): + raise AssertionError(f"Param: {param_field.name} can only be a request body, use Body()") + return True + + +def get_flat_params(dependant: Dependant) -> list[ModelField]: + """ + Get a list of all the parameters from a Dependant object. + + Parameters + ---------- + dependant : Dependant + The Dependant object containing the parameters. + + Returns + ------- + list[ModelField] + A list of ModelField objects containing the flat parameters from the Dependant object. + + """ + flat_dependant = get_flat_dependant(dependant) + return ( + flat_dependant.path_params + + flat_dependant.query_params + + flat_dependant.header_params + + flat_dependant.cookie_params + ) + + +def get_body_field(*, dependant: Dependant, name: str) -> ModelField | None: + """ + Get the Body field for a given Dependant object. + """ + + flat_dependant = get_flat_dependant(dependant) + if not flat_dependant.body_params: + return None + + first_param = flat_dependant.body_params[0] + field_info = first_param.field_info + + # Handle the case where there is only one body parameter and it is embedded + embed = getattr(field_info, "embed", None) + body_param_names_set = {param.name for param in flat_dependant.body_params} + if len(body_param_names_set) == 1 and not embed: + return first_param + + # If one field requires to embed, all have to be embedded + for param in flat_dependant.body_params: + setattr(param.field_info, "embed", True) # noqa: B010 + + # Generate a custom body model for this endpoint + model_name = "Body_" + name + body_model = create_body_model(fields=flat_dependant.body_params, model_name=model_name) + + required = any(True for f in flat_dependant.body_params if f.required) + + body_field_info, body_field_info_kwargs = get_body_field_info( + body_model=body_model, + flat_dependant=flat_dependant, + required=required, + ) + + final_field = create_response_field( + name="body", + type_=body_model, + required=required, + alias="body", + field_info=body_field_info(**body_field_info_kwargs), + ) + return final_field + + +def get_body_field_info( + *, + body_model: type[BaseModel], + flat_dependant: Dependant, + required: bool, +) -> tuple[type[Body], dict[str, Any]]: + """ + Get the Body field info and kwargs for a given body model. + """ + + body_field_info_kwargs: dict[str, Any] = {"annotation": body_model, "alias": "body"} + + if not required: + body_field_info_kwargs["default"] = None + + if any(isinstance(f.field_info, _File) for f in flat_dependant.body_params): + # MAINTENANCE: body_field_info: type[Body] = _File + raise NotImplementedError("_File fields are not supported in request bodies") + elif any(isinstance(f.field_info, _Form) for f in flat_dependant.body_params): + # MAINTENANCE: body_field_info: type[Body] = _Form + raise NotImplementedError("_Form fields are not supported in request bodies") + else: + body_field_info = Body + + body_param_media_types = [ + f.field_info.media_type for f in flat_dependant.body_params if isinstance(f.field_info, Body) + ] + if len(set(body_param_media_types)) == 1: + body_field_info_kwargs["media_type"] = body_param_media_types[0] + + return body_field_info, body_field_info_kwargs diff --git a/aws_lambda_powertools/event_handler/openapi/encoders.py b/aws_lambda_powertools/event_handler/openapi/encoders.py new file mode 100644 index 00000000000..59ce47ebc1d --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/encoders.py @@ -0,0 +1,379 @@ +from __future__ import annotations + +import dataclasses +import datetime +from collections import defaultdict, deque +from decimal import Decimal +from enum import Enum +from pathlib import Path, PurePath +from re import Pattern +from types import GeneratorType +from typing import TYPE_CHECKING, Any +from uuid import UUID + +from pydantic import BaseModel +from pydantic.types import SecretBytes, SecretStr + +from aws_lambda_powertools.event_handler.openapi.compat import _model_dump + +if TYPE_CHECKING: + from collections.abc import Callable + + from aws_lambda_powertools.event_handler.openapi.types import IncEx + +from aws_lambda_powertools.event_handler.openapi.exceptions import SerializationError + +""" +This module contains the encoders used by jsonable_encoder to convert Python objects to JSON serializable data types. +""" + + +def jsonable_encoder( # noqa: PLR0911 + obj: Any, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + custom_serializer: Callable[[Any], str] | None = None, +) -> Any: + """ + JSON encodes an arbitrary Python object into JSON serializable data types. + + This is a modified version of fastapi.encoders.jsonable_encoder that supports + encoding of pydantic.BaseModel objects. + + Parameters + ---------- + obj : Any + The object to encode + include : IncEx | None, optional + A set or dictionary of strings that specifies which properties should be included, by default None, + meaning everything is included + exclude : IncEx | None, optional + A set or dictionary of strings that specifies which properties should be excluded, by default None, + meaning nothing is excluded + by_alias : bool, optional + Whether field aliases should be respected, by default True + exclude_unset : bool, optional + Whether fields that are not set should be excluded, by default False + exclude_defaults : bool, optional + Whether fields that are equal to their default value (as specified in the model) should be excluded, + by default False + exclude_none : bool, optional + Whether fields that are equal to None should be excluded, by default False + custom_serializer : Callable, optional + A custom serializer to use for encoding the object, when everything else fails. + + Returns + ------- + Any + The JSON serializable data types + """ + if include is not None and not isinstance(include, (set, dict)): + include = set(include) + if exclude is not None and not isinstance(exclude, (set, dict)): + exclude = set(exclude) + + try: + # Pydantic models + if isinstance(obj, BaseModel): + return _dump_base_model( + obj=obj, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + exclude_defaults=exclude_defaults, + ) + + # Dataclasses + if dataclasses.is_dataclass(obj): + obj_dict = dataclasses.asdict(obj) # type: ignore[arg-type] + return jsonable_encoder( + obj_dict, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + custom_serializer=custom_serializer, + ) + + # Enums + if isinstance(obj, Enum): + return obj.value + + # Paths + if isinstance(obj, PurePath): + return str(obj) + + # Scalars + if isinstance(obj, (str, int, float, type(None))): + return obj + + # Dictionaries + if isinstance(obj, dict): + return _dump_dict( + obj=obj, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + custom_serializer=custom_serializer, + ) + + # Sequences + if isinstance(obj, (list, set, frozenset, GeneratorType, tuple, deque)): + return _dump_sequence( + obj=obj, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_none=exclude_none, + exclude_defaults=exclude_defaults, + exclude_unset=exclude_unset, + custom_serializer=custom_serializer, + ) + + # Other types + if type(obj) in ENCODERS_BY_TYPE: + return ENCODERS_BY_TYPE[type(obj)](obj) + + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(obj, classes_tuple): + return encoder(obj) + + # Use custom serializer if present + if custom_serializer: + return custom_serializer(obj) + + # Default + return _dump_other( + obj=obj, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_none=exclude_none, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + custom_serializer=custom_serializer, + ) + except ValueError as exc: + raise SerializationError( + f"Unable to serialize the object {obj} as it is not a supported type. Error details: {exc}", + "See: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#serializing-objects", + ) from exc + + +def _dump_base_model( + *, + obj: Any, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_none: bool = False, + exclude_defaults: bool = False, +): + """ + Dump a BaseModel object to a dict, using the same parameters as jsonable_encoder + """ + obj_dict = _model_dump( + obj, + mode="json", + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + exclude_defaults=exclude_defaults, + ) + if "__root__" in obj_dict: + obj_dict = obj_dict["__root__"] + + return jsonable_encoder( + obj_dict, + exclude_none=exclude_none, + exclude_defaults=exclude_defaults, + ) + + +def _dump_dict( + *, + obj: Any, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_none: bool = False, + custom_serializer: Callable[[Any], str] | None = None, +) -> dict[str, Any]: + """ + Dump a dict to a dict, using the same parameters as jsonable_encoder + + Parameters + ---------- + custom_serializer : Callable, optional + A custom serializer to use for encoding the object, when everything else fails. + """ + encoded_dict = {} + allowed_keys = set(obj.keys()) + if include is not None: + allowed_keys &= set(include) + if exclude is not None: + allowed_keys -= set(exclude) + for key, value in obj.items(): + if ( + (not isinstance(key, str) or not key.startswith("_sa")) + and (value is not None or not exclude_none) + and key in allowed_keys + ): + encoded_key = jsonable_encoder( + key, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + custom_serializer=custom_serializer, + ) + encoded_value = jsonable_encoder( + value, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + custom_serializer=custom_serializer, + ) + encoded_dict[encoded_key] = encoded_value + return encoded_dict + + +def _dump_sequence( + *, + obj: Any, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_none: bool = False, + exclude_defaults: bool = False, + custom_serializer: Callable[[Any], str] | None = None, +) -> list[Any]: + """ + Dump a sequence to a list, using the same parameters as jsonable_encoder. + """ + encoded_list = [] + for item in obj: + encoded_list.append( + jsonable_encoder( + item, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + custom_serializer=custom_serializer, + ), + ) + return encoded_list + + +def _dump_other( + *, + obj: Any, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_none: bool = False, + exclude_defaults: bool = False, + custom_serializer: Callable[[Any], str] | None = None, +) -> Any: + """ + Dump an object to a hashable object, using the same parameters as jsonable_encoder + """ + try: + data = dict(obj) + except Exception as e: + errors: list[Exception] = [e] + try: + data = vars(obj) + except Exception as e: + errors.append(e) + raise ValueError(errors) from e + return jsonable_encoder( + data, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + custom_serializer=custom_serializer, + ) + + +def iso_format(o: datetime.date | datetime.time) -> str: + """ + ISO format for date and time + """ + return o.isoformat() + + +def decimal_encoder(dec_value: Decimal) -> int | float: + """ + Encodes a Decimal as int of there's no exponent, otherwise float + + This is useful when we use ConstrainedDecimal to represent Numeric(x,0) + where an integer (but not int typed) is used. Encoding this as a float + results in failed round-tripping between encode and parse. + + >>> decimal_encoder(Decimal("1.0")) + 1.0 + + >>> decimal_encoder(Decimal("1")) + 1 + """ + if dec_value.as_tuple().exponent >= 0: # type: ignore[operator] + return int(dec_value) + else: + return float(dec_value) + + +# Encoders for types that are not JSON serializable +ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = { + bytes: lambda o: o.decode(), + datetime.date: iso_format, + datetime.datetime: iso_format, + datetime.time: iso_format, + datetime.timedelta: lambda td: td.total_seconds(), + Decimal: decimal_encoder, + Enum: lambda o: o.value, + frozenset: list, + deque: list, + GeneratorType: list, + Path: str, + Pattern: lambda o: o.pattern, + SecretBytes: str, + SecretStr: str, + set: list, + UUID: str, +} + + +# Generates a mapping of encoders to a tuple of classes that they can encode +def generate_encoders_by_class_tuples( + type_encoder_map: dict[Any, Callable[[Any], Any]], +) -> dict[Callable[[Any], Any], tuple[Any, ...]]: + encoders: dict[Callable[[Any], Any], tuple[Any, ...]] = defaultdict(tuple) + for type_, encoder in type_encoder_map.items(): + encoders[encoder] += (type_,) + return encoders + + +# Mapping of encoders to a tuple of classes that they can encode +encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE) diff --git a/aws_lambda_powertools/event_handler/openapi/exceptions.py b/aws_lambda_powertools/event_handler/openapi/exceptions.py new file mode 100644 index 00000000000..22807dfab29 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/exceptions.py @@ -0,0 +1,47 @@ +from collections.abc import Sequence +from typing import Any, Literal + + +class ValidationException(Exception): + """ + Base exception for all validation errors + """ + + def __init__(self, errors: Sequence[Any]) -> None: + self._errors = errors + + def errors(self) -> Sequence[Any]: + return self._errors + + +class RequestValidationError(ValidationException): + """ + Raised when the request body does not match the OpenAPI schema + """ + + def __init__(self, errors: Sequence[Any], *, body: Any = None) -> None: + super().__init__(errors) + self.body = body + + +class ResponseValidationError(ValidationException): + """ + Raised when the response body does not match the OpenAPI schema + """ + + def __init__(self, errors: Sequence[Any], *, body: Any = None, source: Literal["route", "app"] = "app") -> None: + super().__init__(errors) + self.body = body + self.source = source + + +class SerializationError(Exception): + """ + Base exception for all encoding errors + """ + + +class SchemaValidationError(ValidationException): + """ + Raised when the OpenAPI schema validation fails + """ diff --git a/aws_lambda_powertools/event_handler/openapi/models.py b/aws_lambda_powertools/event_handler/openapi/models.py new file mode 100644 index 00000000000..53becd3f870 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/models.py @@ -0,0 +1,486 @@ +# ruff: noqa: FA100 +from enum import Enum +from typing import Any, Dict, List, Literal, Optional, Set, Union + +from pydantic import AnyUrl, BaseModel, ConfigDict, Field, model_validator +from typing_extensions import Annotated + +from aws_lambda_powertools.event_handler.openapi.compat import model_rebuild +from aws_lambda_powertools.event_handler.openapi.exceptions import SchemaValidationError + +MODEL_CONFIG_ALLOW = ConfigDict(extra="allow") +MODEL_CONFIG_IGNORE = ConfigDict(extra="ignore") + +""" +The code defines Pydantic models for the various OpenAPI objects like OpenAPI, PathItem, Operation, Parameter etc. +These models can be used to parse OpenAPI JSON/YAML files into Python objects, or generate OpenAPI from Python data. +""" + + +class OpenAPIExtensions(BaseModel): + """ + This class serves as a Pydantic proxy model to add OpenAPI extensions. + + OpenAPI extensions are arbitrary fields, so we remove openapi_extensions when dumping + and add only the provided value in the schema. + """ + + openapi_extensions: Optional[Dict[str, Any]] = None + + # If the 'openapi_extensions' field is present in the 'values' dictionary, + # And if the extension starts with x- (must respect the RFC) + # update the 'values' dictionary with the contents of 'openapi_extensions', + # and then remove the 'openapi_extensions' field from the 'values' dictionary + model_config = {"extra": "allow"} + + @model_validator(mode="before") + def serialize_openapi_extension_v2(self): + if isinstance(self, dict) and self.get("openapi_extensions"): + openapi_extension_value = self.get("openapi_extensions") + + for extension_key in openapi_extension_value: + if not str(extension_key).startswith("x-"): + raise SchemaValidationError("An OpenAPI extension key must start with x-") + + self.update(openapi_extension_value) + self.pop("openapi_extensions", None) + + return self + + +# https://swagger.io/specification/#contact-object +class Contact(BaseModel): + name: Optional[str] = None + url: Optional[AnyUrl] = None + email: Optional[str] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#license-object +class License(BaseModel): + name: str + identifier: Optional[str] = None + url: Optional[AnyUrl] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#info-object +class Info(BaseModel): + title: str + description: Optional[str] = None + termsOfService: Optional[str] = None + contact: Optional[Contact] = None + license: Optional[License] = None # noqa: A003 + version: str + summary: Optional[str] = None + + model_config = MODEL_CONFIG_IGNORE + + +# https://swagger.io/specification/#server-variable-object +class ServerVariable(BaseModel): + enum: Annotated[Optional[List[str]], Field(min_length=1)] = None + default: str + description: Optional[str] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#server-object +class Server(OpenAPIExtensions): + url: Union[AnyUrl, str] + description: Optional[str] = None + variables: Optional[Dict[str, ServerVariable]] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#reference-object +class Reference(BaseModel): + ref: str = Field(alias="$ref") + + +# https://swagger.io/specification/#discriminator-object +class Discriminator(BaseModel): + propertyName: str + mapping: Optional[Dict[str, str]] = None + + +# https://swagger.io/specification/#xml-object +class XML(BaseModel): + name: Optional[str] = None + namespace: Optional[str] = None + prefix: Optional[str] = None + attribute: Optional[bool] = None + wrapped: Optional[bool] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#external-documentation-object +class ExternalDocumentation(BaseModel): + description: Optional[str] = None + url: AnyUrl + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#schema-object +class Schema(BaseModel): + # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu + # Core Vocabulary + schema_: Optional[str] = Field(default=None, alias="$schema") + vocabulary: Optional[str] = Field(default=None, alias="$vocabulary") + id: Optional[str] = Field(default=None, alias="$id") # noqa: A003 + anchor: Optional[str] = Field(default=None, alias="$anchor") + dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor") + ref: Optional[str] = Field(default=None, alias="$ref") + dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef") + defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs") + comment: Optional[str] = Field(default=None, alias="$comment") + # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s + # A Vocabulary for Applying Subschemas + allOf: Optional[List["SchemaOrBool"]] = None + anyOf: Optional[List["SchemaOrBool"]] = None + oneOf: Optional[List["SchemaOrBool"]] = None + not_: Optional["SchemaOrBool"] = Field(default=None, alias="not") + if_: Optional["SchemaOrBool"] = Field(default=None, alias="if") + then: Optional["SchemaOrBool"] = None + else_: Optional["SchemaOrBool"] = Field(default=None, alias="else") + dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None + prefixItems: Optional[List["SchemaOrBool"]] = None + # MAINTENANCE: uncomment and remove below when deprecating Pydantic v1 + # MAINTENANCE: It generates a list of schemas for tuples, before prefixItems was available + # MAINTENANCE: items: Optional["SchemaOrBool"] = None + items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None + contains: Optional["SchemaOrBool"] = None + properties: Optional[Dict[str, "SchemaOrBool"]] = None + patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None + additionalProperties: Optional["SchemaOrBool"] = None + propertyNames: Optional["SchemaOrBool"] = None + unevaluatedItems: Optional["SchemaOrBool"] = None + unevaluatedProperties: Optional["SchemaOrBool"] = None + # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural + # A Vocabulary for Structural Validation + type: Optional[str] = None # noqa: A003 + enum: Optional[List[Any]] = None + const: Optional[Any] = None + multipleOf: Optional[float] = Field(default=None, gt=0) + maximum: Optional[float] = None + exclusiveMaximum: Optional[float] = None + minimum: Optional[float] = None + exclusiveMinimum: Optional[float] = None + maxLength: Optional[int] = Field(default=None, ge=0) + minLength: Optional[int] = Field(default=None, ge=0) + pattern: Optional[str] = None + maxItems: Optional[int] = Field(default=None, ge=0) + minItems: Optional[int] = Field(default=None, ge=0) + uniqueItems: Optional[bool] = None + maxContains: Optional[int] = Field(default=None, ge=0) + minContains: Optional[int] = Field(default=None, ge=0) + maxProperties: Optional[int] = Field(default=None, ge=0) + minProperties: Optional[int] = Field(default=None, ge=0) + required: Optional[List[str]] = None + dependentRequired: Optional[Dict[str, Set[str]]] = None + # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c + # Vocabularies for Semantic Content With "format" + format: Optional[str] = None # noqa: A003 + # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten + # A Vocabulary for the Contents of String-Encoded Data + contentEncoding: Optional[str] = None + contentMediaType: Optional[str] = None + contentSchema: Optional["SchemaOrBool"] = None + # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta + # A Vocabulary for Basic Meta-Data Annotations + title: Optional[str] = None + description: Optional[str] = None + default: Optional[Any] = None + deprecated: Optional[bool] = None + readOnly: Optional[bool] = None + writeOnly: Optional[bool] = None + examples: Optional[List[Any]] = None + # Ref: OpenAPI 3.0.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#schema-object + # Schema Object + discriminator: Optional[Discriminator] = None + xml: Optional[XML] = None + externalDocs: Optional[ExternalDocumentation] = None + + model_config = MODEL_CONFIG_ALLOW + + +# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents +# A JSON Schema MUST be an object or a boolean. +SchemaOrBool = Union[Schema, bool] + + +# https://swagger.io/specification/#example-object +class Example(BaseModel): + summary: Optional[str] = None + description: Optional[str] = None + value: Optional[Any] = None + externalValue: Optional[AnyUrl] = None + + model_config = MODEL_CONFIG_ALLOW + + +class ParameterInType(Enum): + query = "query" + header = "header" + path = "path" + cookie = "cookie" + + +# https://swagger.io/specification/#encoding-object +class Encoding(BaseModel): + contentType: Optional[str] = None + headers: Optional[Dict[str, Union["Header", Reference]]] = None + style: Optional[str] = None + explode: Optional[bool] = None + allowReserved: Optional[bool] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#media-type-object +class MediaType(BaseModel): + schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") + examples: Optional[Dict[str, Union[Example, Reference]]] = None + encoding: Optional[Dict[str, Encoding]] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#parameter-object +class ParameterBase(BaseModel): + description: Optional[str] = None + required: Optional[bool] = None + deprecated: Optional[bool] = None + # Serialization rules for simple scenarios + style: Optional[str] = None + explode: Optional[bool] = None + allowReserved: Optional[bool] = None + schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") + examples: Optional[Dict[str, Union[Example, Reference]]] = None + # Serialization rules for more complex scenarios + content: Optional[Dict[str, MediaType]] = None + + model_config = MODEL_CONFIG_ALLOW + + +class Parameter(ParameterBase): + name: str + in_: ParameterInType = Field(alias="in") + + +class Header(ParameterBase): + pass + + +# https://swagger.io/specification/#request-body-object +class RequestBody(BaseModel): + description: Optional[str] = None + content: Dict[str, MediaType] + required: Optional[bool] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#link-object +class Link(BaseModel): + operationRef: Optional[str] = None + operationId: Optional[str] = None + parameters: Optional[Dict[str, Union[Any, str]]] = None + requestBody: Optional[Union[Any, str]] = None + description: Optional[str] = None + server: Optional[Server] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#response-object +class Response(BaseModel): + description: str + headers: Optional[Dict[str, Union[Header, Reference]]] = None + content: Optional[Dict[str, MediaType]] = None + links: Optional[Dict[str, Union[Link, Reference]]] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#tag-object +class Tag(BaseModel): + name: str + description: Optional[str] = None + externalDocs: Optional[ExternalDocumentation] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#operation-object +class Operation(OpenAPIExtensions): + tags: Optional[List[str]] = None + summary: Optional[str] = None + description: Optional[str] = None + externalDocs: Optional[ExternalDocumentation] = None + operationId: Optional[str] = None + parameters: Optional[List[Union[Parameter, Reference]]] = None + requestBody: Optional[Union[RequestBody, Reference]] = None + # Using Any for Specification Extensions + responses: Optional[Dict[int, Union[Response, Any]]] = None + callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None + deprecated: Optional[bool] = None + security: Optional[List[Dict[str, List[str]]]] = None + servers: Optional[List[Server]] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#path-item-object +class PathItem(BaseModel): + ref: Optional[str] = Field(default=None, alias="$ref") + summary: Optional[str] = None + description: Optional[str] = None + get: Optional[Operation] = None + put: Optional[Operation] = None + post: Optional[Operation] = None + delete: Optional[Operation] = None + options: Optional[Operation] = None + head: Optional[Operation] = None + patch: Optional[Operation] = None + trace: Optional[Operation] = None + servers: Optional[List[Server]] = None + parameters: Optional[List[Union[Parameter, Reference]]] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#security-scheme-object +class SecuritySchemeType(Enum): + apiKey = "apiKey" + http = "http" + oauth2 = "oauth2" + openIdConnect = "openIdConnect" + mutualTLS = "mutualTLS" + + +class SecurityBase(OpenAPIExtensions): + type_: SecuritySchemeType = Field(alias="type") + description: Optional[str] = None + + model_config = {"extra": "allow", "populate_by_name": True} + + +class APIKeyIn(Enum): + query = "query" + header = "header" + cookie = "cookie" + + +class APIKey(SecurityBase): + type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type") + in_: APIKeyIn = Field(alias="in") + name: str + + +class HTTPBase(SecurityBase): + type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type") + scheme: str + + +class HTTPBearer(HTTPBase): # type: ignore[override] + scheme: Literal["bearer"] = "bearer" + bearerFormat: Optional[str] = None + + +class OAuthFlow(BaseModel): + refreshUrl: Optional[str] = None + scopes: Dict[str, str] = {} + + model_config = MODEL_CONFIG_ALLOW + + +class OAuthFlowImplicit(OAuthFlow): + authorizationUrl: str + + +class OAuthFlowPassword(OAuthFlow): + tokenUrl: str + + +class OAuthFlowClientCredentials(OAuthFlow): + tokenUrl: str + + +class OAuthFlowAuthorizationCode(OAuthFlow): + authorizationUrl: str + tokenUrl: str + + +class OAuthFlows(BaseModel): + implicit: Optional[OAuthFlowImplicit] = None + password: Optional[OAuthFlowPassword] = None + clientCredentials: Optional[OAuthFlowClientCredentials] = None + authorizationCode: Optional[OAuthFlowAuthorizationCode] = None + + model_config = MODEL_CONFIG_ALLOW + + +class OAuth2(SecurityBase): + type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type") + flows: OAuthFlows + + +class OpenIdConnect(SecurityBase): + type_: SecuritySchemeType = Field( + default=SecuritySchemeType.openIdConnect, + alias="type", + ) + openIdConnectUrl: str + + +class MutualTLS(SecurityBase): + type_: SecuritySchemeType = Field(default=SecuritySchemeType.mutualTLS, alias="type") + + +SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer, MutualTLS] + + +# https://swagger.io/specification/#components-object +class Components(BaseModel): + schemas: Optional[Dict[str, Union[Schema, Reference]]] = None + responses: Optional[Dict[str, Union[Response, Reference]]] = None + parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None + examples: Optional[Dict[str, Union[Example, Reference]]] = None + requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None + headers: Optional[Dict[str, Union[Header, Reference]]] = None + securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None + links: Optional[Dict[str, Union[Link, Reference]]] = None + # Using Any for Specification Extensions + callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None + pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None + + model_config = MODEL_CONFIG_ALLOW + + +# https://swagger.io/specification/#openapi-object +class OpenAPI(OpenAPIExtensions): + openapi: str + info: Info + jsonSchemaDialect: Optional[str] = None + servers: Optional[List[Server]] = None + # Using Any for Specification Extensions + paths: Optional[Dict[str, Union[PathItem, Any]]] = None + webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None + components: Optional[Components] = None + security: Optional[List[Dict[str, List[str]]]] = None + tags: Optional[List[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + + model_config = MODEL_CONFIG_ALLOW + + +model_rebuild(Schema) +model_rebuild(Operation) +model_rebuild(Encoding) diff --git a/aws_lambda_powertools/event_handler/openapi/params.py b/aws_lambda_powertools/event_handler/openapi/params.py new file mode 100644 index 00000000000..7b1b1c06f49 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/params.py @@ -0,0 +1,1124 @@ +from __future__ import annotations + +import inspect +from enum import Enum +from typing import TYPE_CHECKING, Any, Literal + +from pydantic import BaseConfig +from pydantic.fields import FieldInfo +from typing_extensions import Annotated, get_args, get_origin + +from aws_lambda_powertools.event_handler import Response +from aws_lambda_powertools.event_handler.openapi.compat import ( + ModelField, + Required, + Undefined, + UndefinedType, + copy_field_info, + field_annotation_is_scalar, + get_annotation_from_field_info, +) + +if TYPE_CHECKING: + from collections.abc import Callable + + from aws_lambda_powertools.event_handler.openapi.models import Example + from aws_lambda_powertools.event_handler.openapi.types import CacheKey + +""" +This turns the low-level function signature into typed, validated Pydantic models for consumption. +""" + + +class ParamTypes(Enum): + query = "query" + header = "header" + path = "path" + cookie = "cookie" + + +# MAINTENANCE: update when deprecating Pydantic v1, remove this alias +_Unset: Any = Undefined + + +class Dependant: + """ + A class used internally to represent a dependency between path operation decorators and the path operation function. + """ + + def __init__( + self, + *, + path_params: list[ModelField] | None = None, + query_params: list[ModelField] | None = None, + header_params: list[ModelField] | None = None, + cookie_params: list[ModelField] | None = None, + body_params: list[ModelField] | None = None, + return_param: ModelField | None = None, + response_extra_models: list[ModelField] | None = None, + name: str | None = None, + call: Callable[..., Any] | None = None, + request_param_name: str | None = None, + websocket_param_name: str | None = None, + http_connection_param_name: str | None = None, + response_param_name: str | None = None, + background_tasks_param_name: str | None = None, + path: str | None = None, + ) -> None: + self.path_params = path_params or [] + self.query_params = query_params or [] + self.header_params = header_params or [] + self.cookie_params = cookie_params or [] + self.body_params = body_params or [] + self.return_param = return_param or None + self.response_extra_models = response_extra_models or [] + self.request_param_name = request_param_name + self.websocket_param_name = websocket_param_name + self.http_connection_param_name = http_connection_param_name + self.response_param_name = response_param_name + self.background_tasks_param_name = background_tasks_param_name + self.name = name + self.call = call + # Store the path to be able to re-generate a dependable from it in overrides + self.path = path + # Save the cache key at creation to optimize performance + self.cache_key: CacheKey = self.call + + +class Param(FieldInfo): + """ + A class used internally to represent a parameter in a path operation. + """ + + in_: ParamTypes + + def __init__( + self, + default: Any = Undefined, + *, + default_factory: Callable[[], Any] | None = _Unset, + annotation: Any | None = None, + alias: str | None = None, + alias_priority: int | None = _Unset, + # MAINTENANCE: update when deprecating Pydantic v1, import these types + # MAINTENANCE: validation_alias: str | AliasPath | AliasChoices | None + validation_alias: str | None = None, + serialization_alias: str | None = None, + title: str | None = None, + description: str | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | None = None, + discriminator: str | None = None, + strict: bool | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + examples: list[Any] | None = None, + openapi_examples: dict[str, Example] | None = None, + deprecated: bool | None = None, + include_in_schema: bool = True, + json_schema_extra: dict[str, Any] | None = None, + **extra: Any, + ): + """ + Constructs a new Param. + + Parameters + ---------- + default: Any + The default value of the parameter + default_factory: Callable[[], Any], optional + Callable that will be called when a default value is needed for this field + annotation: Any, optional + The type annotation of the parameter + alias: str, optional + The public name of the field + alias_priority: int, optional + Priority of the alias. This affects whether an alias generator is used + validation_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for validation only + serialization_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for serialization only + title: str, optional + The title of the parameter + description: str, optional + The description of the parameter + gt: float, optional + Only applies to numbers, required the field to be "greater than" + ge: float, optional + Only applies to numbers, required the field to be "greater than or equal" + lt: float, optional + Only applies to numbers, required the field to be "less than" + le: float, optional + Only applies to numbers, required the field to be "less than or equal" + min_length: int, optional + Only applies to strings, required the field to have a minimum length + max_length: int, optional + Only applies to strings, required the field to have a maximum length + pattern: str, optional + Only applies to strings, requires the field match against a regular expression pattern string + discriminator: str, optional + Parameter field name for discriminating the type in a tagged union + strict: bool, optional + Enables Pydantic's strict mode for the field + multiple_of: float, optional + Only applies to numbers, requires the field to be a multiple of the given value + allow_inf_nan: bool, optional + Only applies to numbers, requires the field to allow infinity and NaN values + max_digits: int, optional + Only applies to Decimals, requires the field to have a maxmium number of digits within the decimal. + decimal_places: int, optional + Only applies to Decimals, requires the field to have at most a number of decimal places + examples: list[Any], optional + A list of examples for the parameter + deprecated: bool, optional + If `True`, the parameter will be marked as deprecated + include_in_schema: bool, optional + If `False`, the parameter will be excluded from the generated OpenAPI schema + json_schema_extra: dict[str, Any], optional + Extra values to include in the generated OpenAPI schema + """ + self.deprecated = deprecated + self.include_in_schema = include_in_schema + + kwargs = dict( + default=default, + default_factory=default_factory, + alias=alias, + title=title, + description=description, + gt=gt, + ge=ge, + lt=lt, + le=le, + min_length=min_length, + max_length=max_length, + discriminator=discriminator, + multiple_of=multiple_of, + allow_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + **extra, + ) + if examples is not None: + kwargs["examples"] = examples + + if openapi_examples is not None: + kwargs["openapi_examples"] = openapi_examples + + current_json_schema_extra = json_schema_extra or extra + + self.openapi_examples = openapi_examples + + kwargs.update( + { + "annotation": annotation, + "alias_priority": alias_priority, + "validation_alias": validation_alias, + "serialization_alias": serialization_alias, + "strict": strict, + "json_schema_extra": current_json_schema_extra, + "pattern": pattern, + }, + ) + + use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset} + + super().__init__(**use_kwargs) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.default})" + + +class Path(Param): + """ + A class used internally to represent a path parameter in a path operation. + """ + + in_ = ParamTypes.path + + def __init__( + self, + default: Any = ..., + *, + default_factory: Callable[[], Any] | None = _Unset, + annotation: Any | None = None, + alias: str | None = None, + alias_priority: int | None = _Unset, + # MAINTENANCE: update when deprecating Pydantic v1, import these types + # MAINTENANCE: validation_alias: str | AliasPath | AliasChoices | None + validation_alias: str | None = None, + serialization_alias: str | None = None, + title: str | None = None, + description: str | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | None = None, + discriminator: str | None = None, + strict: bool | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + examples: list[Any] | None = None, + openapi_examples: dict[str, Example] | None = None, + deprecated: bool | None = None, + include_in_schema: bool = True, + json_schema_extra: dict[str, Any] | None = None, + **extra: Any, + ): + """ + Constructs a new Path param. + + Parameters + ---------- + default: Any + The default value of the parameter + default_factory: Callable[[], Any], optional + Callable that will be called when a default value is needed for this field + annotation: Any, optional + The type annotation of the parameter + alias: str, optional + The public name of the field + alias_priority: int, optional + Priority of the alias. This affects whether an alias generator is used + validation_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for validation only + serialization_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for serialization only + title: str, optional + The title of the parameter + description: str, optional + The description of the parameter + gt: float, optional + Only applies to numbers, required the field to be "greater than" + ge: float, optional + Only applies to numbers, required the field to be "greater than or equal" + lt: float, optional + Only applies to numbers, required the field to be "less than" + le: float, optional + Only applies to numbers, required the field to be "less than or equal" + min_length: int, optional + Only applies to strings, required the field to have a minimum length + max_length: int, optional + Only applies to strings, required the field to have a maximum length + pattern: str, optional + Only applies to strings, requires the field match against a regular expression pattern string + discriminator: str, optional + Parameter field name for discriminating the type in a tagged union + strict: bool, optional + Enables Pydantic's strict mode for the field + multiple_of: float, optional + Only applies to numbers, requires the field to be a multiple of the given value + allow_inf_nan: bool, optional + Only applies to numbers, requires the field to allow infinity and NaN values + max_digits: int, optional + Only applies to Decimals, requires the field to have a maxmium number of digits within the decimal. + decimal_places: int, optional + Only applies to Decimals, requires the field to have at most a number of decimal places + examples: list[Any], optional + A list of examples for the parameter + deprecated: bool, optional + If `True`, the parameter will be marked as deprecated + include_in_schema: bool, optional + If `False`, the parameter will be excluded from the generated OpenAPI schema + json_schema_extra: dict[str, Any], optional + Extra values to include in the generated OpenAPI schema + """ + if default is not ...: + raise AssertionError("Path parameters cannot have a default value") + + super().__init__( + default=default, + default_factory=default_factory, + annotation=annotation, + alias=alias, + alias_priority=alias_priority, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + title=title, + description=description, + gt=gt, + ge=ge, + lt=lt, + le=le, + min_length=min_length, + max_length=max_length, + pattern=pattern, + discriminator=discriminator, + strict=strict, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + deprecated=deprecated, + examples=examples, + openapi_examples=openapi_examples, + include_in_schema=include_in_schema, + json_schema_extra=json_schema_extra, + **extra, + ) + + +class Query(Param): + """ + A class used internally to represent a query parameter in a path operation. + """ + + in_ = ParamTypes.query + + def __init__( + self, + default: Any = _Unset, + *, + default_factory: Callable[[], Any] | None = _Unset, + annotation: Any | None = None, + alias: str | None = None, + alias_priority: int | None = _Unset, + validation_alias: str | None = None, + serialization_alias: str | None = None, + title: str | None = None, + description: str | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | None = None, + discriminator: str | None = None, + strict: bool | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + examples: list[Any] | None = None, + openapi_examples: dict[str, Example] | None = None, + deprecated: bool | None = None, + include_in_schema: bool = True, + json_schema_extra: dict[str, Any] | None = None, + **extra: Any, + ): + """ + Constructs a new Query param. + + Parameters + ---------- + default: Any + The default value of the parameter + default_factory: Callable[[], Any], optional + Callable that will be called when a default value is needed for this field + annotation: Any, optional + The type annotation of the parameter + alias: str, optional + The public name of the field + alias_priority: int, optional + Priority of the alias. This affects whether an alias generator is used + validation_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for validation only + serialization_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for serialization only + title: str, optional + The title of the parameter + description: str, optional + The description of the parameter + gt: float, optional + Only applies to numbers, required the field to be "greater than" + ge: float, optional + Only applies to numbers, required the field to be "greater than or equal" + lt: float, optional + Only applies to numbers, required the field to be "less than" + le: float, optional + Only applies to numbers, required the field to be "less than or equal" + min_length: int, optional + Only applies to strings, required the field to have a minimum length + max_length: int, optional + Only applies to strings, required the field to have a maximum length + pattern: str, optional + Only applies to strings, requires the field match against a regular expression pattern string + discriminator: str, optional + Parameter field name for discriminating the type in a tagged union + strict: bool, optional + Enables Pydantic's strict mode for the field + multiple_of: float, optional + Only applies to numbers, requires the field to be a multiple of the given value + allow_inf_nan: bool, optional + Only applies to numbers, requires the field to allow infinity and NaN values + max_digits: int, optional + Only applies to Decimals, requires the field to have a maxmium number of digits within the decimal. + decimal_places: int, optional + Only applies to Decimals, requires the field to have at most a number of decimal places + examples: list[Any], optional + A list of examples for the parameter + deprecated: bool, optional + If `True`, the parameter will be marked as deprecated + include_in_schema: bool, optional + If `False`, the parameter will be excluded from the generated OpenAPI schema + json_schema_extra: dict[str, Any], optional + Extra values to include in the generated OpenAPI schema + """ + super().__init__( + default=default, + default_factory=default_factory, + annotation=annotation, + alias=alias, + alias_priority=alias_priority, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + title=title, + description=description, + gt=gt, + ge=ge, + lt=lt, + le=le, + min_length=min_length, + max_length=max_length, + pattern=pattern, + discriminator=discriminator, + strict=strict, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + deprecated=deprecated, + examples=examples, + openapi_examples=openapi_examples, + include_in_schema=include_in_schema, + json_schema_extra=json_schema_extra, + **extra, + ) + + +class Header(Param): + """ + A class used internally to represent a header parameter in a path operation. + """ + + in_ = ParamTypes.header + + def __init__( + self, + default: Any = Undefined, + *, + default_factory: Callable[[], Any] | None = _Unset, + annotation: Any | None = None, + alias: str | None = None, + alias_priority: int | None = _Unset, + # MAINTENANCE: update when deprecating Pydantic v1, import these types + # str | AliasPath | AliasChoices | None + validation_alias: str | None = None, + serialization_alias: str | None = None, + convert_underscores: bool = True, + title: str | None = None, + description: str | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | None = None, + discriminator: str | None = None, + strict: bool | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + examples: list[Any] | None = None, + openapi_examples: dict[str, Example] | None = None, + deprecated: bool | None = None, + include_in_schema: bool = True, + json_schema_extra: dict[str, Any] | None = None, + **extra: Any, + ): + """ + Constructs a new Query param. + + Parameters + ---------- + default: Any + The default value of the parameter + default_factory: Callable[[], Any], optional + Callable that will be called when a default value is needed for this field + annotation: Any, optional + The type annotation of the parameter + alias: str, optional + The public name of the field + alias_priority: int, optional + Priority of the alias. This affects whether an alias generator is used + validation_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for validation only + serialization_alias: str | AliasPath | AliasChoices | None, optional + Alias to be used for serialization only + convert_underscores: bool + If true convert "_" to "-" + See RFC: https://www.rfc-editor.org/rfc/rfc9110.html#name-field-name-registry + title: str, optional + The title of the parameter + description: str, optional + The description of the parameter + gt: float, optional + Only applies to numbers, required the field to be "greater than" + ge: float, optional + Only applies to numbers, required the field to be "greater than or equal" + lt: float, optional + Only applies to numbers, required the field to be "less than" + le: float, optional + Only applies to numbers, required the field to be "less than or equal" + min_length: int, optional + Only applies to strings, required the field to have a minimum length + max_length: int, optional + Only applies to strings, required the field to have a maximum length + pattern: str, optional + Only applies to strings, requires the field match against a regular expression pattern string + discriminator: str, optional + Parameter field name for discriminating the type in a tagged union + strict: bool, optional + Enables Pydantic's strict mode for the field + multiple_of: float, optional + Only applies to numbers, requires the field to be a multiple of the given value + allow_inf_nan: bool, optional + Only applies to numbers, requires the field to allow infinity and NaN values + max_digits: int, optional + Only applies to Decimals, requires the field to have a maxmium number of digits within the decimal. + decimal_places: int, optional + Only applies to Decimals, requires the field to have at most a number of decimal places + examples: list[Any], optional + A list of examples for the parameter + deprecated: bool, optional + If `True`, the parameter will be marked as deprecated + include_in_schema: bool, optional + If `False`, the parameter will be excluded from the generated OpenAPI schema + json_schema_extra: dict[str, Any], optional + Extra values to include in the generated OpenAPI schema + """ + self.convert_underscores = convert_underscores + self._alias = alias + + super().__init__( + default=default, + default_factory=default_factory, + annotation=annotation, + alias=self._alias, + alias_priority=alias_priority, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + title=title, + description=description, + gt=gt, + ge=ge, + lt=lt, + le=le, + min_length=min_length, + max_length=max_length, + pattern=pattern, + discriminator=discriminator, + strict=strict, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + deprecated=deprecated, + examples=examples, + openapi_examples=openapi_examples, + include_in_schema=include_in_schema, + json_schema_extra=json_schema_extra, + **extra, + ) + + @property + def alias(self): + return self._alias + + @alias.setter + def alias(self, value: str | None = None): + if value is not None: + # Headers are case-insensitive according to RFC 7540 (HTTP/2), so we lower the parameter name + # This ensures that customers can access headers with any casing, as per the RFC guidelines. + # Reference: https://www.rfc-editor.org/rfc/rfc7540#section-8.1.2 + self._alias = value.lower() + + +class Body(FieldInfo): + """ + A class used internally to represent a body parameter in a path operation. + """ + + def __init__( + self, + default: Any = Undefined, + *, + default_factory: Callable[[], Any] | None = _Unset, + annotation: Any | None = None, + embed: bool = False, + media_type: str = "application/json", + alias: str | None = None, + alias_priority: int | None = _Unset, + # MAINTENANCE: update when deprecating Pydantic v1, import these types + # str | AliasPath | AliasChoices | None + validation_alias: str | None = None, + serialization_alias: str | None = None, + title: str | None = None, + description: str | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | None = None, + discriminator: str | None = None, + strict: bool | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + examples: list[Any] | None = None, + openapi_examples: dict[str, Example] | None = None, + deprecated: bool | None = None, + include_in_schema: bool = True, + json_schema_extra: dict[str, Any] | None = None, + **extra: Any, + ): + self.embed = embed + self.media_type = media_type + self.deprecated = deprecated + self.include_in_schema = include_in_schema + kwargs = dict( + default=default, + default_factory=default_factory, + alias=alias, + title=title, + description=description, + gt=gt, + ge=ge, + lt=lt, + le=le, + min_length=min_length, + max_length=max_length, + discriminator=discriminator, + multiple_of=multiple_of, + allow_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + **extra, + ) + if examples is not None: + kwargs["examples"] = examples + current_json_schema_extra = json_schema_extra or extra + + kwargs.update( + { + "annotation": annotation, + "alias_priority": alias_priority, + "validation_alias": validation_alias, + "serialization_alias": serialization_alias, + "strict": strict, + "json_schema_extra": current_json_schema_extra, + "pattern": pattern, + }, + ) + + use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset} + + super().__init__(**use_kwargs) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.default})" + + +class _Form(Body): + """ + A class used internally to represent a form parameter in a path operation. + """ + + def __init__( + self, + default: Any = Undefined, + *, + default_factory: Callable[[], Any] | None = _Unset, + annotation: Any | None = None, + media_type: str = "application/x-www-form-urlencoded", + alias: str | None = None, + alias_priority: int | None = _Unset, + # MAINTENANCE: update when deprecating Pydantic v1, import these types + # str | AliasPath | AliasChoices | None + validation_alias: str | None = None, + serialization_alias: str | None = None, + title: str | None = None, + description: str | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | None = None, + discriminator: str | None = None, + strict: bool | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + examples: list[Any] | None = None, + deprecated: bool | None = None, + include_in_schema: bool = True, + json_schema_extra: dict[str, Any] | None = None, + **extra: Any, + ): + super().__init__( + default=default, + default_factory=default_factory, + annotation=annotation, + embed=True, + media_type=media_type, + alias=alias, + alias_priority=alias_priority, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + title=title, + description=description, + gt=gt, + ge=ge, + lt=lt, + le=le, + min_length=min_length, + max_length=max_length, + pattern=pattern, + discriminator=discriminator, + strict=strict, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + deprecated=deprecated, + examples=examples, + include_in_schema=include_in_schema, + json_schema_extra=json_schema_extra, + **extra, + ) + + +class _File(_Form): + """ + A class used internally to represent a file parameter in a path operation. + """ + + def __init__( + self, + default: Any = Undefined, + *, + default_factory: Callable[[], Any] | None = _Unset, + annotation: Any | None = None, + media_type: str = "multipart/form-data", + alias: str | None = None, + alias_priority: int | None = _Unset, + # MAINTENANCE: update when deprecating Pydantic v1, import these types + # str | AliasPath | AliasChoices | None + validation_alias: str | None = None, + serialization_alias: str | None = None, + title: str | None = None, + description: str | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | None = None, + discriminator: str | None = None, + strict: bool | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + examples: list[Any] | None = None, + deprecated: bool | None = None, + include_in_schema: bool = True, + json_schema_extra: dict[str, Any] | None = None, + **extra: Any, + ): + super().__init__( + default=default, + default_factory=default_factory, + annotation=annotation, + media_type=media_type, + alias=alias, + alias_priority=alias_priority, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + title=title, + description=description, + gt=gt, + ge=ge, + lt=lt, + le=le, + min_length=min_length, + max_length=max_length, + pattern=pattern, + discriminator=discriminator, + strict=strict, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + deprecated=deprecated, + examples=examples, + include_in_schema=include_in_schema, + json_schema_extra=json_schema_extra, + **extra, + ) + + +def get_flat_dependant( + dependant: Dependant, + visited: list[CacheKey] | None = None, +) -> Dependant: + """ + Flatten a recursive Dependant model structure. + + This function recursively concatenates the parameter fields of a Dependant model and its dependencies into a flat + Dependant structure. This is useful for scenarios like parameter validation where the nested structure is not + relevant. + + Parameters + ---------- + dependant: Dependant + The dependant model to flatten + visited: list[CacheKey], optional + Keeps track of visited Dependents to avoid infinite recursion. Defaults to empty list. + + Returns + ------- + Dependant + The flattened Dependant model + """ + if visited is None: + visited = [] + visited.append(dependant.cache_key) + + return Dependant( + path_params=dependant.path_params.copy(), + query_params=dependant.query_params.copy(), + header_params=dependant.header_params.copy(), + cookie_params=dependant.cookie_params.copy(), + body_params=dependant.body_params.copy(), + path=dependant.path, + ) + + +def analyze_param( + *, + param_name: str, + annotation: Any, + value: Any, + is_path_param: bool, + is_response_param: bool, +) -> ModelField | None: + """ + Analyze a parameter annotation and value to determine the type and default value of the parameter. + + Parameters + ---------- + param_name: str + The name of the parameter + annotation + The annotation of the parameter + value + The value of the parameter + is_path_param + Whether the parameter is a path parameter + is_response_param + Whether the parameter is the return annotation + + Returns + ------- + ModelField | None + The type annotation and the Pydantic field representing the parameter + """ + field_info, type_annotation = get_field_info_and_type_annotation( + annotation, + value, + is_path_param, + is_response_param, + ) + + # If the value is a FieldInfo, we use it as the FieldInfo for the parameter + if isinstance(value, FieldInfo): + if field_info is not None: + raise AssertionError("Cannot use a FieldInfo as a parameter annotation and pass a FieldInfo as a value") + field_info = value + + field_info.annotation = type_annotation # type: ignore[attr-defined,unused-ignore] + + # If we didn't determine the FieldInfo yet, we create a default one + if field_info is None: + default_value = value if value is not inspect.Signature.empty else Required + + # Check if the parameter is part of the path. Otherwise, defaults to query. + if is_path_param: + field_info = Path(annotation=type_annotation) + elif not field_annotation_is_scalar(annotation=type_annotation): + field_info = Body(annotation=type_annotation, default=default_value) + else: + field_info = Query(annotation=type_annotation, default=default_value) + + # When we have a response field, we need to set the default value to Required + if is_response_param: + field_info.default = Required + + field = _create_model_field(field_info, type_annotation, param_name, is_path_param) + return field + + +def get_field_info_and_type_annotation( + annotation, + value, + is_path_param: bool, + is_response_param: bool, +) -> tuple[FieldInfo | None, Any]: + """ + Get the FieldInfo and type annotation from an annotation and value. + """ + field_info: FieldInfo | None = None + type_annotation: Any = Any + + if annotation is not inspect.Signature.empty: + # If the annotation is an Annotated type, we need to extract the type annotation and the FieldInfo + if get_origin(annotation) is Annotated: + field_info, type_annotation = get_field_info_annotated_type(annotation, value, is_path_param) + # If the annotation is a Response type, we recursively call this function with the inner type + elif get_origin(annotation) is Response: + field_info, type_annotation = get_field_info_response_type(annotation, value) + # If the response param is a tuple with two elements, we use the first element as the type annotation, + # just like we did in the APIGateway._to_response + elif is_response_param and get_origin(annotation) is tuple and len(get_args(annotation)) == 2: + field_info, type_annotation = get_field_info_tuple_type(annotation, value) + # If the annotation is not an Annotated type, we use it as the type annotation + else: + type_annotation = annotation + + return field_info, type_annotation + + +def get_field_info_tuple_type(annotation, value) -> tuple[FieldInfo | None, Any]: + (inner_type, _) = get_args(annotation) + + # If the inner type is an Annotated type, we need to extract the type annotation and the FieldInfo + if get_origin(inner_type) is Annotated: + return get_field_info_annotated_type(inner_type, value, False) + + return None, inner_type + + +def get_field_info_response_type(annotation, value) -> tuple[FieldInfo | None, Any]: + # Example: get_args(Response[inner_type]) == (inner_type,) # noqa: ERA001 + (inner_type,) = get_args(annotation) + + # Recursively resolve the inner type + return get_field_info_and_type_annotation(inner_type, value, False, True) + + +def get_field_info_annotated_type(annotation, value, is_path_param: bool) -> tuple[FieldInfo | None, Any]: + """ + Get the FieldInfo and type annotation from an Annotated type. + """ + field_info: FieldInfo | None = None + annotated_args = get_args(annotation) + type_annotation = annotated_args[0] + powertools_annotations = [arg for arg in annotated_args[1:] if isinstance(arg, FieldInfo)] + + if len(powertools_annotations) > 1: + raise AssertionError("Only one FieldInfo can be used per parameter") + + powertools_annotation = next(iter(powertools_annotations), None) + + if isinstance(powertools_annotation, FieldInfo): + # Copy `field_info` because we mutate `field_info.default` later + field_info = copy_field_info( + field_info=powertools_annotation, + annotation=annotation, + ) + if field_info.default not in [Undefined, Required]: + raise AssertionError("FieldInfo needs to have a default value of Undefined or Required") + + if value is not inspect.Signature.empty: + if is_path_param: + raise AssertionError("Cannot use a FieldInfo as a path parameter and pass a value") + field_info.default = value + else: + field_info.default = Required + + return field_info, type_annotation + + +def create_response_field( + name: str, + type_: type[Any], + default: Any | None = Undefined, + required: bool | UndefinedType = Undefined, + model_config: type[BaseConfig] = BaseConfig, + field_info: FieldInfo | None = None, + alias: str | None = None, + mode: Literal["validation", "serialization"] = "validation", +) -> ModelField: + """ + Create a new response field. Raises if type_ is invalid. + """ + field_info = field_info or FieldInfo( + annotation=type_, + default=default, + alias=alias, + ) + + kwargs = {"name": name, "field_info": field_info, "mode": mode} + + return ModelField(**kwargs) # type: ignore[arg-type] + + +def _create_model_field( + field_info: FieldInfo | None, + type_annotation: Any, + param_name: str, + is_path_param: bool, +) -> ModelField | None: + """ + Create a new ModelField from a FieldInfo and type annotation. + """ + if field_info is None: + return None + + if is_path_param: + if not isinstance(field_info, Path): + raise AssertionError("Path parameters must be of type Path") + elif isinstance(field_info, Param) and getattr(field_info, "in_", None) is None: + field_info.in_ = ParamTypes.query + + # If the field_info is a Param, we use the `in_` attribute to determine the type annotation + use_annotation = get_annotation_from_field_info(type_annotation, field_info, param_name) + + # If the field doesn't have a defined alias, we use the param name + if not field_info.alias and getattr(field_info, "convert_underscores", None): + alias = param_name.replace("_", "-") + else: + alias = field_info.alias or param_name + field_info.alias = alias + + return create_response_field( + name=param_name, + type_=use_annotation, + default=field_info.default, + alias=alias, + required=field_info.default in (Required, Undefined), + field_info=field_info, + ) diff --git a/aws_lambda_powertools/event_handler/openapi/pydantic_loader.py b/aws_lambda_powertools/event_handler/openapi/pydantic_loader.py new file mode 100644 index 00000000000..225f7e88096 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/pydantic_loader.py @@ -0,0 +1,6 @@ +try: + from pydantic.version import VERSION as PYDANTIC_VERSION + + PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.") +except ImportError: + PYDANTIC_V2 = False # pragma: no cover # false positive; dropping in v3 diff --git a/aws_lambda_powertools/event_handler/openapi/swagger_ui/__init__.py b/aws_lambda_powertools/event_handler/openapi/swagger_ui/__init__.py new file mode 100644 index 00000000000..bc6eda8abb3 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/swagger_ui/__init__.py @@ -0,0 +1,13 @@ +from aws_lambda_powertools.event_handler.openapi.swagger_ui.html import ( + generate_swagger_html, +) +from aws_lambda_powertools.event_handler.openapi.swagger_ui.oauth2 import ( + OAuth2Config, + generate_oauth2_redirect_html, +) + +__all__ = [ + "generate_swagger_html", + "generate_oauth2_redirect_html", + "OAuth2Config", +] diff --git a/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py b/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py new file mode 100644 index 00000000000..6bcbcff50a4 --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from aws_lambda_powertools.event_handler.openapi.swagger_ui.oauth2 import OAuth2Config + + +def generate_swagger_html( + spec: str, + swagger_js: str, + swagger_css: str, + swagger_base_url: str, + oauth2_config: OAuth2Config | None, + persist_authorization: bool = False, +) -> str: + """ + Generate Swagger UI HTML page + + Parameters + ---------- + spec: str + The OpenAPI spec + swagger_js: str + Swagger UI JavaScript source code or URL + swagger_css: str + Swagger UI CSS source code or URL + swagger_base_url: str + The base URL for Swagger UI + oauth2_config: OAuth2Config, optional + The OAuth2 configuration. + persist_authorization: bool, optional + Whether to persist authorization data on browser close/refresh. + """ + + # If Swagger base URL is present, generate HTML content with linked CSS and JavaScript files + # If no Swagger base URL is provided, include CSS and JavaScript directly in the HTML + if swagger_base_url: + swagger_css_content = f"<link rel='stylesheet' type='text/css' href='{swagger_css}'>" + swagger_js_content = f"<script src='{swagger_js}'></script>" + else: + swagger_css_content = f"<style>{swagger_css}</style>" + swagger_js_content = f"<script>{swagger_js}</script>" + + # Prepare oauth2 config + oauth2_content = ( + f"ui.initOAuth({oauth2_config.json(exclude_none=True, exclude_unset=True)});" if oauth2_config else "" + ) + + return f""" +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Swagger UI + + {swagger_css_content} + + + +
+ Loading... +
+ + +{swagger_js_content} + + + + """.strip() diff --git a/aws_lambda_powertools/event_handler/openapi/swagger_ui/oauth2.py b/aws_lambda_powertools/event_handler/openapi/swagger_ui/oauth2.py new file mode 100644 index 00000000000..4cafdfe401c --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/swagger_ui/oauth2.py @@ -0,0 +1,154 @@ +# ruff: noqa: E501 FA100 +import warnings +from typing import Dict, Optional, Sequence + +from pydantic import BaseModel, Field, field_validator + +from aws_lambda_powertools.event_handler.openapi.models import ( + MODEL_CONFIG_ALLOW, +) +from aws_lambda_powertools.shared.functions import powertools_dev_is_set + + +# Based on https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/ +class OAuth2Config(BaseModel): + """ + OAuth2 configuration for Swagger UI + """ + + # The client ID for the OAuth2 application + clientId: Optional[str] = Field(alias="client_id", default=None) + + # The client secret for the OAuth2 application. This is sensitive information and requires the explicit presence + # of the POWERTOOLS_DEV environment variable. + clientSecret: Optional[str] = Field(alias="client_secret", default=None) + + # The realm in which the OAuth2 application is registered. Optional. + realm: Optional[str] = Field(default=None) + + # The name of the OAuth2 application + appName: str = Field(alias="app_name") + + # The scopes that the OAuth2 application requires. Defaults to an empty list. + scopes: Sequence[str] = Field(default=[]) + + # Additional query string parameters to be included in the OAuth2 request. Defaults to an empty dictionary. + additionalQueryStringParams: Dict[str, str] = Field(alias="additional_query_string_params", default={}) + + # Whether to use basic authentication with the access code grant type. Defaults to False. + useBasicAuthenticationWithAccessCodeGrant: bool = Field( + alias="use_basic_authentication_with_access_code_grant", + default=False, + ) + + # Whether to use PKCE with the authorization code grant type. Defaults to False. + usePkceWithAuthorizationCodeGrant: bool = Field(alias="use_pkce_with_authorization_code_grant", default=False) + + model_config = MODEL_CONFIG_ALLOW + + @field_validator("clientSecret") + def client_secret_only_on_dev(cls, v: Optional[str]) -> Optional[str]: + if not v: + return None + + if not powertools_dev_is_set(): + raise ValueError( + "cannot use client_secret without POWERTOOLS_DEV mode. See " + "https://docs.powertools.aws.dev/lambda/python/latest/#optimizing-for-non-production-environments", + ) + else: + warnings.warn( + "OAuth2Config is using client_secret and POWERTOOLS_DEV is set. This reveals sensitive information. " + "DO NOT USE THIS OUTSIDE LOCAL DEVELOPMENT", + stacklevel=2, + ) + return v + + +def generate_oauth2_redirect_html() -> str: + """ + Generates the HTML content for the OAuth2 redirect page. + + Source: https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html + """ + return """ + + + + Swagger UI: OAuth2 Redirect + + + + + + """.strip() diff --git a/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui-bundle.min.js b/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui-bundle.min.js new file mode 100644 index 00000000000..bc1d328090c --- /dev/null +++ b/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui-bundle.min.js @@ -0,0 +1,12 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,()=>{var s,i,n={69119:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BLANK_URL=t.relativeFirstCharacters=t.urlSchemeRegex=t.ctrlCharactersRegex=t.htmlCtrlEntityRegex=t.htmlEntitiesRegex=t.invalidProtocolRegex=void 0,t.invalidProtocolRegex=/^([^\w]*)(javascript|data|vbscript)/im,t.htmlEntitiesRegex=/&#(\w+)(^\w|;)?/g,t.htmlCtrlEntityRegex=/&(newline|tab);/gi,t.ctrlCharactersRegex=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,t.urlSchemeRegex=/^.+(:|:)/gim,t.relativeFirstCharacters=[".","/"],t.BLANK_URL="about:blank"},16750:(e,t,n)=>{"use strict";t.J=void 0;var r=n(69119);t.J=function(e){if(!e)return r.BLANK_URL;e=e.replace(r.ctrlCharactersRegex,"").replace(r.htmlEntitiesRegex,function(e,t){return String.fromCharCode(t)}).replace(r.htmlCtrlEntityRegex,"").replace(r.ctrlCharactersRegex,"").trim();if(!e)return r.BLANK_URL;if(-1{"use strict";t.byteLength=function(e){var e=u(e),t=e[0],e=e[1];return 3*(t+e)/4-e},t.toByteArray=function(e){for(var t,n=u(e),r=n[0],n=n[1],o=new c(3*(r+n)/4-n),s=0,i=0>16&255,o[s++]=t>>8&255,o[s++]=255&t;return 2===n&&(t=l[e.charCodeAt(a)]<<2|l[e.charCodeAt(a+1)]>>4,o[s++]=255&t),1===n&&(t=l[e.charCodeAt(a)]<<10|l[e.charCodeAt(a+1)]<<4|l[e.charCodeAt(a+2)]>>2,o[s++]=t>>8&255,o[s++]=255&t),o},t.fromByteArray=function(e){for(var t,n=e.length,r=n%3,o=[],s=0,i=n-r;s>18&63]+a[r>>12&63]+a[r>>6&63]+a[63&r]);return o.join("")}(e,s,i>2]+a[t<<4&63]+"==")):2==r&&(t=(e[n-2]<<8)+e[n-1],o.push(a[t>>10]+a[t>>4&63]+a[t<<2&63]+"=")),o.join("")};for(var a=[],l=[],c="undefined"!=typeof Uint8Array?Uint8Array:Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r=0;r<64;++r)a[r]=n[r],l[n.charCodeAt(r)]=r;function u(e){var t=e.length;if(0{"use strict";const g=e(67526),s=e(251),t="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null,r=(n.Buffer=u,n.SlowBuffer=function(e){return u.alloc(+(e=+e!=e?0:e))},n.INSPECT_MAX_BYTES=50,2147483647);function c(e){if(e>r)throw new RangeError('The value "'+e+'" is invalid for option "size"');e=new Uint8Array(e);return Object.setPrototypeOf(e,u.prototype),e}function u(e,t,n){if("number"!=typeof e)return o(e,t,n);if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return a(e)}function o(t,n,e){if("string"==typeof t){var r=t,o=n;if(!u.isEncoding(o="string"==typeof o&&""!==o?o:"utf8"))throw new TypeError("Unknown encoding: "+o);var s=0|f(r,o);let e=c(s);return r=e.write(r,o),e=r!==s?e.slice(0,r):e}if(ArrayBuffer.isView(t))return T(o=t,Uint8Array)?h((s=new Uint8Array(o)).buffer,s.byteOffset,s.byteLength):p(o);if(null==t)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if(T(t,ArrayBuffer)||t&&T(t.buffer,ArrayBuffer))return h(t,n,e);if("undefined"!=typeof SharedArrayBuffer&&(T(t,SharedArrayBuffer)||t&&T(t.buffer,SharedArrayBuffer)))return h(t,n,e);if("number"==typeof t)throw new TypeError('The "value" argument must not be of type number. Received type number');r=t.valueOf&&t.valueOf();if(null!=r&&r!==t)return u.from(r,n,e);var i,a,l=u.isBuffer(i=t)?(0!==(a=c(l=0|d(i.length))).length&&i.copy(a,0,0,l),a):void 0!==i.length?"number"!=typeof i.length||R(i.length)?c(0):p(i):"Buffer"===i.type&&Array.isArray(i.data)?p(i.data):void 0;if(l)return l;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof t[Symbol.toPrimitive])return u.from(t[Symbol.toPrimitive]("string"),n,e);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t)}function i(e){if("number"!=typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function a(e){return i(e),c(e<0?0:0|d(e))}function p(t){const n=t.length<0?0:0|d(t.length),r=c(n);for(let e=0;e=r)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+r.toString(16)+" bytes");return 0|e}function f(e,t){if(u.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||T(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);var n=e.length,r=2>>1;case"base64":return $(e).length;default:if(o)return r?-1:N(e).length;t=(""+t).toLowerCase(),o=!0}}function F(e,n,r){let t=!1;if((n=void 0===n||n<0?0:n)>this.length)return"";if((r=void 0===r||r>this.length?this.length:r)<=0)return"";if((r>>>=0)<=(n>>>=0))return"";for(e=e||"utf8";;)switch(e){case"hex":{var o=this;var s=n;var i=r;var a=o.length;(!s||s<0)&&(s=0),(!i||i<0||a=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=u.from(t,r)),u.isBuffer(t))return 0===t.length?-1:y(e,t,n,r,o);if("number"==typeof t)return t&=255,"function"==typeof Uint8Array.prototype.indexOf?(o?Uint8Array.prototype.indexOf:Uint8Array.prototype.lastIndexOf).call(e,t,n):y(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function y(n,r,t,e,o){let s,i=1,a=n.length,l=r.length;if(void 0!==e&&("ucs2"===(e=String(e).toLowerCase())||"ucs-2"===e||"utf16le"===e||"utf-16le"===e)){if(n.length<2||r.length<2)return-1;i=2,a/=2,l/=2,t/=2}function c(e,t){return 1===i?e[t]:e.readUInt16BE(t*i)}if(o){let e=-1;for(s=t;sa&&(t=a-l),s=t;0<=s;s--){let t=!0;for(let e=0;e>>10&1023|55296),o=56320|1023&o),n.push(o),l+=s}{var r=n,o=r.length;if(o<=b)return String.fromCharCode.apply(String,r);let e="",t=0;for(;tr.length?(e=u.isBuffer(e)?e:u.from(e)).copy(r,o):Uint8Array.prototype.set.call(r,e,o);else{if(!u.isBuffer(e))throw new TypeError('"list" argument must be an Array of Buffers');e.copy(r,o)}o+=e.length}return r},u.byteLength=f,u.prototype._isBuffer=!0,u.prototype.swap16=function(){var t=this.length;if(t%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let e=0;et&&(e+=" ... "),""},t&&(u.prototype[t]=u.prototype.inspect),u.prototype.compare=function(e,t,n,r,o){if(T(e,Uint8Array)&&(e=u.from(e,e.offset,e.byteLength)),!u.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),(t=void 0===t?0:t)<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(o<=r&&n<=t)return 0;if(o<=r)return-1;if(n<=t)return 1;if(this===e)return 0;let s=(o>>>=0)-(r>>>=0),i=(n>>>=0)-(t>>>=0);var a=Math.min(s,i),l=this.slice(r,o),c=e.slice(t,n);for(let e=0;e>>=0,isFinite(r)?(r>>>=0,void 0===e&&(e="utf8")):(e=r,r=void 0)}var o,s,i,a,l,c,u=this.length-n;if((void 0===r||uthis.length)throw new RangeError("Attempt to write outside buffer bounds");e=e||"utf8";let p=!1;for(;;)switch(e){case"hex":{var h=this;var d=t;var f=n;var m=r;f=Number(f)||0;var g=h.length-f,g=((!m||(m=Number(m))>g)&&(m=g),d.length);let e;for(g/2>8,s.push(r%256),s.push(o);return s}(t,(o=this).length-s),o,s,i);default:if(p)throw new TypeError("Unknown encoding: "+e);e=(""+e).toLowerCase(),p=!0}},u.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const b=4096;function w(e,t,n){if(e%1!=0||e<0)throw new RangeError("offset is not uint");if(ne.length)throw new RangeError("Index out of range")}function S(e,t,n,r,o){L(t,r,o,e,n,7);r=Number(t&BigInt(4294967295)),e[n++]=r,e[n++]=r>>=8,e[n++]=r>>=8,e[n++]=r>>=8,o=Number(t>>BigInt(32)&BigInt(4294967295));return e[n++]=o,e[n++]=o>>=8,e[n++]=o>>=8,e[n++]=o>>=8,n}function x(e,t,n,r,o){L(t,r,o,e,n,7);r=Number(t&BigInt(4294967295)),e[n+7]=r,e[n+6]=r>>=8,e[n+5]=r>>=8,e[n+4]=r>>=8,o=Number(t>>BigInt(32)&BigInt(4294967295));return e[n+3]=o,e[n+2]=o>>=8,e[n+1]=o>>=8,e[n]=o>>=8,n+8}function _(e,t,n,r){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function k(e,t,n,r,o){return t=+t,n>>>=0,o||_(e,0,n,4),s.write(e,t,n,r,23,4),n+4}function A(e,t,n,r,o){return t=+t,n>>>=0,o||_(e,0,n,8),s.write(e,t,n,r,52,8),n+8}u.prototype.slice=function(e,t){var n=this.length,n=((e=~~e)<0?(e+=n)<0&&(e=0):n>>=0,t>>>=0,n||w(e,t,this.length);let r=this[e],o=1,s=0;for(;++s>>=0,t>>>=0,n||w(e,t,this.length);let r=this[e+--t],o=1;for(;0>>=0,t||w(e,1,this.length),this[e]},u.prototype.readUint16LE=u.prototype.readUInt16LE=function(e,t){return e>>>=0,t||w(e,2,this.length),this[e]|this[e+1]<<8},u.prototype.readUint16BE=u.prototype.readUInt16BE=function(e,t){return e>>>=0,t||w(e,2,this.length),this[e]<<8|this[e+1]},u.prototype.readUint32LE=u.prototype.readUInt32LE=function(e,t){return e>>>=0,t||w(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},u.prototype.readUint32BE=u.prototype.readUInt32BE=function(e,t){return e>>>=0,t||w(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},u.prototype.readBigUInt64LE=M(function(e){j(e>>>=0,"offset");var t=this[e],n=this[e+7],t=(void 0!==t&&void 0!==n||P(e,this.length-8),t+256*this[++e]+65536*this[++e]+this[++e]*2**24),e=this[++e]+256*this[++e]+65536*this[++e]+n*2**24;return BigInt(t)+(BigInt(e)<>>=0,"offset");var t=this[e],n=this[e+7],t=(void 0!==t&&void 0!==n||P(e,this.length-8),t*2**24+65536*this[++e]+256*this[++e]+this[++e]),e=this[++e]*2**24+65536*this[++e]+256*this[++e]+n;return(BigInt(t)<>>=0,t>>>=0,n||w(e,t,this.length);let r=this[e],o=1,s=0;for(;++s=o&&(r-=Math.pow(2,8*t)),r},u.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||w(e,t,this.length);let r=t,o=1,s=this[e+--r];for(;0=o&&(s-=Math.pow(2,8*t)),s},u.prototype.readInt8=function(e,t){return e>>>=0,t||w(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},u.prototype.readInt16LE=function(e,t){e>>>=0,t||w(e,2,this.length);t=this[e]|this[e+1]<<8;return 32768&t?4294901760|t:t},u.prototype.readInt16BE=function(e,t){e>>>=0,t||w(e,2,this.length);t=this[e+1]|this[e]<<8;return 32768&t?4294901760|t:t},u.prototype.readInt32LE=function(e,t){return e>>>=0,t||w(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},u.prototype.readInt32BE=function(e,t){return e>>>=0,t||w(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},u.prototype.readBigInt64LE=M(function(e){j(e>>>=0,"offset");var t=this[e],n=this[e+7],n=(void 0!==t&&void 0!==n||P(e,this.length-8),this[e+4]+256*this[e+5]+65536*this[e+6]+(n<<24));return(BigInt(n)<>>=0,"offset");var t=this[e],n=this[e+7],t=(void 0!==t&&void 0!==n||P(e,this.length-8),(t<<24)+65536*this[++e]+256*this[++e]+this[++e]);return(BigInt(t)<>>=0,t||w(e,4,this.length),s.read(this,e,!0,23,4)},u.prototype.readFloatBE=function(e,t){return e>>>=0,t||w(e,4,this.length),s.read(this,e,!1,23,4)},u.prototype.readDoubleLE=function(e,t){return e>>>=0,t||w(e,8,this.length),s.read(this,e,!0,52,8)},u.prototype.readDoubleBE=function(e,t){return e>>>=0,t||w(e,8,this.length),s.read(this,e,!1,52,8)},u.prototype.writeUintLE=u.prototype.writeUIntLE=function(e,t,n,r){e=+e,t>>>=0,n>>>=0,r||E(this,e,t,n,Math.pow(2,8*n)-1,0);let o=1,s=0;for(this[t]=255&e;++s>>=0,n>>>=0,r||E(this,e,t,n,Math.pow(2,8*n)-1,0);let o=n-1,s=1;for(this[t+o]=255&e;0<=--o&&(s*=256);)this[t+o]=e/s&255;return t+n},u.prototype.writeUint8=u.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,1,255,0),this[t]=255&e,t+1},u.prototype.writeUint16LE=u.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},u.prototype.writeUint16BE=u.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},u.prototype.writeUint32LE=u.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},u.prototype.writeUint32BE=u.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},u.prototype.writeBigUInt64LE=M(function(e,t=0){return S(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))}),u.prototype.writeBigUInt64BE=M(function(e,t=0){return x(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))}),u.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);E(this,e,t,n,r-1,-r)}let o=0,s=1,i=0;for(this[t]=255&e;++o>0)-i&255;return t+n},u.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);E(this,e,t,n,r-1,-r)}let o=n-1,s=1,i=0;for(this[t+o]=255&e;0<=--o&&(s*=256);)e<0&&0===i&&0!==this[t+o+1]&&(i=1),this[t+o]=(e/s>>0)-i&255;return t+n},u.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,1,127,-128),this[t]=255&(e=e<0?255+e+1:e),t+1},u.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},u.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},u.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},u.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||E(this,e,t,4,2147483647,-2147483648),this[t]=(e=e<0?4294967295+e+1:e)>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},u.prototype.writeBigInt64LE=M(function(e,t=0){return S(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))}),u.prototype.writeBigInt64BE=M(function(e,t=0){return x(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))}),u.prototype.writeFloatLE=function(e,t,n){return k(this,e,t,!0,n)},u.prototype.writeFloatBE=function(e,t,n){return k(this,e,t,!1,n)},u.prototype.writeDoubleLE=function(e,t,n){return A(this,e,t,!0,n)},u.prototype.writeDoubleBE=function(e,t,n){return A(this,e,t,!1,n)},u.prototype.copy=function(e,t,n,r){if(!u.isBuffer(e))throw new TypeError("argument should be a Buffer");if(n=n||0,r||0===r||(r=this.length),t>=e.length&&(t=e.length),(r=0=this.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length);var o=(r=e.length-t>>=0,n=void 0===n?this.length:n>>>0,"number"==typeof(e=e||0))for(o=t;o=4+r;n-=3)t="_"+e.slice(n-3,n)+t;return""+e.slice(0,n)+t}function L(e,t,n,r,o,s){if(n= 0${r} and < 2${r} ** `+8*(s+1)+r:`>= -(2${r} ** ${8*(s+1)-1}${r}) and < 2 ** `+(8*(s+1)-1)+r:`>= ${t}${r} and <= `+n+r;throw new O.ERR_OUT_OF_RANGE("value",t,e)}n=r,t=s,j(e=o,"offset"),void 0!==n[e]&&void 0!==n[e+t]||P(e,n.length-(t+1))}function j(e,t){if("number"!=typeof e)throw new O.ERR_INVALID_ARG_TYPE(t,"number",e)}function P(e,t,n){if(Math.floor(e)!==e)throw j(e,n),new O.ERR_OUT_OF_RANGE(n||"offset","an integer",e);if(t<0)throw new O.ERR_BUFFER_OUT_OF_BOUNDS;throw new O.ERR_OUT_OF_RANGE(n||"offset",`>= ${n?1:0} and <= `+t,e)}C("ERR_BUFFER_OUT_OF_BOUNDS",function(e){return e?e+" is outside of buffer bounds":"Attempt to access memory outside buffer bounds"},RangeError),C("ERR_INVALID_ARG_TYPE",function(e,t){return`The "${e}" argument must be of type number. Received type `+typeof t},TypeError),C("ERR_OUT_OF_RANGE",function(e,t,n){let r=`The value of "${e}" is out of range.`,o=n;return Number.isInteger(n)&&Math.abs(n)>2**32?o=B(String(n)):"bigint"==typeof n&&(o=String(n),(n>BigInt(2)**BigInt(32)||n<-(BigInt(2)**BigInt(32)))&&(o=B(o)),o+="n"),r+=` It must be ${t}. Received `+o},RangeError);const q=/[^+/0-9A-Za-z-_]/g;function N(t,n){let r;n=n||1/0;var o=t.length;let s=null;const i=[];for(let e=0;e>6|192,63&r|128)}else if(r<65536){if((n-=3)<0)break;i.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((n-=4)<0)break;i.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return i}function $(e){return g.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace(q,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function I(e,t,n,r){let o;for(o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}function T(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function R(e){return e!=e}const z=function(){const n="0123456789abcdef",r=new Array(256);for(let t=0;t<16;++t){var o=16*t;for(let e=0;e<16;++e)r[o+e]=n[t]+n[e]}return r}();function M(e){return"undefined"==typeof BigInt?U:e}function U(){throw new Error("BigInt not supported")}},38075:(e,t,n)=>{"use strict";var r=n(70453),o=n(10487),s=o(r("String.prototype.indexOf"));e.exports=function(e,t){t=r(e,!!t);return"function"==typeof t&&-1{"use strict";var r=n(66743),o=n(70453),s=n(96897),i=o("%TypeError%"),a=o("%Function.prototype.apply%"),l=o("%Function.prototype.call%"),c=o("%Reflect.apply%",!0)||r.call(l,a),n=o("%Object.defineProperty%",!0),u=o("%Math.max%");if(n)try{n({},"a",{value:1})}catch(e){n=null}e.exports=function(e){if("function"!=typeof e)throw new i("a function is required");var t=c(r,l,arguments);return s(t,1+u(0,e.length-(arguments.length-1)),!0)};function p(){return c(r,a,arguments)}n?n(e.exports,"apply",{value:p}):e.exports.apply=p},17965:(e,t,n)=>{"use strict";var p=n(16426),h={"text/plain":"Text","text/html":"Url",default:"Text"};e.exports=function(n,r){var t,e,o,s,i,a,l,c=!1,u=(r=r||{}).debug||!1;try{if(e=p(),o=document.createRange(),s=document.getSelection(),(i=document.createElement("span")).textContent=n,i.ariaHidden="true",i.style.all="unset",i.style.position="fixed",i.style.top=0,i.style.clip="rect(0, 0, 0, 0)",i.style.whiteSpace="pre",i.style.webkitUserSelect="text",i.style.MozUserSelect="text",i.style.msUserSelect="text",i.style.userSelect="text",i.addEventListener("copy",function(e){var t;e.stopPropagation(),r.format&&(e.preventDefault(),void 0===e.clipboardData?(u&&console.warn("unable to use e.clipboardData"),u&&console.warn("trying IE specific stuff"),window.clipboardData.clearData(),t=h[r.format]||h.default,window.clipboardData.setData(t,n)):(e.clipboardData.clearData(),e.clipboardData.setData(r.format,n))),r.onCopy&&(e.preventDefault(),r.onCopy(e.clipboardData))}),document.body.appendChild(i),o.selectNodeContents(i),s.addRange(o),!document.execCommand("copy"))throw new Error("copy command was unsuccessful");c=!0}catch(e){u&&console.error("unable to copy using execCommand: ",e),u&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(r.format||"text",n),r.onCopy&&r.onCopy(window.clipboardData),c=!0}catch(e){u&&console.error("unable to copy using clipboardData: ",e),u&&console.error("falling back to prompt"),a="message"in r?r.message:"Copy to clipboard: #{key}, Enter",l=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C",t=a.replace(/#{\s*key\s*}/g,l),window.prompt(t,n)}}finally{s&&("function"==typeof s.removeRange?s.removeRange(o):s.removeAllRanges()),i&&document.body.removeChild(i),e()}return c}},2205:function(e,t,n){n=void 0!==n.g?n.g:this;e.exports=function(e){if(e.CSS&&e.CSS.escape)return e.CSS.escape;function t(e){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var t,n=String(e),r=n.length,o=-1,s="",i=n.charCodeAt(0);++o{"use strict";var r=n(48287).Buffer;function s(e){return e instanceof r||e instanceof Date||e instanceof RegExp}function i(e){var t;if(e instanceof r)return t=r.alloc?r.alloc(e.length):new r(e.length),e.copy(t),t;if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function a(e,t){return"__proto__"===t?void 0:e[t]}var l=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var n,r,o=arguments[0];return Array.prototype.slice.call(arguments,1).forEach(function(t){"object"!=typeof t||null===t||Array.isArray(t)||Object.keys(t).forEach(function(e){return r=a(o,e),(n=a(t,e))===o?void 0:"object"!=typeof n||null===n?void(o[e]=n):Array.isArray(n)?void(o[e]=function n(e){var r=[];return e.forEach(function(e,t){"object"==typeof e&&null!==e?Array.isArray(e)?r[t]=n(e):s(e)?r[t]=i(e):r[t]=l({},e):r[t]=e}),r}(n)):s(n)?void(o[e]=i(n)):"object"!=typeof r||null===r||Array.isArray(r)?void(o[e]=l({},n)):void(o[e]=l(r,n))})}),o}},14744:e=>{"use strict";function o(e){return!(!(t=e)||"object"!=typeof t||(t=e,"[object RegExp]"===(e=Object.prototype.toString.call(t))||"[object Date]"===e||t.$$typeof===n));var t}var n="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function a(e,t){return!1!==t.clone&&t.isMergeableObject(e)?c(Array.isArray(e)?[]:{},e,t):e}function s(e,t,n){return e.concat(t).map(function(e){return a(e,n)})}function t(e){return Object.keys(e).concat((t=e,Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(t).filter(function(e){return Object.propertyIsEnumerable.call(t,e)}):[]));var t}function l(e,t){try{return t in e}catch(e){return!1}}function i(r,o,s){var i={};return s.isMergeableObject(r)&&t(r).forEach(function(e){i[e]=a(r[e],s)}),t(o).forEach(function(e){var t,n;(!l(t=r,n=e)||Object.hasOwnProperty.call(t,n)&&Object.propertyIsEnumerable.call(t,n))&&(l(r,e)&&s.isMergeableObject(o[e])?i[e]=function(e,t){if(!t.customMerge)return c;t=t.customMerge(e);return"function"==typeof t?t:c}(e,s)(r[e],o[e],s):i[e]=a(o[e],s))}),i}function c(e,t,n){(n=n||{}).arrayMerge=n.arrayMerge||s,n.isMergeableObject=n.isMergeableObject||o,n.cloneUnlessOtherwiseSpecified=a;var r=Array.isArray(t);return r===Array.isArray(e)?r?n.arrayMerge(e,t,n):i(e,t,n):a(t,n)}c.all=function(e,n){if(Array.isArray(e))return e.reduce(function(e,t){return c(e,t,n)},{});throw new Error("first argument should be an array")},e.exports=c},30041:(e,t,n)=>{"use strict";var r=n(30592)(),o=n(70453),l=r&&o("%Object.defineProperty%",!0);if(l)try{l({},"a",{value:1})}catch(e){l=!1}var c=o("%SyntaxError%"),u=o("%TypeError%"),p=n(75795);e.exports=function(e,t,n){if(!e||"object"!=typeof e&&"function"!=typeof e)throw new u("`obj` must be an object or a function`");if("string"!=typeof t&&"symbol"!=typeof t)throw new u("`property` must be a string or a symbol`");if(3/gm),p=e(/\${[\w\W]*}/gm),h=e(/^data-[\-\w.\u00B7-\uFFFF]/),d=e(/^aria-[\-\w]+$/),ht=e(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),f=e(/^(?:\w+script|data):/i),m=e(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),dt=e(/^html$/i);var ft=Object.freeze({__proto__:null,MUSTACHE_EXPR:t,ERB_EXPR:n,TMPLIT_EXPR:p,DATA_ATTR:h,ARIA_ATTR:d,IS_ALLOWED_URI:ht,IS_SCRIPT_OR_DATA:f,ATTR_WHITESPACE:m,DOCTYPE_NAME:dt});return function D(e){e=0D(e);if(c.version="3.0.9",c.removed=[],!e||!e.document||9!==e.document.nodeType)return c.isSupported=!1,c;let s=e.document;const l=s,F=l.currentScript,{DocumentFragment:B,HTMLTemplateElement:L,Node:u,Element:q,NodeFilter:t,NamedNodeMap:$=e.NamedNodeMap||e.MozNamedAttrMap,HTMLFormElement:z,DOMParser:U,trustedTypes:p}=e,n=q.prototype,V=nt(n,"cloneNode"),K=nt(n,"nextSibling"),W=nt(n,"childNodes"),o=nt(n,"parentNode");if("function"==typeof L){const ze=s.createElement("template");ze.content&&ze.content.ownerDocument&&(s=ze.content.ownerDocument)}let h,d="";const{implementation:i,createNodeIterator:J,createDocumentFragment:H,getElementsByTagName:G}=s,Y=l.importNode;let r={};c.isSupported="function"==typeof ze&&"function"==typeof o&&i&&void 0!==i.createHTMLDocument;const{MUSTACHE_EXPR:f,ERB_EXPR:m,TMPLIT_EXPR:X,DATA_ATTR:Q,ARIA_ATTR:Z,IS_SCRIPT_OR_DATA:ee,ATTR_WHITESPACE:te}=ft;let ne=ft.IS_ALLOWED_URI,g=null;const re=et({},[...rt,...ot,...st,...it,...at]);let y=null;const oe=et({},[...lt,...ct,...ut,...pt]);let a=Object.seal(Ve(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),v=null,se=null,ie=!0,ae=!0,le=!1,ce=!0,b=!1,w=!1,ue=!1,pe=!1,E=!1,S=!1,x=!1,he=!0,de=!1;const fe="user-content-";let me=!0,_=!1,k={},A=null;const ge=et({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let ye=null;const ve=et({},["audio","video","img","source","image","track"]);let be=null;const we=et({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),O="http://www.w3.org/1998/Math/MathML",C="http://www.w3.org/2000/svg",j="http://www.w3.org/1999/xhtml";let P=j,Ee,Se=null;const xe=et({},[O,C,j],Ge);let N=null;const _e=["application/xhtml+xml","text/html"];let I=null,T=null;function ke(e){return e instanceof RegExp||e instanceof Function}function Ae(){let e=0e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+t+" could not be created."),null}}(p,F):h)&&"string"==typeof d&&(d=h.createHTML(""));Ue&&Ue(e),T=e}}function R(t){Je(c.removed,{element:t});try{t.parentNode.removeChild(t)}catch(e){t.remove()}}function Oe(e){let t=null,n=null;if(pe)e=""+e;else{const t=mt(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===N&&P===j&&(e=''+e+"");var r=h?h.createHTML(e):e;if(P===j)try{t=(new U).parseFromString(r,N)}catch(e){}if(!t||!t.documentElement){t=i.createDocument(P,"template",null);try{t.documentElement.innerHTML=Ee?d:r}catch(e){}}const o=t.body||t.documentElement;return e&&n&&o.insertBefore(s.createTextNode(n),o.childNodes[0]||null),P===j?G.call(t,w?"html":"body")[0]:w?t.documentElement:o}function Ce(e){return J.call(e.ownerDocument||e,e,t.SHOW_ELEMENT|t.SHOW_COMMENT|t.SHOW_TEXT,null)}function je(e){return"function"==typeof u&&e instanceof u}function Pe(t){let n=null;if(M("beforeSanitizeElements",t,null),(e=t)instanceof z&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof $)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes))return R(t),1;var e;const r=I(t.nodeName);if(M("uponSanitizeElement",t,{tagName:r,allowedTags:g}),t.hasChildNodes()&&!je(t.firstElementChild)&&Qe(/<[/\w]/g,t.innerHTML)&&Qe(/<[/\w]/g,t.textContent))return R(t),1;if(g[r]&&!v[r])return t instanceof q&&!function(e){let t=o(e);t&&t.tagName||(t={namespaceURI:P,tagName:"template"});var n=He(e.tagName),r=He(t.tagName);return Se[e.namespaceURI]&&(e.namespaceURI===C?t.namespaceURI===j?"svg"===n:t.namespaceURI===O?"svg"===n&&("annotation-xml"===r||Me[r]):Boolean(Be[n]):e.namespaceURI===O?t.namespaceURI===j?"math"===n:t.namespaceURI===C?"math"===n&&De[r]:Boolean(Le[n]):e.namespaceURI===j?(t.namespaceURI!==C||De[r])&&(t.namespaceURI!==O||Me[r])&&!Le[n]&&(Fe[n]||!Be[n]):"application/xhtml+xml"===N&&Se[e.namespaceURI])}(t)||("noscript"===r||"noembed"===r||"noframes"===r)&&Qe(/<\/no(script|embed|frames)/i,t.innerHTML)?(R(t),!0):(b&&3===t.nodeType&&(n=t.textContent,Ke([f,m,X],e=>{n=Ye(n,e," ")}),t.textContent!==n&&(Je(c.removed,{element:t.cloneNode()}),t.textContent=n)),M("afterSanitizeElements",t,null),!1);if(!v[r]&&Ne(r)){if(a.tagNameCheck instanceof RegExp&&Qe(a.tagNameCheck,r))return;if(a.tagNameCheck instanceof Function&&a.tagNameCheck(r))return}if(me&&!A[r]){const n=o(t)||t.parentNode,r=W(t)||t.childNodes;if(r&&n)for(let e=r.length-1;0<=e;--e)n.insertBefore(V(r[e],!0),K(t))}return R(t),1}function Ne(e){return"annotation-xml"!==e&&0/i,t))qe(o,n);else if(b&&Ke([f,m,X],e=>{t=Ye(t,e," ")}),i=I(n.nodeName),$e(i,a,t)){if(!de||"id"!==a&&"name"!==a||(qe(o,n),t=fe+t),h&&"object"==typeof p&&"function"==typeof p.getAttributeType&&!s)switch(p.getAttributeType(i,a)){case"TrustedHTML":t=h.createHTML(t);break;case"TrustedScriptURL":t=h.createScriptURL(t)}try{s?n.setAttributeNS(s,o,t):n.setAttribute(o,t),We(c.removed)}catch(n){}}}M("afterSanitizeAttributes",n,null)}}function Te(e){var t;const n=Ce(e);for(M("beforeSanitizeShadowDOM",e,null);t=n.nextNode();)M("uponSanitizeShadowNode",t,null),Pe(t)||(t.content instanceof B&&Te(t.content),Ie(t));M("afterSanitizeShadowDOM",e,null)}const Re=s.createElement("form"),Me=et({},["mi","mo","mn","ms","mtext"]),De=et({},["foreignobject","desc","title","annotation-xml"]),Fe=et({},["title","style","font","a","script"]),Be=et({},[...ot,...st,...vt]),Le=et({},[...it,...bt]),qe=function(e,t){try{Je(c.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){Je(c.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!y[e])if(E||S)try{R(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},M=function(e,t,n){r[e]&&Ke(r[e],e=>{e.call(c,t,n,T)})},$e=function(e,t,n){if(he&&("id"===t||"name"===t)&&(n in s||n in Re))return!1;if((!ae||se[t]||!Qe(Q,t))&&(!ie||!Qe(Z,t)))if(!y[t]||se[t]){if(!(Ne(e)&&(a.tagNameCheck instanceof RegExp&&Qe(a.tagNameCheck,e)||a.tagNameCheck instanceof Function&&a.tagNameCheck(e))&&(a.attributeNameCheck instanceof RegExp&&Qe(a.attributeNameCheck,t)||a.attributeNameCheck instanceof Function&&a.attributeNameCheck(t))||"is"===t&&a.allowCustomizedBuiltInElements&&(a.tagNameCheck instanceof RegExp&&Qe(a.tagNameCheck,n)||a.tagNameCheck instanceof Function&&a.tagNameCheck(n))))return!1}else if(!be[t]&&!Qe(ne,Ye(n,te,""))&&("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==gt(n,"data:")||!ye[e])&&(!le||Qe(ee,Ye(n,te,"")))&&n)return!1;return!0};return c.sanitize=function(e){let t=1\n"+a),b&&Ke([f,m,X],e=>{a=Ye(a,e," ")}),h&&x?h.createHTML(a):a},c.setConfig=function(){Ae(0{"use strict";class s{constructor(e,t){this.low=e,this.high=t,this.length=1+t-e}overlaps(e){return!(this.highe.high)}touches(e){return!(this.high+1e.high)}add(e){return new s(Math.min(this.low,e.low),Math.max(this.high,e.high))}subtract(e){return e.low<=this.low&&e.high>=this.high?[]:e.low>this.low&&e.highe+t.length,0)}add(e,t){var n=e=>{for(var t=0;t{for(var t=0;t{for(var t=0;t{for(var n=t.low;n<=t.high;)e.push(n),n++;return e},[])}subranges(){return this.ranges.map(e=>({low:e.low,high:e.high,length:1+e.high-e.low}))}}},37007:e=>{"use strict";var t="object"==typeof Reflect?Reflect:null,l=t&&"function"==typeof t.apply?t.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)},n=t&&"function"==typeof t.ownKeys?t.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)},r=Number.isNaN||function(e){return e!=e};function o(){o.init.call(this)}e.exports=o,e.exports.once=function(a,l){return new Promise(function(e,t){function n(e){a.removeListener(l,r),t(e)}function r(){"function"==typeof a.removeListener&&a.removeListener("error",n),e([].slice.call(arguments))}var o,s,i;f(a,l,r,{once:!0}),"error"!==l&&(s=n,i={once:!0},"function"==typeof(o=a).on&&f(o,"error",s,i))})},(o.EventEmitter=o).prototype._events=void 0,o.prototype._eventsCount=0,o.prototype._maxListeners=void 0;var s=10;function c(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function i(e){return void 0===e._maxListeners?o.defaultMaxListeners:e._maxListeners}function a(e,t,n,r){var o,s;return c(n),void 0===(o=e._events)?(o=e._events=Object.create(null),e._eventsCount=0):(void 0!==o.newListener&&(e.emit("newListener",t,n.listener||n),o=e._events),s=o[t]),void 0===s?(s=o[t]=n,++e._eventsCount):("function"==typeof s?s=o[t]=r?[n,s]:[s,n]:r?s.unshift(n):s.push(n),0<(o=i(e))&&s.length>o&&!s.warned&&(s.warned=!0,(r=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit")).name="MaxListenersExceededWarning",r.emitter=e,r.type=t,r.count=s.length,n=r,console&&console.warn&&console.warn(n))),e}function u(e,t,n){e={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},t=function(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}.bind(e);return t.listener=n,e.wrapFn=t}function p(e,t,n){e=e._events;if(void 0===e)return[];e=e[t];{if(void 0===e)return[];if("function"==typeof e)return n?[e.listener||e]:[e];if(n){for(var r=e,o=new Array(r.length),s=0;s{"use strict";var r=n(26311),n=o(Error);function o(t){return e.displayName=t.displayName||t.name,e;function e(e){return e=e&&r.apply(null,arguments),new t(e)}}(e.exports=n).eval=o(EvalError),n.range=o(RangeError),n.reference=o(ReferenceError),n.syntax=o(SyntaxError),n.type=o(TypeError),n.uri=o(URIError),n.create=o},26311:e=>{function n(t){function e(){return i[s++]}for(var n,r,o,s=1,i=[].slice.call(arguments),a=0,l=t.length,c="",u=!1,p=!1;a{"use strict";function l(e,t){for(var n=[],r=0;r{"use strict";n=n(89353);e.exports=Function.prototype.bind||n},70453:(e,t,n)=>{"use strict";var r,h=SyntaxError,o=Function,d=TypeError,s=function(e){try{return o('"use strict"; return ('+e+").constructor;")()}catch(e){}},f=Object.getOwnPropertyDescriptor;if(f)try{f({},"")}catch(e){f=null}function i(){throw new d}var a=f?function(){try{return i}catch(e){try{return f(arguments,"callee").get}catch(e){return i}}}():i,l=n(64039)(),c=n(80024)(),u=Object.getPrototypeOf||(c?function(e){return e.__proto__}:null),m={},c="undefined"!=typeof Uint8Array&&u?u(Uint8Array):r,g={"%AggregateError%":"undefined"==typeof AggregateError?r:AggregateError,"%Array%":Array,"%ArrayBuffer%":"undefined"==typeof ArrayBuffer?r:ArrayBuffer,"%ArrayIteratorPrototype%":l&&u?u([][Symbol.iterator]()):r,"%AsyncFromSyncIteratorPrototype%":r,"%AsyncFunction%":m,"%AsyncGenerator%":m,"%AsyncGeneratorFunction%":m,"%AsyncIteratorPrototype%":m,"%Atomics%":"undefined"==typeof Atomics?r:Atomics,"%BigInt%":"undefined"==typeof BigInt?r:BigInt,"%BigInt64Array%":"undefined"==typeof BigInt64Array?r:BigInt64Array,"%BigUint64Array%":"undefined"==typeof BigUint64Array?r:BigUint64Array,"%Boolean%":Boolean,"%DataView%":"undefined"==typeof DataView?r:DataView,"%Date%":Date,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":Error,"%eval%":eval,"%EvalError%":EvalError,"%Float32Array%":"undefined"==typeof Float32Array?r:Float32Array,"%Float64Array%":"undefined"==typeof Float64Array?r:Float64Array,"%FinalizationRegistry%":"undefined"==typeof FinalizationRegistry?r:FinalizationRegistry,"%Function%":o,"%GeneratorFunction%":m,"%Int8Array%":"undefined"==typeof Int8Array?r:Int8Array,"%Int16Array%":"undefined"==typeof Int16Array?r:Int16Array,"%Int32Array%":"undefined"==typeof Int32Array?r:Int32Array,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":l&&u?u(u([][Symbol.iterator]())):r,"%JSON%":"object"==typeof JSON?JSON:r,"%Map%":"undefined"==typeof Map?r:Map,"%MapIteratorPrototype%":"undefined"!=typeof Map&&l&&u?u((new Map)[Symbol.iterator]()):r,"%Math%":Math,"%Number%":Number,"%Object%":Object,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":"undefined"==typeof Promise?r:Promise,"%Proxy%":"undefined"==typeof Proxy?r:Proxy,"%RangeError%":RangeError,"%ReferenceError%":ReferenceError,"%Reflect%":"undefined"==typeof Reflect?r:Reflect,"%RegExp%":RegExp,"%Set%":"undefined"==typeof Set?r:Set,"%SetIteratorPrototype%":"undefined"!=typeof Set&&l&&u?u((new Set)[Symbol.iterator]()):r,"%SharedArrayBuffer%":"undefined"==typeof SharedArrayBuffer?r:SharedArrayBuffer,"%String%":String,"%StringIteratorPrototype%":l&&u?u(""[Symbol.iterator]()):r,"%Symbol%":l?Symbol:r,"%SyntaxError%":h,"%ThrowTypeError%":a,"%TypedArray%":c,"%TypeError%":d,"%Uint8Array%":"undefined"==typeof Uint8Array?r:Uint8Array,"%Uint8ClampedArray%":"undefined"==typeof Uint8ClampedArray?r:Uint8ClampedArray,"%Uint16Array%":"undefined"==typeof Uint16Array?r:Uint16Array,"%Uint32Array%":"undefined"==typeof Uint32Array?r:Uint32Array,"%URIError%":URIError,"%WeakMap%":"undefined"==typeof WeakMap?r:WeakMap,"%WeakRef%":"undefined"==typeof WeakRef?r:WeakRef,"%WeakSet%":"undefined"==typeof WeakSet?r:WeakSet};if(u)try{null.error}catch(e){l=u(u(e));g["%Error.prototype%"]=l}function y(e){var t,n;return"%AsyncFunction%"===e?t=s("async function () {}"):"%GeneratorFunction%"===e?t=s("function* () {}"):"%AsyncGeneratorFunction%"===e?t=s("async function* () {}"):"%AsyncGenerator%"===e?(n=y("%AsyncGeneratorFunction%"))&&(t=n.prototype):"%AsyncIteratorPrototype%"===e&&(n=y("%AsyncGenerator%"))&&u&&(t=u(n.prototype)),g[e]=t}var v={"%ArrayBufferPrototype%":["ArrayBuffer","prototype"],"%ArrayPrototype%":["Array","prototype"],"%ArrayProto_entries%":["Array","prototype","entries"],"%ArrayProto_forEach%":["Array","prototype","forEach"],"%ArrayProto_keys%":["Array","prototype","keys"],"%ArrayProto_values%":["Array","prototype","values"],"%AsyncFunctionPrototype%":["AsyncFunction","prototype"],"%AsyncGenerator%":["AsyncGeneratorFunction","prototype"],"%AsyncGeneratorPrototype%":["AsyncGeneratorFunction","prototype","prototype"],"%BooleanPrototype%":["Boolean","prototype"],"%DataViewPrototype%":["DataView","prototype"],"%DatePrototype%":["Date","prototype"],"%ErrorPrototype%":["Error","prototype"],"%EvalErrorPrototype%":["EvalError","prototype"],"%Float32ArrayPrototype%":["Float32Array","prototype"],"%Float64ArrayPrototype%":["Float64Array","prototype"],"%FunctionPrototype%":["Function","prototype"],"%Generator%":["GeneratorFunction","prototype"],"%GeneratorPrototype%":["GeneratorFunction","prototype","prototype"],"%Int8ArrayPrototype%":["Int8Array","prototype"],"%Int16ArrayPrototype%":["Int16Array","prototype"],"%Int32ArrayPrototype%":["Int32Array","prototype"],"%JSONParse%":["JSON","parse"],"%JSONStringify%":["JSON","stringify"],"%MapPrototype%":["Map","prototype"],"%NumberPrototype%":["Number","prototype"],"%ObjectPrototype%":["Object","prototype"],"%ObjProto_toString%":["Object","prototype","toString"],"%ObjProto_valueOf%":["Object","prototype","valueOf"],"%PromisePrototype%":["Promise","prototype"],"%PromiseProto_then%":["Promise","prototype","then"],"%Promise_all%":["Promise","all"],"%Promise_reject%":["Promise","reject"],"%Promise_resolve%":["Promise","resolve"],"%RangeErrorPrototype%":["RangeError","prototype"],"%ReferenceErrorPrototype%":["ReferenceError","prototype"],"%RegExpPrototype%":["RegExp","prototype"],"%SetPrototype%":["Set","prototype"],"%SharedArrayBufferPrototype%":["SharedArrayBuffer","prototype"],"%StringPrototype%":["String","prototype"],"%SymbolPrototype%":["Symbol","prototype"],"%SyntaxErrorPrototype%":["SyntaxError","prototype"],"%TypedArrayPrototype%":["TypedArray","prototype"],"%TypeErrorPrototype%":["TypeError","prototype"],"%Uint8ArrayPrototype%":["Uint8Array","prototype"],"%Uint8ClampedArrayPrototype%":["Uint8ClampedArray","prototype"],"%Uint16ArrayPrototype%":["Uint16Array","prototype"],"%Uint32ArrayPrototype%":["Uint32Array","prototype"],"%URIErrorPrototype%":["URIError","prototype"],"%WeakMapPrototype%":["WeakMap","prototype"],"%WeakSetPrototype%":["WeakSet","prototype"]},a=n(66743),b=n(9957),w=a.call(Function.call,Array.prototype.concat),E=a.call(Function.apply,Array.prototype.splice),S=a.call(Function.call,String.prototype.replace),x=a.call(Function.call,String.prototype.slice),_=a.call(Function.call,RegExp.prototype.exec),k=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,A=/\\(\\)?/g;e.exports=function(e,t){if("string"!=typeof e||0===e.length)throw new d("intrinsic name must be a non-empty string");if(1=n.length?(l=!!(p=f(s,c)))&&"get"in p&&!("originalValue"in p.get)?p.get:s[c]:(l=b(s,c),s[c]),l&&!i&&(g[u]=s)}}return s}},75795:(e,t,n)=>{"use strict";n=n(70453)("%Object.getOwnPropertyDescriptor%",!0);if(n)try{n([],"length")}catch(e){n=null}e.exports=n},30592:(e,t,n)=>{"use strict";function r(){if(o)try{return o({},"a",{value:1}),!0}catch(e){return!1}return!1}var o=n(70453)("%Object.defineProperty%",!0);r.hasArrayLengthDefineBug=function(){if(!r())return null;try{return 1!==o([],"length",{value:1}).length}catch(e){return!0}},e.exports=r},80024:e=>{"use strict";var t={foo:{}},n=Object;e.exports=function(){return{__proto__:t}.foo===t.foo&&!({__proto__:null}instanceof n)}},64039:(e,t,n)=>{"use strict";var r="undefined"!=typeof Symbol&&Symbol,o=n(41333);e.exports=function(){return"function"==typeof r&&"function"==typeof Symbol&&"symbol"==typeof r("foo")&&"symbol"==typeof Symbol("bar")&&o()}},41333:e=>{"use strict";e.exports=function(){if("function"!=typeof Symbol||"function"!=typeof Object.getOwnPropertySymbols)return!1;if("symbol"==typeof Symbol.iterator)return!0;var e={},t=Symbol("test"),n=Object(t);if("string"==typeof t)return!1;if("[object Symbol]"!==Object.prototype.toString.call(t))return!1;if("[object Symbol]"!==Object.prototype.toString.call(n))return!1;for(t in e[t]=42,e)return!1;if("function"==typeof Object.keys&&0!==Object.keys(e).length)return!1;if("function"==typeof Object.getOwnPropertyNames&&0!==Object.getOwnPropertyNames(e).length)return!1;n=Object.getOwnPropertySymbols(e);if(1!==n.length||n[0]!==t)return!1;if(!Object.prototype.propertyIsEnumerable.call(e,t))return!1;if("function"==typeof Object.getOwnPropertyDescriptor){n=Object.getOwnPropertyDescriptor(e,t);if(42!==n.value||!0!==n.enumerable)return!1}return!0}},9957:(e,t,n)=>{"use strict";var r=Function.prototype.call,o=Object.prototype.hasOwnProperty,n=n(66743);e.exports=n.call(r,o)},45981:e=>{function n(t){return t instanceof Map?t.clear=t.delete=t.set=function(){throw new Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=function(){throw new Error("set is read-only")}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach(function(e){e=t[e];"object"!=typeof e||Object.isFrozen(e)||n(e)}),t}var g=n;g.default=n;class P{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function u(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function l(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t];return t.forEach(function(e){for(const t in e)n[t]=e[t]}),n}const r=e=>!!e.kind;class t{constructor(e,t){this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){this.buffer+=u(e)}openNode(t){if(r(t)){let e=t.kind;t.sublanguage||(e=""+this.classPrefix+e),this.span(e)}}closeNode(e){r(e)&&(this.buffer+="")}value(){return this.buffer}span(e){this.buffer+=``}}class o{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){e={kind:e,children:[]};this.add(e),this.stack.push(e)}closeNode(){if(1this._walk(t,e)),t.closeNode(e)),t}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{o._collapse(e)}))}}class y extends o{constructor(e){super(),this.options=e}addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){return new t(this,this.options).value()}finalize(){return!0}}function c(e){return e?"string"==typeof e?e:e.source:null}function s(e,t,n={}){const r=l({className:"comment",begin:e,end:t,contains:[]},n);return r.contains.push(w),r.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),r}const p=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,i="[a-zA-Z]\\w*",a="[a-zA-Z_]\\w*",h="\\b\\d+(\\.\\d+)?",d="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",f="\\b(0b[01]+)",m={begin:"\\\\[\\s\\S]",relevance:0},v={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[m]},b={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[m]},w={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},E=s("//","$"),S=s("/\\*","\\*/"),x=s("#","$"),C={className:"number",begin:h,relevance:0},j={className:"number",begin:d,relevance:0},B={className:"number",begin:f,relevance:0},L={className:"number",begin:h+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},q={begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[m,{begin:/\[/,end:/\]/,relevance:0,contains:[m]}]}]},$={className:"title",begin:i,relevance:0},z={className:"title",begin:a,relevance:0},U={begin:"\\.\\s*"+a,relevance:0};var N=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:i,UNDERSCORE_IDENT_RE:a,NUMBER_RE:h,C_NUMBER_RE:d,BINARY_NUMBER_RE:f,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{var t=/^#![ ]*\//;return e.binary&&(e.begin=[t,/.*\b/,e.binary,/\b.*/].map(e=>c(e)).join("")),l({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:m,APOS_STRING_MODE:v,QUOTE_STRING_MODE:b,PHRASAL_WORDS_MODE:w,COMMENT:s,C_LINE_COMMENT_MODE:E,C_BLOCK_COMMENT_MODE:S,HASH_COMMENT_MODE:x,NUMBER_MODE:C,C_NUMBER_MODE:j,BINARY_NUMBER_MODE:B,CSS_NUMBER_MODE:L,REGEXP_MODE:q,TITLE_MODE:$,UNDERSCORE_TITLE_MODE:z,METHOD_GUARD:U,END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{t.data._beginMatch!==e[1]&&t.ignoreMatch()}})}});function V(e,t){"."===e.input[e.index-1]&&t.ignoreMatch()}const K=["of","and","for","in","not","or","if","then","parent","list","value"],W="keyword";function _(t,r,e=W){const o={};return"string"==typeof t?n(e,t.split(" ")):Array.isArray(t)?n(e,t):Object.keys(t).forEach(function(e){Object.assign(o,_(t[e],r,e))}),o;function n(n,e){(e=r?e.map(e=>e.toLowerCase()):e).forEach(function(e){var t,e=e.split("|");o[e[0]]=[n,(t=e[0],(e=e[1])?Number(e):function(e){return K.includes(e.toLowerCase())}(t)?0:1)]})}}function J(s,{}){function i(e,t){return new RegExp(c(e),"m"+(s.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,t){t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),this.matchAt+=new RegExp(e.toString()+"|").exec("").length-1+1}compile(){0===this.regexes.length&&(this.exec=()=>null);var e=this.regexes.map(e=>e[1]);this.matcherRe=i(function(e,t="|"){let o=0;return e.map(e=>{var t=o+=1;let n=c(e),r="";for(;0`(${e})`).join(t)}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e);if(!t)return null;var e=t.findIndex((e,t)=>0n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex;let n=t.exec(e);if(this.resumingScanAtSamePosition()&&(!n||n.index!==this.lastIndex)){const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}return n&&(this.regexIndex+=n.position+1,this.regexIndex===this.count&&this.considerAll()),n}}if(s.compilerExtensions||(s.compilerExtensions=[]),s.contains&&s.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return s.classNameAliases=l(s.classNameAliases||{}),function t(n,r){const o=n;if(n.isCompiled)return o;[function(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}].forEach(e=>e(n,r)),s.compilerExtensions.forEach(e=>e(n,r)),n.__beforeBegin=null,[function(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=V,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))},function(e,t){Array.isArray(e.illegal)&&(e.illegal=([...e]=[...e.illegal],"("+e.map(e=>c(e)).join("|")+")"))},function(e,t){void 0===e.relevance&&(e.relevance=1)}].forEach(e=>e(n,r)),n.isCompiled=!0;let e=null;if("object"==typeof n.keywords&&(e=n.keywords.$pattern,delete n.keywords.$pattern),n.keywords&&(n.keywords=_(n.keywords,s.case_insensitive)),n.lexemes&&e)throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return e=e||n.lexemes||/\w+/,o.keywordPatternRe=i(e,!0),r&&(n.begin||(n.begin=/\B|\b/),o.beginRe=i(n.begin),n.endSameAsBegin&&(n.end=n.begin),n.end||n.endsWithParent||(n.end=/\B|\b/),n.end&&(o.endRe=i(n.end)),o.terminatorEnd=c(n.end)||"",n.endsWithParent&&r.terminatorEnd&&(o.terminatorEnd+=(n.end?"|":"")+r.terminatorEnd)),n.illegal&&(o.illegalRe=i(n.illegal)),n.contains||(n.contains=[]),n.contains=[].concat(...n.contains.map(function(e){return(t="self"===e?n:e).variants&&!t.cachedVariants&&(t.cachedVariants=t.variants.map(function(e){return l(t,{variants:null},e)})),t.cachedVariants||(function e(t){return!!t&&(t.endsWithParent||e(t.starts))}(t)?l(t,{starts:t.starts?l(t.starts):null}):Object.isFrozen(t)?l(t):t);var t})),n.contains.forEach(function(e){t(e,o)}),n.starts&&t(n.starts,r),o.matcher=function(e){const t=new a;return e.contains.forEach(e=>t.addRule(e.begin,{rule:e,type:"begin"})),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t}(o),o}(s)}function H(t){const n={props:["language","code","autodetect"],data:function(){return{detectedLanguage:"",unknownLanguage:!1}},computed:{className(){return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){if(!this.autoDetect&&!t.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),this.unknownLanguage=!0,u(this.code);let e={};return this.autoDetect?(e=t.highlightAuto(this.code),this.detectedLanguage=e.language):(e=t.highlight(this.language,this.code,this.ignoreIllegals),this.detectedLanguage=this.language),e.value},autoDetect(){return!this.language||(e=this.autodetect,Boolean(e||""===e));var e},ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{Component:n,VuePlugin:{install(e){e.component("highlightjs",n)}}}}const G={"after:highlightElement":({el:e,result:t,text:n})=>{e=A(e);if(e.length){const r=document.createElement("div");r.innerHTML=t.value,t.value=function(t,e,n){let r=0,o="";const s=[];function i(){return t.length&&e.length?t[0].offset!==e[0].offset?t[0].offset"}function l(e){o+=""}function c(e){("start"===e.event?a:l)(e.node)}for(;t.length||e.length;){let e=i();if(o+=u(n.substring(r,e[0].offset)),r=e[0].offset,e===t){for(s.reverse().forEach(l);c(e.splice(0,1)[0]),(e=i())===t&&e.length&&e[0].offset===r;);s.reverse().forEach(a)}else"start"===e[0].event?s.push(e[0].node):s.pop(),c(e.splice(0,1)[0])}return o+u(n.substr(r))}(e,A(r),n)}}};function k(e){return e.nodeName.toLowerCase()}function A(e){const o=[];return function t(n,r){for(let e=n.firstChild;e;e=e.nextSibling)3===e.nodeType?r+=e.nodeValue.length:1===e.nodeType&&(o.push({event:"start",offset:r,node:e}),r=t(e,r),k(e).match(/br|hr|img|input/)||o.push({event:"stop",offset:r,node:e}));return r}(e,0),o}const O={},I=e=>{console.error(e)},T=(e,...t)=>{console.log("WARN: "+e,...t)},R=(e,t)=>{O[e+"/"+t]||(console.log(`Deprecated as of ${e}. `+t),O[e+"/"+t]=!0)},M=u,D=l,F=Symbol("nomatch");var Y=function(r){const S=Object.create(null),s=Object.create(null),x=[];let _=!0;const t=/(^(<[^>]+>|\t|)+|\n)/gm,k="Could not find the language '{}', did you forget to load/include a language module?",l={disableAutodetect:!0,name:"Plain text",contains:[]};let A={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:y};function i(e){return A.noHighlightRe.test(e)}function a(e,t,n,r){let o="",s="";"object"==typeof t?(o=e,n=t.ignoreIllegals,s=t.language,r=void 0):(R("10.7.0","highlight(lang, code, ...args) has been deprecated."),R("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),s=e,o=t);e={code:o,language:s};m("before:highlight",e);const i=e.result||O(e.language,e.code,n,r);return i.code=e.code,m("after:highlight",i),i}function O(r,o,s,e){function i(){(null!=d.subLanguage?function(){if(""!==g){let e=null;if("string"==typeof d.subLanguage){if(!S[d.subLanguage])return m.addText(g);e=O(d.subLanguage,g,!0,f[d.subLanguage]),f[d.subLanguage]=e.top}else e=C(g,d.subLanguage.length?d.subLanguage:null);0")+'"');throw r.mode=d,r}if("end"===t.type){const r=c(t);if(r!==F)return r}if("illegal"===t.type&&""===n)return 1;if(1e53*t.index)throw new Error("potential infinite loop, way more iterations than matches");return g+=n,n.length}const p=j(r);if(!p)throw I(k.replace("{}",r)),new Error('Unknown language: "'+r+'"');var n=J(p,{plugins:x});let h="",d=e||n;const f={},m=new A.__emitter(A);{const E=[];for(let e=d;e!==p;e=e.parent)e.className&&E.unshift(e.className);E.forEach(e=>m.openNode(e))}let g="",y=0,v=0,b=0,w=!1;try{for(d.matcher.considerAll();;){b++,w?w=!1:d.matcher.considerAll(),d.matcher.lastIndex=v;const r=d.matcher.exec(o);if(!r)break;const S=t(o.substring(v,r.index),r);v=r.index+S}return t(o.substr(v)),m.closeAllNodes(),m.finalize(),h=m.toHTML(),{relevance:Math.floor(y),value:h,language:r,illegal:!1,emitter:m,top:d}}catch(e){if(e.message&&e.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:e.message,context:o.slice(v-100,v+100),mode:e.mode},sofar:h,relevance:0,value:M(o),emitter:m};if(_)return{illegal:!1,relevance:0,value:M(o),emitter:m,language:r,top:d,errorRaised:e};throw e}}function C(t,e){e=e||A.languages||Object.keys(S);const n=function(e){const t={relevance:0,emitter:new A.__emitter(A),value:M(e),illegal:!1,top:l};return t.emitter.addText(e),t}(t),r=e.filter(j).filter(f).map(e=>O(e,t,!1)),o=(r.unshift(n),r.sort((e,t)=>{if(e.relevance!==t.relevance)return t.relevance-e.relevance;if(e.language&&t.language){if(j(e.language).supersetOf===t.language)return 1;if(j(t.language).supersetOf===e.language)return-1}return 0})),[s,i]=o,a=s;return a.second_best=i,a}const e={"before:highlightElement":({el:e})=>{A.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"))},"after:highlightElement":({result:e})=>{A.useBR&&(e.value=e.value.replace(/\n/g,"
"))}},n=/^(<[^>]+>|\t)+/gm,o={"after:highlightElement":({result:e})=>{A.tabReplace&&(e.value=e.value.replace(n,e=>e.replace(/\t/g,A.tabReplace)))}};function c(e){var t,n,r,o=function(e){let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"";var n=A.languageDetectRe.exec(t);if(n){const t=j(n[1]);return t||(T(k.replace("{}",n[1])),T("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}return t.split(/\s+/).find(e=>i(e)||j(e))}(e);i(o)||(m("before:highlightElement",{el:e,language:o}),n=e.textContent,m("after:highlightElement",{el:e,result:t=o?a(n,{language:o,ignoreIllegals:!0}):C(n),text:n}),e.innerHTML=t.value,n=e,o=o,r=t.language,o=o?s[o]:r,n.classList.add("hljs"),o&&n.classList.add(o),e.result={language:t.language,re:t.relevance,relavance:t.relevance},t.second_best&&(e.second_best={language:t.second_best.language,re:t.second_best.relevance,relavance:t.second_best.relevance}))}const u=()=>{u.called||(u.called=!0,R("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead."),document.querySelectorAll("pre code").forEach(c))};let p=!1;function h(){"loading"===document.readyState?p=!0:document.querySelectorAll("pre code").forEach(c)}function j(e){return e=(e||"").toLowerCase(),S[e]||S[s[e]]}function d(e,{languageName:t}){(e="string"==typeof e?[e]:e).forEach(e=>{s[e.toLowerCase()]=t})}function f(e){e=j(e);return e&&!e.disableAutodetect}function m(e,t){const n=e;x.forEach(function(e){e[n]&&e[n](t)})}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",function(){p&&h()},!1),Object.assign(r,{highlight:a,highlightAuto:C,highlightAll:h,fixMarkup:function(e){return R("10.2.0","fixMarkup will be removed entirely in v11.0"),R("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),e=e,A.tabReplace||A.useBR?e.replace(t,e=>"\n"===e?A.useBR?"
":e:A.tabReplace?e.replace(/\t/g,A.tabReplace):e):e},highlightElement:c,highlightBlock:function(e){return R("10.7.0","highlightBlock will be removed entirely in v12.0"),R("10.7.0","Please use highlightElement now."),c(e)},configure:function(e){e.useBR&&(R("10.3.0","'useBR' will be removed entirely in v11.0"),R("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),A=D(A,e)},initHighlighting:u,initHighlightingOnLoad:function(){R("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),p=!0},registerLanguage:function(t,e){let n=null;try{n=e(r)}catch(e){if(I("Language definition for '{}' could not be registered.".replace("{}",t)),!_)throw e;I(e),n=l}n.name||(n.name=t),(S[t]=n).rawDefinition=e.bind(null,r),n.aliases&&d(n.aliases,{languageName:t})},unregisterLanguage:function(e){delete S[e];for(const t of Object.keys(s))s[t]===e&&delete s[t]},listLanguages:function(){return Object.keys(S)},getLanguage:j,registerAliases:d,requireLanguage:function(e){R("10.4.0","requireLanguage will be removed entirely in v11."),R("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844");var t=j(e);if(t)return t;throw new Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:f,inherit:D,addPlugin:function(e){var t;(t=e)["before:highlightBlock"]&&!t["before:highlightElement"]&&(t["before:highlightElement"]=e=>{t["before:highlightBlock"](Object.assign({block:e.el},e))}),t["after:highlightBlock"]&&!t["after:highlightElement"]&&(t["after:highlightElement"]=e=>{t["after:highlightBlock"](Object.assign({block:e.el},e))}),x.push(e)},vuePlugin:H(r).VuePlugin}),r.debugMode=function(){_=!1},r.safeMode=function(){_=!0},r.versionString="10.7.3";for(const r in N)"object"==typeof N[r]&&g(N[r]);return Object.assign(r,N),r.addPlugin(e),r.addPlugin(G),r.addPlugin(o),r}({});e.exports=Y},35344:e=>{e.exports=function(e){var t={},n={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]};Object.assign(t,{className:"variable",variants:[{begin:[/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])"].map(e=>{return e?"string"==typeof e?e:e.source:null}).join("")},n]});const r={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},o={begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,t,r]};r.contains.push(s);var n={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t]},i=e.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),a={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp"},contains:[i,e.SHEBANG(),a,n,e.HASH_COMMENT_MODE,o,s,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},t]}}},73402:e=>{e.exports=function(e){var t="HTTP/(2|1\\.[01])",n={className:"attribute",begin:["^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"].map(e=>{return e?"string"==typeof e?e:e.source:null}).join(""),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},r=[n,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+t+" \\d{3})",end:/$/,contains:[{className:"meta",begin:t},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:r}},{begin:"(?=^[A-Z]+ (.*?) "+t+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:t},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:r}},e.inherit(n,{relevance:0})]}}},95089:e=>{const b="[A-Za-z$_][0-9A-Za-z$_]*",w=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],E=["true","false","null","undefined","NaN","Infinity"],S=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function x(e){return _("(?=",e,")")}function _(...e){return e.map(e=>{return e?"string"==typeof e?e:e.source:null}).join("")}e.exports=function(e){const t=b,n=/<[A-Za-z0-9\\._:-]+/,r=/\/[A-Za-z0-9\\._:-]+>|\/>/,o=(e,t)=>{var n=e[0].length+e.index,r=e.input[n];"<"!==r?">"===r&&([r,e]=[e,{after:n}["after"]],n="",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:y}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:n,"on:begin":o,end:r}],subLanguage:"xml",contains:[{begin:n,end:r,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:s,contains:["self",e.inherit(e.TITLE_MODE,{begin:t}),v],illegal:/%/},{beginKeywords:"while if switch catch for"},{className:"function",begin:e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,contains:[v,e.inherit(e.TITLE_MODE,{begin:t})]},{variants:[{begin:"\\."+t},{begin:"\\$"+t}],relevance:0},{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{beginKeywords:"extends"},e.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,end:/[{;]/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:t}),"self",v]},{begin:"(get|set)\\s+(?="+t+"\\()",end:/\{/,keywords:"get set",contains:[e.inherit(e.TITLE_MODE,{begin:t}),{begin:/\(\)/},v]},{begin:/\$[(.]/}]}}},65772:e=>{e.exports=function(e){const t={literal:"true false null"},n=[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],r=[e.QUOTE_STRING_MODE,e.C_NUMBER_MODE],o={end:",",endsWithParent:!0,excludeEnd:!0,contains:r,keywords:t},s={begin:/\{/,end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE],illegal:"\\n"},e.inherit(o,{begin:/:/})].concat(n),illegal:"\\S"},i={begin:"\\[",end:"\\]",contains:[e.inherit(o)],illegal:"\\S"};return r.push(s,i),n.forEach(function(e){r.push(e)}),{name:"JSON",contains:r,keywords:t,illegal:"\\S"}}},26571:e=>{e.exports=function(e){const t={$pattern:/-?[A-z\.\-]+\b/,keyword:"if else foreach return do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch hidden static parameter",built_in:"ac asnp cat cd CFS chdir clc clear clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo|0 epal epcsv epsn erase etsn exsn fc fhx fl ft fw gal gbp gc gcb gci gcm gcs gdr gerr ghy gi gin gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv gwmi h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start stz sujb sv swmi tee trcm type wget where wjb write"},n={begin:"`[\\s\\S]",relevance:0},r={className:"variable",variants:[{begin:/\$\B/},{className:"keyword",begin:/\$this/},{begin:/\$[\w\d][\w\d_:]*/}]},o={className:"string",variants:[{begin:/"/,end:/"/},{begin:/@"/,end:/^"@/}],contains:[n,r,{className:"variable",begin:/\$[A-z]/,end:/[^A-z]/}]},s={className:"string",variants:[{begin:/'/,end:/'/},{begin:/@'/,end:/^'@/}]},i=e.inherit(e.COMMENT(null,null),{variants:[{begin:/#/,end:/$/},{begin:/<#/,end:/#>/}],contains:[{className:"doctag",variants:[{begin:/\.(synopsis|description|example|inputs|outputs|notes|link|component|role|functionality)/},{begin:/\.(parameter|forwardhelptargetname|forwardhelpcategory|remotehelprunspace|externalhelp)\s+\S+/}]}]}),a={className:"built_in",variants:[{begin:"(".concat("Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where",")+(-)[\\w\\d]+")}]},l={className:"class",beginKeywords:"class enum",end:/\s*[{]/,excludeEnd:!0,relevance:0,contains:[e.TITLE_MODE]},c={className:"function",begin:/function\s+/,end:/\s*\{|$/,excludeEnd:!0,returnBegin:!0,relevance:0,contains:[{begin:"function",relevance:0,className:"keyword"},{className:"title",begin:/\w[\w\d]*((-)[\w\d]+)*/,relevance:0},{begin:/\(/,end:/\)/,className:"params",relevance:0,contains:[r]}]},u={begin:/using\s/,end:/$/,returnBegin:!0,contains:[o,s,{className:"keyword",begin:/(using|assembly|command|module|namespace|type)/}]},p={variants:[{className:"operator",begin:"(".concat("-and|-as|-band|-bnot|-bor|-bxor|-casesensitive|-ccontains|-ceq|-cge|-cgt|-cle|-clike|-clt|-cmatch|-cne|-cnotcontains|-cnotlike|-cnotmatch|-contains|-creplace|-csplit|-eq|-exact|-f|-file|-ge|-gt|-icontains|-ieq|-ige|-igt|-ile|-ilike|-ilt|-imatch|-in|-ine|-inotcontains|-inotlike|-inotmatch|-ireplace|-is|-isnot|-isplit|-join|-le|-like|-lt|-match|-ne|-not|-notcontains|-notin|-notlike|-notmatch|-or|-regex|-replace|-shl|-shr|-split|-wildcard|-xor",")\\b")},{className:"literal",begin:/(-)[\w\d]+/,relevance:0}]},h={className:"function",begin:/\[.*\]\s*[\w]+[ ]??\(/,end:/$/,returnBegin:!0,relevance:0,contains:[{className:"keyword",begin:"(".concat(t.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0,relevance:0},e.inherit(e.TITLE_MODE,{endsParent:!0})]},d=[h,i,n,e.NUMBER_MODE,o,s,a,r,{className:"literal",begin:/\$(null|true|false)\b/},{className:"selector-tag",begin:/@\B/,relevance:0}],f={begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",d,{begin:"("+["string","char","byte","int","long","bool","decimal","single","double","DateTime","xml","array","hashtable","void"].join("|")+")",className:"built_in",relevance:0},{className:"type",begin:/[\.\w\d]+/,relevance:0})};return h.contains.unshift(f),{name:"PowerShell",aliases:["ps","ps1"],case_insensitive:!0,keywords:t,contains:d.concat(l,c,u,p,f)}}},17285:e=>{function l(e){return e?"string"==typeof e?e:e.source:null}function c(e){return u("(?=",e,")")}function u(...e){return e.map(e=>l(e)).join("")}e.exports=function(e){var t=u(/[A-Z_]/,u("(",/[A-Z0-9_.-]*:/,")?"),/[A-Z0-9_.-]*/),n={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},r={begin:/\s/,contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},o=e.inherit(r,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{className:"meta-string"}),i=e.inherit(e.QUOTE_STRING_MODE,{className:"meta-string"}),a={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[r,i,s,o,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[r,o,i,s]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[a],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[a],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:u(//,/>/,/\s/].map(e=>l(e)).join("|")+")"))),end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:a}]},{className:"tag",begin:u(/<\//,c(u(t,/>/))),contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}},17533:e=>{e.exports=function(e){var t="true false yes no null",n="[\\w#;/?:@&=+$,.~*'()[\\]]+",r={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},o=e.inherit(r,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),s={end:",",endsWithParent:!0,excludeEnd:!0,keywords:t,relevance:0},n=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+n},{className:"type",begin:"!<"+n+">"},{className:"type",begin:"!"+n},{className:"type",begin:"!!"+n},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},{begin:/\{/,end:/\}/,contains:[s],illegal:"\\n",relevance:0},{begin:"\\[",end:"\\]",contains:[s],illegal:"\\n",relevance:0},r],t=[...n];return t.pop(),t.push(o),s.contains=t,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:n}}},251:(e,t)=>{t.read=function(e,t,n,r,o){var s,i,a=8*o-r-1,l=(1<>1,u=-7,p=n?o-1:0,h=n?-1:1,o=e[t+p];for(p+=h,s=o&(1<<-u)-1,o>>=-u,u+=a;0>=-u,u+=r;0>1,p=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,h=r?0:s-1,d=r?1:-1,s=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,i=c):(i=Math.floor(Math.log(t)/Math.LN2),t*(r=Math.pow(2,-i))<1&&(i--,r*=2),2<=(t+=1<=i+u?p/r:p*Math.pow(2,1-u))*r&&(i++,r/=2),c<=i+u?(a=0,i=c):1<=i+u?(a=(t*r-1)*Math.pow(2,o),i+=u):(a=t*Math.pow(2,u-1)*Math.pow(2,o),i=0));8<=o;e[n+h]=255&a,h+=d,a/=256,o-=8);for(i=i<>>0;if(""+n!==t||4294967295==n)return NaN;t=n}return t<0?H(e)+t:t}function Y(){return!0}function X(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&n<=t)}function Q(e,t){return ee(e,t,0)}function Z(e,t){return ee(e,t,t)}function ee(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var te=0,h=1,ne=2,re="function"==typeof Symbol&&Symbol.iterator,oe="@@iterator",se=re||oe;function d(e){this.next=e}function f(e,t,n,r){e=0===e?t:1===e?n:[t,n];return r?r.value=e:r={value:e,done:!1},r}function m(){return{value:void 0,done:!0}}function ie(e){return ce(e)}function ae(e){return e&&"function"==typeof e.next}function le(e){var t=ce(e);return t&&t.call(e)}function ce(e){e=e&&(re&&e[re]||e[oe]);if("function"==typeof e)return e}function ue(e){return e&&"number"==typeof e.length}function n(e){{if(null==e)return Se();if(u(e))return e.toSeq();var t=ke(e)||"object"==typeof e&&new ve(e);if(t)return t;throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e)}}function pe(e){return null==e?Se().toKeyedSeq():u(e)?c(e)?e.toSeq():e.fromEntrySeq():xe(e)}function g(e){return null==e?Se():u(e)?c(e)?e.entrySeq():e.toIndexedSeq():_e(e)}function he(e){return(null==e?Se():u(e)?c(e)?e.entrySeq():e:_e(e)).toSetSeq()}d.prototype.toString=function(){return"[Iterator]"},d.KEYS=te,d.VALUES=h,d.ENTRIES=ne,d.prototype.inspect=d.prototype.toSource=function(){return this.toString()},d.prototype[se]=function(){return this},e(n,l),n.of=function(){return n(arguments)},n.prototype.toSeq=function(){return this},n.prototype.toString=function(){return this.__toString("Seq {","}")},n.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},n.prototype.__iterate=function(e,t){return Ae(this,e,t,!0)},n.prototype.__iterator=function(e,t){return Oe(this,e,t,!0)},e(pe,n),pe.prototype.toKeyedSeq=function(){return this},e(g,n),g.of=function(){return g(arguments)},g.prototype.toIndexedSeq=function(){return this},g.prototype.toString=function(){return this.__toString("Seq [","]")},g.prototype.__iterate=function(e,t){return Ae(this,e,t,!1)},g.prototype.__iterator=function(e,t){return Oe(this,e,t,!1)},e(he,n),he.of=function(){return he(arguments)},he.prototype.toSetSeq=function(){return this},n.isSeq=Ee,n.Keyed=pe,n.Set=he,n.Indexed=g;var de,fe,me,ge="@@__IMMUTABLE_SEQ__@@";function ye(e){this._array=e,this.size=e.length}function ve(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function be(e){this._iterable=e,this.size=e.length||e.size}function we(e){this._iterator=e,this._iteratorCache=[]}function Ee(e){return!(!e||!e[ge])}function Se(){return de=de||new ye([])}function xe(e){var t=Array.isArray(e)?new ye(e).fromEntrySeq():ae(e)?new we(e).fromEntrySeq():ie(e)?new be(e).fromEntrySeq():"object"==typeof e?new ve(e):void 0;if(t)return t;throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e)}function _e(e){var t=ke(e);if(t)return t;throw new TypeError("Expected Array or iterable object of values: "+e)}function ke(e){return ue(e)?new ye(e):ae(e)?new we(e):ie(e)?new be(e):void 0}function Ae(e,t,n,r){var o=e._cache;if(o){for(var s=o.length-1,i=0;i<=s;i++){var a=o[n?s-i:i];if(!1===t(a[1],r?a[0]:i,e))return i+1}return i}return e.__iterateUncached(t,n)}function Oe(e,t,n,r){var o,s,i=e._cache;return i?(o=i.length-1,s=0,new d(function(){var e=i[n?o-s:s];return s++>o?m():f(t,r?e[0]:s-1,e[1])})):e.__iteratorUncached(t,n)}function Ce(e,t){return t?function n(r,o,e,t){return Array.isArray(o)?r.call(t,e,g(o).map(function(e,t){return n(r,e,t,o)})):Pe(o)?r.call(t,e,pe(o).map(function(e,t){return n(r,e,t,o)})):o}(t,e,"",{"":e}):je(e)}function je(e){return Array.isArray(e)?g(e).map(je).toList():Pe(e)?pe(e).map(je).toMap():e}function Pe(e){return e&&(e.constructor===Object||void 0===e.constructor)}function v(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function Ne(n,e){if(n===e)return!0;if(!u(e)||void 0!==n.size&&void 0!==e.size&&n.size!==e.size||void 0!==n.__hash&&void 0!==e.__hash&&n.__hash!==e.__hash||c(n)!==c(e)||p(n)!==p(e)||B(n)!==B(e))return!1;if(0===n.size&&0===e.size)return!0;var r,o=!F(n);if(B(n))return r=n.entries(),e.every(function(e,t){var n=r.next().value;return n&&v(n[1],e)&&(o||v(n[0],t))})&&r.next().done;var s=!1,i=(void 0===n.size&&(void 0===e.size?"function"==typeof n.cacheResult&&n.cacheResult():(s=!0,t=n,n=e,e=t)),!0),t=e.__iterate(function(e,t){if(o?!n.has(e):s?!v(e,n.get(t,x)):!v(n.get(t,x),e))return i=!1});return i&&n.size===t}function s(e,t){if(!(this instanceof s))return new s(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(fe)return fe;fe=this}}function Ie(e,t){if(!e)throw new Error(t)}function i(e,t,n){if(!(this instanceof i))return new i(e,t,n);if(Ie(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),ts?m():f(t,e,r[e])})},ve.prototype[z]=!0,e(be,g),be.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n,r=le(this._iterable),o=0;if(ae(r))for(;!(n=r.next()).done&&!1!==e(n.value,o++,this););return o},be.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var n=le(this._iterable);if(!ae(n))return new d(m);var r=0;return new d(function(){var e=n.next();return e.done?e:f(t,r++,e.value)})},e(we,g),we.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n=this._iterator,r=this._iteratorCache,o=0;o=r.length){var e=n.next();if(e.done)return e;r[o]=e.value}return f(t,o,r[o++])})},e(s,g),s.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},s.prototype.get=function(e,t){return this.has(e)?this._value:t},s.prototype.includes=function(e){return v(this._value,e)},s.prototype.slice=function(e,t){var n=this.size;return X(e,t,n)?this:new s(this._value,Z(t,n)-Q(e,n))},s.prototype.reverse=function(){return this},s.prototype.indexOf=function(e){return v(this._value,e)?0:-1},s.prototype.lastIndexOf=function(e){return v(this._value,e)?this.size:-1},s.prototype.__iterate=function(e,t){for(var n=0;n>>16)*r+n*(t>>>16)<<16>>>0)|0};function Be(e){return e>>>1&1073741824|3221225471&e}function _(e){if(!1===e||null==e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null==e))return 0;if(!0===e)return 1;var t,n,r=typeof e;if("number"==r){if(e!=e||e===1/0)return 0;var o=0|e;for(o!==e&&(o^=4294967295*e);4294967295We?(void 0===(t=Ge[s=e])&&(t=Le(s),He===Je&&(He=0,Ge={}),He++,Ge[s]=t),t):Le(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"==r){var s=e;if(Ue&&void 0!==(n=ze.get(s)))return n;if(void 0!==(n=s[Ke]))return n;if(!$e){if(void 0!==(n=s.propertyIsEnumerable&&s.propertyIsEnumerable[Ke]))return n;if(void 0!==(n=function(e){if(e&&0=n.length)throw new Error("Missing value for key: "+n[t]);e.set(n[t],n[t+1])}})},k.prototype.toString=function(){return this.__toString("Map {","}")},k.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},k.prototype.set=function(e,t){return ct(this,e,t)},k.prototype.setIn=function(e,t){return this.updateIn(e,x,function(){return t})},k.prototype.remove=function(e){return ct(this,e,x)},k.prototype.deleteIn=function(e){return this.updateIn(e,function(){return x})},k.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},k.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);e=function e(t,n,r,o){var s=t===x,i=n.next();if(i.done)return(l=o(a=s?r:t))===a?t:l;Ie(s||t&&t.set,"invalid keyPath");var a=i.value,l=s?x:t.get(a,x),i=e(l,n,r,o);return i===l?t:i===x?t.remove(a):(s?lt():t).set(a,i)}(this,cn(e),t,n);return e===x?void 0:e},k.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):lt()},k.prototype.merge=function(){return dt(this,void 0,arguments)},k.prototype.mergeWith=function(e){return dt(this,e,r.call(arguments,1))},k.prototype.mergeIn=function(e){var t=r.call(arguments,1);return this.updateIn(e,lt(),function(e){return"function"==typeof e.merge?e.merge.apply(e,t):t[t.length-1]})},k.prototype.mergeDeep=function(){return dt(this,ft,arguments)},k.prototype.mergeDeepWith=function(e){return dt(this,mt(e),r.call(arguments,1))},k.prototype.mergeDeepIn=function(e){var t=r.call(arguments,1);return this.updateIn(e,lt(),function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,t):t[t.length-1]})},k.prototype.sort=function(e){return j(Qt(this,e))},k.prototype.sortBy=function(e,t){return j(Qt(this,t,e))},k.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},k.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new W)},k.prototype.asImmutable=function(){return this.__ensureOwner()},k.prototype.wasAltered=function(){return this.__altered},k.prototype.__iterator=function(e,t){return new ot(this,e,t)},k.prototype.__iterate=function(t,e){var n=this,r=0;return this._root&&this._root.iterate(function(e){return r++,t(e[1],e[0],n)},e),r},k.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?at(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},k.isMap=Ye;var Xe,Qe="@@__IMMUTABLE_MAP__@@",A=k.prototype;function Ze(e,t){this.ownerID=e,this.entries=t}function et(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function tt(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function nt(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function rt(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function ot(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&it(e._root)}function st(e,t){return f(e,t[0],t[1])}function it(e,t){return{node:e,index:0,__prev:t}}function at(e,t,n,r){var o=Object.create(A);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function lt(){return Xe=Xe||at(0)}function ct(e,t,n){if(e._root){var r=K(U),o=K(V),s=ut(e._root,e.__ownerID,0,void 0,t,n,r,o);if(!o.value)return e;o=e.size+(r.value?n===x?-1:1:0)}else{if(n===x)return e;o=1,s=new Ze(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=s,e.__hash=void 0,e.__altered=!0,e):s?at(o,s):lt()}function ut(e,t,n,r,o,s,i,a){return e?e.update(t,n,r,o,s,i,a):s===x?e:(y(a),y(i),new rt(t,r,[o,s]))}function pt(e){return e.constructor===rt||e.constructor===nt}function ht(e,t,n,r,o){if(e.keyHash===r)return new nt(t,r,[e.entry,o]);var s=(0===n?e.keyHash:e.keyHash>>>n)&S,i=(0===n?r:r>>>n)&S;return new et(t,1<>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,127&(e+=e>>8)+(e>>16)}function vt(e,t,n,r){r=r?e:J(e);return r[t]=n,r}A[Qe]=!0,A[t]=A.remove,A.removeIn=A.deleteIn,Ze.prototype.get=function(e,t,n,r){for(var o=this.entries,s=0,i=o.length;s=bt){for(var h=e,d=l,i=r,s=o,f=new rt(h=h||new W,_(i),[i,s]),m=0;m>>e)&S),s=this.bitmap;return 0==(s&o)?r:this.nodes[yt(s&o-1)].get(e+w,t,n,r)},et.prototype.update=function(e,t,n,r,o,s,i){void 0===n&&(n=_(r));var a=(0===t?n:n>>>t)&S,l=1<=wt){for(var n=e,f=h,m=c,r=a,o=t,g=0,y=new Array(E),v=0;0!==m;v++,m>>>=1)y[v]=1&m?f[g++]:void 0;return y[r]=o,new tt(n,g+1,y)}if(u&&!t&&2===h.length&&pt(h[1^p]))return h[1^p];if(u&&t&&1===h.length&&pt(t))return t;s=e&&e===this.ownerID,i=u?t?c:c^l:c|l,d=u?t?vt(h,p,t,s):function(e,t,n){var r=e.length-1;if(n&&t===r)return e.pop(),e;for(var o=new Array(r),s=0,i=0;i>>e)&S];return o?o.get(e+w,t,n,r):r},tt.prototype.update=function(e,t,n,r,o,s,i){void 0===n&&(n=_(r));var a=(0===t?n:n>>>t)&S,l=o===x,c=this.nodes,u=c[a];if(l&&!u)return this;l=ut(u,e,t+w,n,r,o,s,i);if(l===u)return this;t=this.count;if(u){if(!l&&--t=n.size||r<0)return n.withMutations(function(e){r<0?Rt(e,r).set(0,o):Rt(e,0,r+1).set(r,o)});r+=n._origin;var e=n._tail,t=n._root,s=K(V);return r>=Dt(n._capacity)?e=Nt(e,n.__ownerID,0,r,o,s):t=Nt(t,n.__ownerID,n._level,r,o,s),s.value?n.__ownerID?(n._root=t,n._tail=e,n.__hash=void 0,n.__altered=!0,n):jt(n._origin,n._capacity,n._level,t,e):n},O.prototype.remove=function(e){return this.has(e)?0===e?this.shift():e===this.size-1?this.pop():this.splice(e,1):this},O.prototype.insert=function(e,t){return this.splice(e,0,t)},O.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=w,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):Pt()},O.prototype.push=function(){var n=arguments,r=this.size;return this.withMutations(function(e){Rt(e,0,r+n.length);for(var t=0;t>>t&S;if(r>=this.array.length)return new _t([],e);var o,s=0==r;if(0>>t&S;if(o>=this.array.length)return this;if(0>r,E<(l=1+(g-o>>r))&&(l=E),function(){for(;;){if(s){var e=s();if(e!==Ot)return e;s=null}if(a===l)return Ot;e=f?--l:a++;s=b(i&&i[e],r-w,o+(e<>>n&S,c=e&&l=Dt(e._capacity))return e._tail;if(t<1<>>r&S],r-=w;return n}}function Rt(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new W,o=e._origin,s=e._capacity,i=o+t,t=void 0===n?s:n<0?s+n:o+n;if(i===o&&t===s)return e;if(t<=i)return e.clear();for(var a=e._level,l=e._root,c=0;i+c<0;)l=new _t(l&&l.array.length?[void 0,l]:[],r),c+=1<<(a+=w);c&&(i+=c,o+=c,t+=c,s+=c);for(var u=Dt(s),p=Dt(t);1<>>f&S,d=d.array[m]=It(d.array[m],r);d.array[u>>>w&S]=n}if(t>>a&S;if(g!=p>>>a&S)break;g&&(c+=(1<o&&(o=a.size),u(i)||(a=a.map(function(e){return Ce(e)})),r.push(a)}return gt(e=o>e.size?e.setSize(o):e,t,r)}function Dt(e){return e>>w<=E&&i.size>=2*s.size?(r=(o=i.filter(function(e,t){return void 0!==e&&a!==t})).toKeyedSeq().map(function(e){return e[0]}).flip().toMap(),e.__ownerID&&(r.__ownerID=o.__ownerID=e.__ownerID)):(r=s.remove(t),o=a===i.size-1?i.pop():i.set(a,void 0))}else if(l){if(n===i.get(a)[1])return e;r=s,o=i.set(a,[t,n])}else r=s.set(t,i.size),o=i.set(i.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=o,e.__hash=void 0,e):Bt(r,o)}function $t(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function zt(e){this._iter=e,this.size=e.size}function Ut(e){this._iter=e,this.size=e.size}function Vt(e){this._iter=e,this.size=e.size}function Kt(o){var e=sn(o);return e._iter=o,e.size=o.size,e.flip=function(){return o},e.reverse=function(){var e=o.reverse.apply(this);return e.flip=function(){return o.reverse()},e},e.has=function(e){return o.includes(e)},e.includes=function(e){return o.has(e)},e.cacheResult=an,e.__iterateUncached=function(n,e){var r=this;return o.__iterate(function(e,t){return!1!==n(t,e,r)},e)},e.__iteratorUncached=function(e,t){var n;return e===ne?(n=o.__iterator(e,t),new d(function(){var e,t=n.next();return t.done||(e=t.value[0],t.value[0]=t.value[1],t.value[1]=e),t})):o.__iterator(e===h?te:h,t)},e}function Wt(s,i,a){var e=sn(s);return e.size=s.size,e.has=function(e){return s.has(e)},e.get=function(e,t){var n=s.get(e,x);return n===x?t:i.call(a,n,e,s)},e.__iterateUncached=function(r,e){var o=this;return s.__iterate(function(e,t,n){return!1!==r(i.call(a,e,t,n),t,o)},e)},e.__iteratorUncached=function(r,e){var o=s.__iterator(ne,e);return new d(function(){var e=o.next();if(e.done)return e;var t=e.value,n=t[0];return f(r,n,i.call(a,t[1],n,s),e)})},e}function Jt(o,n){var e=sn(o);return e._iter=o,e.size=o.size,e.reverse=function(){return o},o.flip&&(e.flip=function(){var e=Kt(o);return e.reverse=function(){return o.flip()},e}),e.get=function(e,t){return o.get(n?e:-1-e,t)},e.has=function(e){return o.has(n?e:-1-e)},e.includes=function(e){return o.includes(e)},e.cacheResult=an,e.__iterate=function(n,e){var r=this;return o.__iterate(function(e,t){return n(e,t,r)},!e)},e.__iterator=function(e,t){return o.__iterator(e,!t)},e}function Ht(i,a,l,c){var e=sn(i);return c&&(e.has=function(e){var t=i.get(e,x);return t!==x&&!!a.call(l,t,e,i)},e.get=function(e,t){var n=i.get(e,x);return n!==x&&a.call(l,n,e,i)?n:t}),e.__iterateUncached=function(r,e){var o=this,s=0;return i.__iterate(function(e,t,n){if(a.call(l,e,t,n))return s++,r(e,c?t:s-1,o)},e),s},e.__iteratorUncached=function(r,e){var o=i.__iterator(ne,e),s=0;return new d(function(){for(;;){var e=o.next();if(e.done)return e;var t=e.value,n=t[0],t=t[1];if(a.call(l,t,n,i))return f(r,c?n:s++,t,e)}})},e}function Gt(a,e,t,l){var n=a.size;if(void 0!==e&&(e|=0),void 0!==t&&(t===1/0?t=n:t|=0),X(e,t,n))return a;var c=Q(e,n),n=Z(t,n);if(c!=c||n!=n)return Gt(a.toSeq().cacheResult(),e,t,l);var u,e=n-c,t=(e==e&&(u=e<0?0:e),sn(a));return t.size=0===u?u:a.size&&u||void 0,!l&&Ee(a)&&0<=u&&(t.get=function(e,t){return 0<=(e=G(this,e))&&eu)return m();var e=n.next();return l||t===h?e:f(t,o-1,t===te?void 0:e.value[1],e)})},t}function Yt(t,c,u,p){var e=sn(t);return e.__iterateUncached=function(r,e){var o=this;if(e)return this.cacheResult().__iterate(r,e);var s=!0,i=0;return t.__iterate(function(e,t,n){if(!s||!(s=c.call(u,e,t,n)))return i++,r(e,p?t:i-1,o)}),i},e.__iteratorUncached=function(o,e){var s=this;if(e)return this.cacheResult().__iterator(o,e);var i=t.__iterator(ne,e),a=!0,l=0;return new d(function(){var e;do{if((e=i.next()).done)return p||o===h?e:f(o,l++,o===te?void 0:e.value[1],e);var t=e.value,n=t[0],r=t[1]}while(a=a&&c.call(u,r,n,s));return o===ne?e:f(o,n,r,e)})},e}function Xt(e,l,c){var t=sn(e);return t.__iterateUncached=function(s,t){var i=0,a=!1;return function n(e,r){var o=this;e.__iterate(function(e,t){return(!l||r>>-15,461845907),t=Fe(t<<13|t>>>-13,5),t=Fe((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=Be((t=Fe(t^t>>>13,3266489909))^t>>>16)}(e.__iterate(n?t?function(e,t){r=31*r+Ln(_(e),_(t))|0}:function(e,t){r=r+Ln(_(e),_(t))|0}:t?function(e){r=31*r+_(e)|0}:function(e){r=r+_(e)|0}),r)}(this))}});var t=l.prototype,Nn=(t[L]=!0,t[se]=t.values,t.__toJS=t.toArray,t.__toStringMapper=Dn,t.inspect=t.toSource=function(){return this.toString()},t.chain=t.flatMap,t.contains=t.includes,Pn(a,{flip:function(){return P(this,Kt(this))},mapEntries:function(n,r){var o=this,s=0;return P(this,this.toSeq().map(function(e,t){return n.call(r,[t,e],s++,o)}).fromEntrySeq())},mapKeys:function(n,r){var o=this;return P(this,this.toSeq().flip().map(function(e,t){return n.call(r,e,t,o)}).flip())}}),a.prototype);function In(e,t){return t}function Tn(e,t){return[t,e]}function Rn(e){return function(){return!e.apply(this,arguments)}}function Mn(e){return function(){return-e.apply(this,arguments)}}function Dn(e){return"string"==typeof e?JSON.stringify(e):String(e)}function Fn(){return J(arguments)}function Bn(e,t){return e>2)|0}return Nn[q]=!0,Nn[se]=t.entries,Nn.__toJS=t.toObject,Nn.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+Dn(e)},Pn(D,{toKeyedSeq:function(){return new $t(this,!1)},filter:function(e,t){return P(this,Ht(this,e,t,!1))},findIndex:function(e,t){e=this.findEntry(e,t);return e?e[0]:-1},indexOf:function(e){e=this.keyOf(e);return void 0===e?-1:e},lastIndexOf:function(e){e=this.lastKeyOf(e);return void 0===e?-1:e},reverse:function(){return P(this,Jt(this,!1))},slice:function(e,t){return P(this,Gt(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=Q(e,e<0?this.count():this.size);var r=this.slice(0,e);return P(this,1===n?r:r.concat(J(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){e=this.findLastEntry(e,t);return e?e[0]:-1},first:function(){return this.get(0)},flatten:function(e){return P(this,Xt(this,e,!1))},get:function(n,e){return(n=G(this,n))<0||this.size===1/0||void 0!==this.size&&n>this.size?e:this.find(function(e,t){return t===n},void 0,e)},has:function(e){return 0<=(e=G(this,e))&&(void 0!==this.size?this.size===1/0||e{"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){var n;t&&(e.super_=t,(n=function(){}).prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e)}},5419:e=>{e.exports=function(e,t,n,r){var o,s,r=new Blob(void 0!==r?[r,e]:[e],{type:n||"application/octet-stream"});void 0!==window.navigator.msSaveBlob?window.navigator.msSaveBlob(r,t):(o=(window.URL&&window.URL.createObjectURL?window.URL:window.webkitURL).createObjectURL(r),(s=document.createElement("a")).style.display="none",s.href=o,s.setAttribute("download",t),void 0===s.download&&s.setAttribute("target","_blank"),document.body.appendChild(s),s.click(),setTimeout(function(){document.body.removeChild(s),window.URL.revokeObjectURL(o)},200))}},20181:(e,t,n)=>{function v(){return c.Date.now()}var r=/^\s+|\s+$/g,o=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,i=/^0o[0-7]+$/i,a=parseInt,n="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,l="object"==typeof self&&self&&self.Object===Object&&self,c=n||l||Function("return this")(),u=Object.prototype.toString,b=Math.max,w=Math.min;function E(e){var t=typeof e;return e&&("object"==t||"function"==t)}function S(e){if("number"==typeof e)return e;if("symbol"==typeof(n=e)||(t=n)&&"object"==typeof t&&"[object Symbol]"==u.call(n))return NaN;var t;if("string"!=typeof(e=E(e)?E(t="function"==typeof e.valueOf?e.valueOf():e)?t+"":t:e))return 0===e?e:+e;e=e.replace(r,"");var n=s.test(e);return n||i.test(e)?a(e.slice(2),n?2:8):o.test(e)?NaN:+e}e.exports=function(r,n,e){var o,s,i,a,l,c,u=0,p=!1,h=!1,t=!0;if("function"!=typeof r)throw new TypeError("Expected a function");function d(e){var t=o,n=s;return o=s=void 0,u=e,a=r.apply(n,t)}function f(e){var t=e-c;return void 0===c||n<=t||t<0||h&&i<=e-u}function m(){var e,t=v();if(f(t))return g(t);l=setTimeout(m,(e=n-((t=t)-c),h?w(e,i-(t-u)):e))}function g(e){return l=void 0,t&&o?d(e):(o=s=void 0,a)}function y(){var e=v(),t=f(e);if(o=arguments,s=this,c=e,t){if(void 0===l)return u=e=c,l=setTimeout(m,n),p?d(e):a;if(h)return l=setTimeout(m,n),d(c)}return void 0===l&&(l=setTimeout(m,n)),a}return n=S(n)||0,E(e)&&(p=!!e.leading,i=(h="maxWait"in e)?b(S(e.maxWait)||0,n):i,t="trailing"in e?!!e.trailing:t),y.cancel=function(){void 0!==l&&clearTimeout(l),o=c=s=l=void(u=0)},y.flush=function(){return void 0===l?a:g(v())},y}},55580:(e,t,n)=>{n=n(56110)(n(9325),"DataView");e.exports=n},21549:(e,t,n)=>{var r=n(22032),o=n(63862),s=n(66721),i=n(12749),n=n(35749);function a(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(39344),n=n(94033);function o(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}o.prototype=r(n.prototype),e.exports=o.prototype.constructor=o},80079:(e,t,n)=>{var r=n(63702),o=n(70080),s=n(24739),i=n(48655),n=n(31175);function a(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(39344),n=n(94033);function o(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=void 0}o.prototype=r(n.prototype),e.exports=o.prototype.constructor=o},68223:(e,t,n)=>{n=n(56110)(n(9325),"Map");e.exports=n},53661:(e,t,n)=>{var r=n(63040),o=n(17670),s=n(90289),i=n(4509),n=n(72949);function a(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{n=n(56110)(n(9325),"Promise");e.exports=n},76545:(e,t,n)=>{n=n(56110)(n(9325),"Set");e.exports=n},38859:(e,t,n)=>{var r=n(53661),o=n(31380),n=n(51459);function s(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new r;++t{var r=n(80079),o=n(51420),s=n(90938),i=n(63605),a=n(29817),n=n(80945);function l(e){e=this.__data__=new r(e);this.size=e.size}l.prototype.clear=o,l.prototype.delete=s,l.prototype.get=i,l.prototype.has=a,l.prototype.set=n,e.exports=l},51873:(e,t,n)=>{n=n(9325).Symbol;e.exports=n},37828:(e,t,n)=>{n=n(9325).Uint8Array;e.exports=n},28303:(e,t,n)=>{n=n(56110)(n(9325),"WeakMap");e.exports=n},91033:e=>{e.exports=function(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}},83729:e=>{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=0,s=[];++n{var r=n(96131);e.exports=function(e,t){return!(null==e||!e.length)&&-1{var u=n(78096),p=n(72428),h=n(56449),d=n(3656),f=n(30361),m=n(37167),g=Object.prototype.hasOwnProperty;e.exports=function(e,t){var n,r=h(e),o=!r&&p(e),s=!r&&!o&&d(e),i=!r&&!o&&!s&&m(e),a=r||o||s||i,l=a?u(e.length,String):[],c=l.length;for(n in e)!t&&!g.call(e,n)||a&&("length"==n||s&&("offset"==n||"parent"==n)||i&&("buffer"==n||"byteLength"==n||"byteOffset"==n)||f(n,c))||l.push(n);return l}},34932:e=>{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=Array(r);++n{e.exports=function(e,t){for(var n=-1,r=t.length,o=e.length;++n{e.exports=function(e,t,n,r){var o=-1,s=null==e?0:e.length;for(r&&s&&(n=e[++o]);++o{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n{e.exports=function(e){return e.split("")}},1733:e=>{var t=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;e.exports=function(e){return e.match(t)||[]}},87805:(e,t,n)=>{var r=n(43360),o=n(75288);e.exports=function(e,t,n){(void 0===n||o(e[t],n))&&(void 0!==n||t in e)||r(e,t,n)}},16547:(e,t,n)=>{var o=n(43360),s=n(75288),i=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){var r=e[t];i.call(e,t)&&s(r,n)&&(void 0!==n||t in e)||o(e,t,n)}},26025:(e,t,n)=>{var r=n(75288);e.exports=function(e,t){for(var n=e.length;n--;)if(r(e[n][0],t))return n;return-1}},74733:(e,t,n)=>{var r=n(21791),o=n(95950);e.exports=function(e,t){return e&&r(t,o(t),e)}},43838:(e,t,n)=>{var r=n(21791),o=n(37241);e.exports=function(e,t){return e&&r(t,o(t),e)}},43360:(e,t,n)=>{var r=n(93243);e.exports=function(e,t,n){"__proto__"==t&&r?r(e,t,{configurable:!0,enumerable:!0,value:n,writable:!0}):e[t]=n}},9999:(e,t,n)=>{var f=n(37217),m=n(83729),g=n(16547),y=n(74733),v=n(43838),b=n(93290),w=n(23007),E=n(92271),S=n(48948),x=n(50002),_=n(83349),k=n(5861),A=n(76189),O=n(77199),C=n(35529),j=n(56449),P=n(3656),N=n(87730),I=n(23805),T=n(38440),R=n(95950),M=n(37241),D="[object Arguments]",F="[object Function]",B="[object Object]",L={};L[D]=L["[object Array]"]=L["[object ArrayBuffer]"]=L["[object DataView]"]=L["[object Boolean]"]=L["[object Date]"]=L["[object Float32Array]"]=L["[object Float64Array]"]=L["[object Int8Array]"]=L["[object Int16Array]"]=L["[object Int32Array]"]=L["[object Map]"]=L["[object Number]"]=L[B]=L["[object RegExp]"]=L["[object Set]"]=L["[object String]"]=L["[object Symbol]"]=L["[object Uint8Array]"]=L["[object Uint8ClampedArray]"]=L["[object Uint16Array]"]=L["[object Uint32Array]"]=!0,L["[object Error]"]=L[F]=L["[object WeakMap]"]=!1,e.exports=function n(r,o,s,e,t,i){var a,l=1&o,c=2&o,u=4&o;if(void 0!==(a=s?t?s(r,e,t,i):s(r):a))return a;if(!I(r))return r;e=j(r);if(e){if(a=A(r),!l)return w(r,a)}else{var p=k(r),h=p==F||"[object GeneratorFunction]"==p;if(P(r))return b(r,l);if(p==B||p==D||h&&!t){if(a=c||h?{}:C(r),!l)return c?S(r,v(a,r)):E(r,y(a,r))}else{if(!L[p])return t?r:{};a=O(r,p,l)}}h=(i=i||new f).get(r);if(h)return h;i.set(r,a),T(r)?r.forEach(function(e){a.add(n(e,o,s,e,r,i))}):N(r)&&r.forEach(function(e,t){a.set(t,n(e,o,s,t,r,i))});var d=e?void 0:(u?c?_:x:c?M:R)(r);return m(d||r,function(e,t){d&&(e=r[t=e]),g(a,t,n(e,o,s,t,r,i))}),a}},39344:(e,t,n)=>{var r=n(23805),o=Object.create;function s(){}e.exports=function(e){if(!r(e))return{};if(o)return o(e);s.prototype=e;e=new s;return s.prototype=void 0,e}},80909:(e,t,n)=>{var r=n(30641),n=n(38329)(r);e.exports=n},2523:e=>{e.exports=function(e,t,n,r){for(var o=e.length,s=n+(r?1:-1);r?s--:++s{var c=n(14528),u=n(45891);e.exports=function e(t,n,r,o,s){var i=-1,a=t.length;for(r=r||u,s=s||[];++i{n=n(83221)();e.exports=n},30641:(e,t,n)=>{var r=n(86649),o=n(95950);e.exports=function(e,t){return e&&r(e,t,o)}},47422:(e,t,n)=>{var o=n(31769),s=n(77797);e.exports=function(e,t){for(var n=0,r=(t=o(t,e)).length;null!=e&&n{var r=n(14528),o=n(56449);e.exports=function(e,t,n){t=t(e);return o(e)?t:r(t,n(e))}},72552:(e,t,n)=>{var r=n(51873),o=n(659),s=n(59350),i=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":(i&&i in Object(e)?o:s)(e)}},28077:e=>{e.exports=function(e,t){return null!=e&&t in Object(e)}},96131:(e,t,n)=>{var r=n(2523),o=n(85463),s=n(76959);e.exports=function(e,t,n){return t==t?s(e,t,n):r(e,o,n)}},27534:(e,t,n)=>{var r=n(72552),o=n(40346);e.exports=function(e){return o(e)&&"[object Arguments]"==r(e)}},60270:(e,t,n)=>{var i=n(87068),a=n(40346);e.exports=function e(t,n,r,o,s){return t===n||(null==t||null==n||!a(t)&&!a(n)?t!=t&&n!=n:i(t,n,r,o,e,s))}},87068:(e,t,n)=>{var p=n(37217),h=n(25911),d=n(21986),f=n(50689),m=n(5861),g=n(56449),y=n(3656),v=n(37167),b="[object Arguments]",w="[object Array]",E="[object Object]",S=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,r,o,s){var i=g(e),a=g(t),l=i?w:m(e),a=a?w:m(t),c=(l=l==b?E:l)==E,u=(a=a==b?E:a)==E,a=l==a;if(a&&y(e)){if(!y(t))return!1;c=!(i=!0)}if(a&&!c)return s=s||new p,i||v(e)?h(e,t,n,r,o,s):d(e,t,l,n,r,o,s);if(!(1&n)){i=c&&S.call(e,"__wrapped__"),l=u&&S.call(t,"__wrapped__");if(i||l)return o(i?e.value():e,l?t.value():t,n,r,s=s||new p)}return!!a&&(s=s||new p,f(e,t,n,r,o,s))}},29172:(e,t,n)=>{var r=n(5861),o=n(40346);e.exports=function(e){return o(e)&&"[object Map]"==r(e)}},41799:(e,t,n)=>{var d=n(37217),f=n(60270);e.exports=function(e,t,n,r){var o=n.length,s=o,i=!r;if(null==e)return!s;for(e=Object(e);o--;){var a=n[o];if(i&&a[2]?a[1]!==e[a[0]]:!(a[0]in e))return!1}for(;++o{e.exports=function(e){return e!=e}},45083:(e,t,n)=>{var r=n(1882),o=n(87296),s=n(23805),i=n(47473),a=/^\[object .+?Constructor\]$/,n=Function.prototype,l=Object.prototype,n=n.toString,l=l.hasOwnProperty,c=RegExp("^"+n.call(l).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");e.exports=function(e){return!(!s(e)||o(e))&&(r(e)?c:a).test(i(e))}},16038:(e,t,n)=>{var r=n(5861),o=n(40346);e.exports=function(e){return o(e)&&"[object Set]"==r(e)}},4901:(e,t,n)=>{var r=n(72552),o=n(30294),s=n(40346),i={};i["[object Float32Array]"]=i["[object Float64Array]"]=i["[object Int8Array]"]=i["[object Int16Array]"]=i["[object Int32Array]"]=i["[object Uint8Array]"]=i["[object Uint8ClampedArray]"]=i["[object Uint16Array]"]=i["[object Uint32Array]"]=!0,i["[object Arguments]"]=i["[object Array]"]=i["[object ArrayBuffer]"]=i["[object Boolean]"]=i["[object DataView]"]=i["[object Date]"]=i["[object Error]"]=i["[object Function]"]=i["[object Map]"]=i["[object Number]"]=i["[object Object]"]=i["[object RegExp]"]=i["[object Set]"]=i["[object String]"]=i["[object WeakMap]"]=!1,e.exports=function(e){return s(e)&&o(e.length)&&!!i[r(e)]}},15389:(e,t,n)=>{var r=n(93663),o=n(87978),s=n(83488),i=n(56449),a=n(50583);e.exports=function(e){return"function"==typeof e?e:null==e?s:"object"==typeof e?i(e)?o(e[0],e[1]):r(e):a(e)}},88984:(e,t,n)=>{var r=n(55527),o=n(3650),s=Object.prototype.hasOwnProperty;e.exports=function(e){if(!r(e))return o(e);var t,n=[];for(t in Object(e))s.call(e,t)&&"constructor"!=t&&n.push(t);return n}},72903:(e,t,n)=>{var o=n(23805),s=n(55527),i=n(90181),a=Object.prototype.hasOwnProperty;e.exports=function(e){if(!o(e))return i(e);var t,n=s(e),r=[];for(t in e)("constructor"!=t||!n&&a.call(e,t))&&r.push(t);return r}},94033:e=>{e.exports=function(){}},93663:(e,t,n)=>{var r=n(41799),o=n(10776),s=n(67197);e.exports=function(t){var n=o(t);return 1==n.length&&n[0][2]?s(n[0][0],n[0][1]):function(e){return e===t||r(e,t,n)}}},87978:(e,t,n)=>{var o=n(60270),s=n(58156),i=n(80631),a=n(28586),l=n(30756),c=n(67197),u=n(77797);e.exports=function(n,r){return a(n)&&l(r)?c(u(n),r):function(e){var t=s(e,n);return void 0===t&&t===r?i(e,n):o(r,t,3)}}},85250:(e,t,n)=>{var c=n(37217),u=n(87805),p=n(86649),h=n(42824),d=n(23805),f=n(37241),m=n(14974);e.exports=function r(o,s,i,a,l){o!==s&&p(s,function(e,t){var n;l=l||new c,d(e)?h(o,s,t,i,r,a,l):(n=a?a(m(o,t),e,t+"",o,s,l):void 0,u(o,t,n=void 0===n?e:n))},f)}},42824:(e,t,n)=>{var d=n(87805),f=n(93290),m=n(71961),g=n(23007),y=n(35529),v=n(72428),b=n(56449),w=n(83693),E=n(3656),S=n(1882),x=n(23805),_=n(11331),k=n(37167),A=n(14974),O=n(69884);e.exports=function(e,t,n,r,o,s,i){var a,l,c,u=A(e,n),p=A(t,n),h=i.get(p);h?d(e,n,h):((t=void 0===(h=s?s(u,p,n+"",e,t,i):void 0))&&(l=!(a=b(p))&&E(p),c=!a&&!l&&k(p),h=p,a||l||c?h=b(u)?u:w(u)?g(u):l?f(p,!(t=!1)):c?m(p,!(t=!1)):[]:_(p)||v(p)?v(h=u)?h=O(u):x(u)&&!S(u)||(h=y(p)):t=!1),t&&(i.set(p,h),o(h,p,r,s,i),i.delete(p)),d(e,n,h))}},47237:e=>{e.exports=function(t){return function(e){return null==e?void 0:e[t]}}},17255:(e,t,n)=>{var r=n(47422);e.exports=function(t){return function(e){return r(e,t)}}},54552:e=>{e.exports=function(t){return function(e){return null==t?void 0:t[e]}}},85558:e=>{e.exports=function(e,r,o,s,t){return t(e,function(e,t,n){o=s?(s=!1,e):r(o,e,t,n)}),o}},69302:(e,t,n)=>{var r=n(83488),o=n(56757),s=n(32865);e.exports=function(e,t){return s(o(e,t,r),e+"")}},73170:(e,t,n)=>{var p=n(16547),h=n(31769),d=n(30361),f=n(23805),m=n(77797);e.exports=function(e,t,n,r){if(!f(e))return e;for(var o=-1,s=(t=h(t,e)).length,i=s-1,a=e;null!=a&&++o{var r=n(83488),o=n(48152);e.exports=o?function(e,t){return o.set(e,t),e}:r},19570:(e,t,n)=>{var r=n(37334),o=n(93243),n=n(83488);e.exports=o?function(e,t){return o(e,"toString",{configurable:!0,enumerable:!1,value:r(t),writable:!0})}:n},25160:e=>{e.exports=function(e,t,n){var r=-1,o=e.length;(n=o>>0,t>>>=0;for(var s=Array(o);++r{var s=n(80909);e.exports=function(e,r){var o;return s(e,function(e,t,n){return!(o=r(e,t,n))}),!!o}},78096:e=>{e.exports=function(e,t){for(var n=-1,r=Array(e);++n{var r=n(51873),o=n(34932),s=n(56449),i=n(44394),n=r?r.prototype:void 0,a=n?n.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(s(t))return o(t,e)+"";if(i(t))return a?a.call(t):"";var n=t+"";return"0"==n&&1/t==-1/0?"-0":n}},54128:(e,t,n)=>{var r=n(31800),o=/^\s+/;e.exports=function(e){return e&&e.slice(0,r(e)+1).replace(o,"")}},27301:e=>{e.exports=function(t){return function(e){return t(e)}}},19931:(e,t,n)=>{var r=n(31769),o=n(68090),s=n(68969),i=n(77797);e.exports=function(e,t){return t=r(t,e),null==(e=s(e,t))||delete e[i(o(t))]}},51234:e=>{e.exports=function(e,t,n){for(var r=-1,o=e.length,s=t.length,i={};++r{e.exports=function(e,t){return e.has(t)}},31769:(e,t,n)=>{var r=n(56449),o=n(28586),s=n(61802),i=n(13222);e.exports=function(e,t){return r(e)?e:o(e,t)?[e]:s(i(e))}},28754:(e,t,n)=>{var o=n(25160);e.exports=function(e,t,n){var r=e.length;return n=void 0===n?r:n,!t&&r<=n?e:o(e,t,n)}},49653:(e,t,n)=>{var r=n(37828);e.exports=function(e){var t=new e.constructor(e.byteLength);return new r(t).set(new r(e)),t}},93290:(e,t,n)=>{e=n.nmd(e);var n=n(9325),t=t&&!t.nodeType&&t,r=t&&e&&!e.nodeType&&e,r=r&&r.exports===t?n.Buffer:void 0,o=r?r.allocUnsafe:void 0;e.exports=function(e,t){if(t)return e.slice();t=e.length,t=o?o(t):new e.constructor(t);return e.copy(t),t}},76169:(e,t,n)=>{var r=n(49653);e.exports=function(e,t){t=t?r(e.buffer):e.buffer;return new e.constructor(t,e.byteOffset,e.byteLength)}},73201:e=>{var n=/\w*$/;e.exports=function(e){var t=new e.constructor(e.source,n.exec(e));return t.lastIndex=e.lastIndex,t}},93736:(e,t,n)=>{var n=n(51873),n=n?n.prototype:void 0,r=n?n.valueOf:void 0;e.exports=function(e){return r?Object(r.call(e)):{}}},71961:(e,t,n)=>{var r=n(49653);e.exports=function(e,t){t=t?r(e.buffer):e.buffer;return new e.constructor(t,e.byteOffset,e.length)}},91596:e=>{var h=Math.max;e.exports=function(e,t,n,r){for(var o=-1,s=e.length,i=n.length,a=-1,l=t.length,c=h(s-i,0),u=Array(l+c),p=!r;++a{var f=Math.max;e.exports=function(e,t,n,r){for(var o=-1,s=e.length,i=-1,a=n.length,l=-1,c=t.length,u=f(s-a,0),p=Array(u+c),h=!r;++o{e.exports=function(e,t){var n=-1,r=e.length;for(t=t||Array(r);++n{var c=n(16547),u=n(43360);e.exports=function(e,t,n,r){var o=!n;n=n||{};for(var s=-1,i=t.length;++s{var r=n(21791),o=n(4664);e.exports=function(e,t){return r(e,o(e),t)}},48948:(e,t,n)=>{var r=n(21791),o=n(86375);e.exports=function(e,t){return r(e,o(e),t)}},55481:(e,t,n)=>{n=n(9325)["__core-js_shared__"];e.exports=n},58523:e=>{e.exports=function(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}},20999:(e,t,n)=>{var r=n(69302),l=n(36800);e.exports=function(a){return r(function(e,t){var n=-1,r=t.length,o=1{var a=n(64894);e.exports=function(s,i){return function(e,t){if(null==e)return e;if(!a(e))return s(e,t);for(var n=e.length,r=i?n:-1,o=Object(e);(i?r--:++r{e.exports=function(l){return function(e,t,n){for(var r=-1,o=Object(e),s=n(e),i=s.length;i--;){var a=s[l?i:++r];if(!1===t(o[a],a,o))break}return e}}},11842:(e,t,n)=>{var s=n(82819),i=n(9325);e.exports=function(t,e,n){var r=1&e,o=s(t);return function e(){return(this&&this!==i&&this instanceof e?o:t).apply(r?n:this,arguments)}}},12507:(e,t,n)=>{var o=n(28754),s=n(49698),i=n(63912),a=n(13222);e.exports=function(r){return function(e){e=a(e);var t=s(e)?i(e):void 0,n=t?t[0]:e.charAt(0),t=t?o(t,1).join(""):e.slice(1);return n[r]()+t}}},45539:(e,t,n)=>{var r=n(40882),o=n(50828),s=n(66645),i=RegExp("['’]","g");e.exports=function(t){return function(e){return r(s(o(e).replace(i,"")),t,"")}}},82819:(e,t,n)=>{var o=n(39344),s=n(23805);e.exports=function(r){return function(){var e=arguments;switch(e.length){case 0:return new r;case 1:return new r(e[0]);case 2:return new r(e[0],e[1]);case 3:return new r(e[0],e[1],e[2]);case 4:return new r(e[0],e[1],e[2],e[3]);case 5:return new r(e[0],e[1],e[2],e[3],e[4]);case 6:return new r(e[0],e[1],e[2],e[3],e[4],e[5]);case 7:return new r(e[0],e[1],e[2],e[3],e[4],e[5],e[6])}var t=o(r.prototype),n=r.apply(t,e);return s(n)?n:t}}},77078:(e,t,n)=>{var c=n(91033),r=n(82819),u=n(37471),p=n(18073),h=n(11287),d=n(36306),f=n(9325);e.exports=function(s,i,a){var l=r(s);return function e(){for(var t=arguments.length,n=Array(t),r=t,o=h(e);r--;)n[r]=arguments[r];o=t<3&&n[0]!==o&&n[t-1]!==o?[]:d(n,o);return(t-=o.length){var i=n(15389),a=n(64894),l=n(95950);e.exports=function(s){return function(e,t,n){var r,o=Object(e),t=(a(e)||(r=i(t,3),e=l(e),t=function(e){return r(o[e],e,o)}),s(e,t,n));return-1{var x=n(91596),_=n(53320),k=n(58523),A=n(82819),O=n(18073),C=n(11287),j=n(68294),P=n(36306),N=n(9325);e.exports=function i(a,l,c,u,p,h,d,f,m,g){var y=128&l,v=1&l,b=2&l,w=24&l,E=512&l,S=b?void 0:A(a);return function e(){for(var t=arguments.length,n=Array(t),r=t;r--;)n[r]=arguments[r];if(w&&(o=C(e),s=k(n,o)),u&&(n=x(n,u,p,w)),h&&(n=_(n,h,d,w)),t-=s,w&&t{var h=n(91033),r=n(82819),d=n(9325);e.exports=function(a,e,l,c){var u=1&e,p=r(a);return function e(){for(var t=-1,n=arguments.length,r=-1,o=c.length,s=Array(o+n),i=this&&this!==d&&this instanceof e?p:a;++r{var p=n(85087),h=n(54641),d=n(70981);e.exports=function(e,t,n,r,o,s,i,a,l,c){var u=8&t,o=(4&(t=(t|(u?32:64))&~(u?64:32))||(t&=-4),[e,t,o,u?s:void 0,u?i:void 0,u?void 0:s,u?void 0:i,a,l,c]),s=n.apply(void 0,o);return p(e)&&h(s,o),s.placeholder=r,d(s,e,t)}},66977:(e,t,n)=>{var d=n(68882),f=n(11842),m=n(77078),g=n(37471),y=n(24168),v=n(37381),b=n(3209),w=n(54641),E=n(70981),S=n(61489),x=Math.max;e.exports=function(e,t,n,r,o,s,i,a){var l=2&t;if(!l&&"function"!=typeof e)throw new TypeError("Expected a function");var c,u=r?r.length:0,p=(u||(t&=-97,r=o=void 0),i=void 0===i?i:x(S(i),0),a=void 0===a?a:S(a),u-=o?o.length:0,64&t&&(h=r,c=o,r=o=void 0),l?void 0:v(e)),h=[e,t,n,r,o,h,c,s,i,a];return p&&b(h,p),e=h[0],t=h[1],n=h[2],r=h[3],o=h[4],!(a=h[9]=void 0===h[9]?l?0:e.length:x(h[9]-u,0))&&24&t&&(t&=-25),c=t&&1!=t?8==t||16==t?m(e,t,a):32!=t&&33!=t||o.length?g.apply(void 0,h):y(e,t,n,r):f(e,t,n),E((p?d:w)(c,h),e,t)}},53138:(e,t,n)=>{var r=n(11331);e.exports=function(e){return r(e)?void 0:e}},24647:(e,t,n)=>{n=n(54552)({"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss","Ā":"A","Ă":"A","Ą":"A","ā":"a","ă":"a","ą":"a","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","ć":"c","ĉ":"c","ċ":"c","č":"c","Ď":"D","Đ":"D","ď":"d","đ":"d","Ē":"E","Ĕ":"E","Ė":"E","Ę":"E","Ě":"E","ē":"e","ĕ":"e","ė":"e","ę":"e","ě":"e","Ĝ":"G","Ğ":"G","Ġ":"G","Ģ":"G","ĝ":"g","ğ":"g","ġ":"g","ģ":"g","Ĥ":"H","Ħ":"H","ĥ":"h","ħ":"h","Ĩ":"I","Ī":"I","Ĭ":"I","Į":"I","İ":"I","ĩ":"i","ī":"i","ĭ":"i","į":"i","ı":"i","Ĵ":"J","ĵ":"j","Ķ":"K","ķ":"k","ĸ":"k","Ĺ":"L","Ļ":"L","Ľ":"L","Ŀ":"L","Ł":"L","ĺ":"l","ļ":"l","ľ":"l","ŀ":"l","ł":"l","Ń":"N","Ņ":"N","Ň":"N","Ŋ":"N","ń":"n","ņ":"n","ň":"n","ŋ":"n","Ō":"O","Ŏ":"O","Ő":"O","ō":"o","ŏ":"o","ő":"o","Ŕ":"R","Ŗ":"R","Ř":"R","ŕ":"r","ŗ":"r","ř":"r","Ś":"S","Ŝ":"S","Ş":"S","Š":"S","ś":"s","ŝ":"s","ş":"s","š":"s","Ţ":"T","Ť":"T","Ŧ":"T","ţ":"t","ť":"t","ŧ":"t","Ũ":"U","Ū":"U","Ŭ":"U","Ů":"U","Ű":"U","Ų":"U","ũ":"u","ū":"u","ŭ":"u","ů":"u","ű":"u","ų":"u","Ŵ":"W","ŵ":"w","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Ź":"Z","Ż":"Z","Ž":"Z","ź":"z","ż":"z","ž":"z","IJ":"IJ","ij":"ij","Œ":"Oe","œ":"oe","ʼn":"'n","ſ":"s"});e.exports=n},93243:(e,t,n)=>{var r=n(56110),n=function(){try{var e=r(Object,"defineProperty");return e({},"",{}),e}catch(e){}}();e.exports=n},25911:(e,t,n)=>{var g=n(38859),y=n(14248),v=n(19219);e.exports=function(e,t,n,r,o,s){var i=1&n,a=e.length,l=t.length;if(a!=l&&!(i&&a{var r=n(51873),c=n(37828),u=n(75288),p=n(25911),h=n(20317),d=n(84247),n=r?r.prototype:void 0,f=n?n.valueOf:void 0;e.exports=function(e,t,n,r,o,s,i){switch(n){case"[object DataView]":if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case"[object ArrayBuffer]":return!(e.byteLength!=t.byteLength||!s(new c(e),new c(t)));case"[object Boolean]":case"[object Date]":case"[object Number]":return u(+e,+t);case"[object Error]":return e.name==t.name&&e.message==t.message;case"[object RegExp]":case"[object String]":return e==t+"";case"[object Map]":var a=h;case"[object Set]":a=a||d;if(e.size!=t.size&&!(1&r))return!1;var l=i.get(e);if(l)return l==t;r|=2,i.set(e,t);l=p(a(e),a(t),r,o,s,i);return i.delete(e),l;case"[object Symbol]":if(f)return f.call(e)==f.call(t)}return!1}},50689:(e,t,n)=>{var v=n(50002),b=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,r,o,s){var i=1&n,a=v(e),l=a.length;if(l!=v(t).length&&!i)return!1;for(var c=l;c--;){var u=a[c];if(!(i?u in t:b.call(t,u)))return!1}var p=s.get(e),h=s.get(t);if(p&&h)return p==t&&h==e;var d=!0;s.set(e,t),s.set(t,e);for(var f=i;++c{var r=n(35970),o=n(56757),s=n(32865);e.exports=function(e){return s(o(e,void 0,r),e+"")}},34840:(e,t,n)=>{n="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;e.exports=n},50002:(e,t,n)=>{var r=n(82199),o=n(4664),s=n(95950);e.exports=function(e){return r(e,s,o)}},83349:(e,t,n)=>{var r=n(82199),o=n(86375),s=n(37241);e.exports=function(e){return r(e,s,o)}},37381:(e,t,n)=>{var r=n(48152),n=n(63950);e.exports=r?function(e){return r.get(e)}:n},62284:(e,t,n)=>{var i=n(84629),a=Object.prototype.hasOwnProperty;e.exports=function(e){for(var t=e.name+"",n=i[t],r=a.call(i,t)?n.length:0;r--;){var o=n[r],s=o.func;if(null==s||s==e)return o.name}return t}},11287:e=>{e.exports=function(e){return e.placeholder}},12651:(e,t,n)=>{var r=n(74218);e.exports=function(e,t){e=e.__data__;return r(t)?e["string"==typeof t?"string":"hash"]:e.map}},10776:(e,t,n)=>{var s=n(30756),i=n(95950);e.exports=function(e){for(var t=i(e),n=t.length;n--;){var r=t[n],o=e[r];t[n]=[r,o,s(o)]}return t}},56110:(e,t,n)=>{var r=n(45083),o=n(10392);e.exports=function(e,t){e=o(e,t);return r(e)?e:void 0}},28879:(e,t,n)=>{n=n(74335)(Object.getPrototypeOf,Object);e.exports=n},659:(e,t,n)=>{var n=n(51873),r=Object.prototype,s=r.hasOwnProperty,i=r.toString,a=n?n.toStringTag:void 0;e.exports=function(e){var t=s.call(e,a),n=e[a];try{var r=!(e[a]=void 0)}catch(e){}var o=i.call(e);return r&&(t?e[a]=n:delete e[a]),o}},4664:(e,t,n)=>{var r=n(79770),n=n(63345),o=Object.prototype.propertyIsEnumerable,s=Object.getOwnPropertySymbols;e.exports=s?function(t){return null==t?[]:(t=Object(t),r(s(t),function(e){return o.call(t,e)}))}:n},86375:(e,t,n)=>{var r=n(14528),o=n(28879),s=n(4664),n=n(63345),n=Object.getOwnPropertySymbols?function(e){for(var t=[];e;)r(t,s(e)),e=o(e);return t}:n;e.exports=n},5861:(e,t,n)=>{var r=n(55580),o=n(68223),s=n(32804),i=n(76545),a=n(28303),l=n(72552),c=n(47473),u="[object Map]",p="[object Promise]",h="[object Set]",d="[object WeakMap]",f="[object DataView]",m=c(r),g=c(o),y=c(s),v=c(i),b=c(a),n=l;(r&&n(new r(new ArrayBuffer(1)))!=f||o&&n(new o)!=u||s&&n(s.resolve())!=p||i&&n(new i)!=h||a&&n(new a)!=d)&&(n=function(e){var t=l(e),e="[object Object]"==t?e.constructor:void 0,e=e?c(e):"";if(e)switch(e){case m:return f;case g:return u;case y:return p;case v:return h;case b:return d}return t}),e.exports=n},10392:e=>{e.exports=function(e,t){return null==e?void 0:e[t]}},75251:e=>{var t=/\{\n\/\* \[wrapped with (.+)\] \*/,n=/,? & /;e.exports=function(e){e=e.match(t);return e?e[1].split(n):[]}},49326:(e,t,n)=>{var a=n(31769),l=n(72428),c=n(56449),u=n(30361),p=n(30294),h=n(77797);e.exports=function(e,t,n){for(var r=-1,o=(t=a(t,e)).length,s=!1;++r{var t=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");e.exports=function(e){return t.test(e)}},45434:e=>{var t=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;e.exports=function(e){return t.test(e)}},22032:(e,t,n)=>{var r=n(81042);e.exports=function(){this.__data__=r?r(null):{},this.size=0}},63862:e=>{e.exports=function(e){e=this.has(e)&&delete this.__data__[e];return this.size-=e?1:0,e}},66721:(e,t,n)=>{var r=n(81042),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t,n=this.__data__;return r?"__lodash_hash_undefined__"===(t=n[e])?void 0:t:o.call(n,e)?n[e]:void 0}},12749:(e,t,n)=>{var r=n(81042),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;return r?void 0!==t[e]:o.call(t,e)}},35749:(e,t,n)=>{var r=n(81042);e.exports=function(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=r&&void 0===t?"__lodash_hash_undefined__":t,this}},76189:e=>{var r=Object.prototype.hasOwnProperty;e.exports=function(e){var t=e.length,n=new e.constructor(t);return t&&"string"==typeof e[0]&&r.call(e,"index")&&(n.index=e.index,n.input=e.input),n}},77199:(e,t,n)=>{var o=n(49653),s=n(76169),i=n(73201),a=n(93736),l=n(71961);e.exports=function(e,t,n){var r=e.constructor;switch(t){case"[object ArrayBuffer]":return o(e);case"[object Boolean]":case"[object Date]":return new r(+e);case"[object DataView]":return s(e,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return l(e,n);case"[object Map]":case"[object Set]":return new r;case"[object Number]":case"[object String]":return new r(e);case"[object RegExp]":return i(e);case"[object Symbol]":return a(e)}}},35529:(e,t,n)=>{var r=n(39344),o=n(28879),s=n(55527);e.exports=function(e){return"function"!=typeof e.constructor||s(e)?{}:r(o(e))}},62060:e=>{var o=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/;e.exports=function(e,t){var n=t.length;if(!n)return e;var r=n-1;return t[r]=(1{var r=n(51873),o=n(72428),s=n(56449),i=r?r.isConcatSpreadable:void 0;e.exports=function(e){return s(e)||o(e)||!!(i&&e&&e[i])}},30361:e=>{var r=/^(?:0|[1-9]\d*)$/;e.exports=function(e,t){var n=typeof e;return!!(t=null==t?9007199254740991:t)&&("number"==n||"symbol"!=n&&r.test(e))&&-1{var o=n(75288),s=n(64894),i=n(30361),a=n(23805);e.exports=function(e,t,n){if(!a(n))return!1;var r=typeof t;return!!("number"==r?s(n)&&i(t,n.length):"string"==r&&t in n)&&o(n[t],e)}},28586:(e,t,n)=>{var r=n(56449),o=n(44394),s=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,i=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!o(e))||i.test(e)||!s.test(e)||null!=t&&e in Object(t)}},74218:e=>{e.exports=function(e){var t=typeof e;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==e:null===e}},85087:(e,t,n)=>{var r=n(30980),o=n(37381),s=n(62284),i=n(53758);e.exports=function(e){var t=s(e),n=i[t];if("function"!=typeof n||!(t in r.prototype))return!1;if(e===n)return!0;t=o(n);return!!t&&e===t[0]}},87296:(e,t,n)=>{var n=n(55481),r=(n=/[^.]+$/.exec(n&&n.keys&&n.keys.IE_PROTO||""))?"Symbol(src)_1."+n:"";e.exports=function(e){return!!r&&r in e}},55527:e=>{var n=Object.prototype;e.exports=function(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||n)}},30756:(e,t,n)=>{var r=n(23805);e.exports=function(e){return e==e&&!r(e)}},63702:e=>{e.exports=function(){this.__data__=[],this.size=0}},70080:(e,t,n)=>{var r=n(26025),o=Array.prototype.splice;e.exports=function(e){var t=this.__data__,e=r(t,e);return!(e<0||(e==t.length-1?t.pop():o.call(t,e,1),--this.size,0))}},24739:(e,t,n)=>{var r=n(26025);e.exports=function(e){var t=this.__data__,e=r(t,e);return e<0?void 0:t[e][1]}},48655:(e,t,n)=>{var r=n(26025);e.exports=function(e){return-1{var o=n(26025);e.exports=function(e,t){var n=this.__data__,r=o(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}},63040:(e,t,n)=>{var r=n(21549),o=n(80079),s=n(68223);e.exports=function(){this.size=0,this.__data__={hash:new r,map:new(s||o),string:new r}}},17670:(e,t,n)=>{var r=n(12651);e.exports=function(e){e=r(this,e).delete(e);return this.size-=e?1:0,e}},90289:(e,t,n)=>{var r=n(12651);e.exports=function(e){return r(this,e).get(e)}},4509:(e,t,n)=>{var r=n(12651);e.exports=function(e){return r(this,e).has(e)}},72949:(e,t,n)=>{var o=n(12651);e.exports=function(e,t){var n=o(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this}},20317:e=>{e.exports=function(e){var n=-1,r=Array(e.size);return e.forEach(function(e,t){r[++n]=[t,e]}),r}},67197:e=>{e.exports=function(t,n){return function(e){return null!=e&&e[t]===n&&(void 0!==n||t in Object(e))}}},62224:(e,t,n)=>{var r=n(50104);e.exports=function(e){var e=r(e,function(e){return 500===t.size&&t.clear(),e}),t=e.cache;return e}},3209:(e,t,n)=>{var a=n(91596),l=n(53320),c=n(36306),u="__lodash_placeholder__",p=Math.min;e.exports=function(e,t){var n=e[1],r=t[1],o=n|r,s=128==r&&8==n||128==r&&256==n&&e[7].length<=t[8]||384==r&&t[7].length<=t[8]&&8==n;if(!(o<131)&&!s)return e;1&r&&(e[2]=t[2],o|=1&n?0:4);var i,s=t[3];return s&&(i=e[3],e[3]=i?a(i,s,t[4]):s,e[4]=i?c(e[3],u):t[4]),(s=t[5])&&(i=e[5],e[5]=i?l(i,s,t[6]):s,e[6]=i?c(e[5],u):t[6]),(s=t[7])&&(e[7]=s),128&r&&(e[8]=null==e[8]?t[8]:p(e[8],t[8])),null==e[9]&&(e[9]=t[9]),e[0]=t[0],e[1]=o,e}},48152:(e,t,n)=>{n=n(28303),n=n&&new n;e.exports=n},81042:(e,t,n)=>{n=n(56110)(Object,"create");e.exports=n},3650:(e,t,n)=>{n=n(74335)(Object.keys,Object);e.exports=n},90181:e=>{e.exports=function(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}},86009:(e,t,n)=>{e=n.nmd(e);var n=n(34840),t=t&&!t.nodeType&&t,r=t&&e&&!e.nodeType&&e,o=r&&r.exports===t&&n.process,t=function(){try{return r&&r.require&&r.require("util").types||o&&o.binding&&o.binding("util")}catch(e){}}();e.exports=t},59350:e=>{var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},74335:e=>{e.exports=function(t,n){return function(e){return t(n(e))}}},56757:(e,t,n)=>{var l=n(91033),c=Math.max;e.exports=function(s,i,a){return i=c(void 0===i?s.length-1:i,0),function(){for(var e=arguments,t=-1,n=c(e.length-i,0),r=Array(n);++t{var r=n(47422),o=n(25160);e.exports=function(e,t){return t.length<2?e:r(e,o(t,0,-1))}},84629:e=>{e.exports={}},68294:(e,t,n)=>{var i=n(23007),a=n(30361),l=Math.min;e.exports=function(e,t){for(var n=e.length,r=l(t.length,n),o=i(e);r--;){var s=t[r];e[r]=a(s,n)?o[s]:void 0}return e}},36306:e=>{var a="__lodash_placeholder__";e.exports=function(e,t){for(var n=-1,r=e.length,o=0,s=[];++n{var n=n(34840),r="object"==typeof self&&self&&self.Object===Object&&self,n=n||r||Function("return this")();e.exports=n},14974:e=>{e.exports=function(e,t){if(("constructor"!==t||"function"!=typeof e[t])&&"__proto__"!=t)return e[t]}},31380:e=>{e.exports=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this}},51459:e=>{e.exports=function(e){return this.__data__.has(e)}},54641:(e,t,n)=>{var r=n(68882),n=n(51811)(r);e.exports=n},84247:e=>{e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach(function(e){n[++t]=e}),n}},32865:(e,t,n)=>{var r=n(19570),n=n(51811)(r);e.exports=n},70981:(e,t,n)=>{var r=n(75251),o=n(62060),s=n(32865),i=n(75948);e.exports=function(e,t,n){t+="";return s(e,o(t,i(r(t),n)))}},51811:e=>{var s=Date.now;e.exports=function(n){var r=0,o=0;return function(){var e=s(),t=16-(e-o);if(o=e,0{var r=n(80079);e.exports=function(){this.__data__=new r,this.size=0}},90938:e=>{e.exports=function(e){var t=this.__data__,e=t.delete(e);return this.size=t.size,e}},63605:e=>{e.exports=function(e){return this.__data__.get(e)}},29817:e=>{e.exports=function(e){return this.__data__.has(e)}},80945:(e,t,n)=>{var o=n(80079),s=n(68223),i=n(53661);e.exports=function(e,t){var n=this.__data__;if(n instanceof o){var r=n.__data__;if(!s||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new i(r)}return n.set(e,t),this.size=n.size,this}},76959:e=>{e.exports=function(e,t,n){for(var r=n-1,o=e.length;++r{var r=n(61074),o=n(49698),s=n(42054);e.exports=function(e){return(o(e)?s:r)(e)}},61802:(e,t,n)=>{var n=n(62224),r=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,s=/\\(\\)?/g,n=n(function(e){var o=[];return 46===e.charCodeAt(0)&&o.push(""),e.replace(r,function(e,t,n,r){o.push(n?r.replace(s,"$1"):t||e)}),o});e.exports=n},77797:(e,t,n)=>{var r=n(44394);e.exports=function(e){if("string"==typeof e||r(e))return e;var t=e+"";return"0"==t&&1/e==-1/0?"-0":t}},47473:e=>{var t=Function.prototype.toString;e.exports=function(e){if(null!=e){try{return t.call(e)}catch(e){}try{return e+""}catch(e){}}return""}},31800:e=>{var n=/\s/;e.exports=function(e){for(var t=e.length;t--&&n.test(e.charAt(t)););return t}},42054:e=>{var t="\\ud800-\\udfff",n="["+t+"]",r="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",o="\\ud83c[\\udffb-\\udfff]",t="[^"+t+"]",s="(?:\\ud83c[\\udde6-\\uddff]){2}",i="[\\ud800-\\udbff][\\udc00-\\udfff]",a="(?:"+r+"|"+o+")?",l="[\\ufe0e\\ufe0f]?",l=l+a+"(?:\\u200d(?:"+[t,s,i].join("|")+")"+l+a+")*",a="(?:"+[t+r+"?",r,s,i,n].join("|")+")",c=RegExp(o+"(?="+o+")|"+a+l,"g");e.exports=function(e){return e.match(c)||[]}},22225:e=>{var t="\\ud800-\\udfff",n="\\u2700-\\u27bf",r="a-z\\xdf-\\xf6\\xf8-\\xff",o="A-Z\\xc0-\\xd6\\xd8-\\xde",s="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",i="["+s+"]",a="["+n+"]",l="["+r+"]",s="[^"+t+s+"\\d+"+n+r+o+"]",n="(?:\\ud83c[\\udde6-\\uddff]){2}",r="[\\ud800-\\udbff][\\udc00-\\udfff]",o="["+o+"]",c="(?:"+l+"|"+s+")",s="(?:"+o+"|"+s+")",u="(?:['’](?:d|ll|m|re|s|t|ve))?",p="(?:['’](?:D|LL|M|RE|S|T|VE))?",h="(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?",d="[\\ufe0e\\ufe0f]?",t=d+h+"(?:\\u200d(?:"+["[^"+t+"]",n,r].join("|")+")"+d+h+")*",d="(?:"+[a,n,r].join("|")+")"+t,f=RegExp([o+"?"+l+"+"+u+"(?="+[i,o,"$"].join("|")+")",s+"+"+p+"(?="+[i,o+c,"$"].join("|")+")",o+"?"+c+"+"+u,o+"+"+p,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])","\\d+",d].join("|"),"g");e.exports=function(e){return e.match(f)||[]}},75948:(e,t,n)=>{var o=n(83729),s=n(15325),i=[["ary",128],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",32],["partialRight",64],["rearg",256]];e.exports=function(n,r){return o(i,function(e){var t="_."+e[0];r&e[1]&&!s(n,t)&&n.push(t)}),n.sort()}},80257:(e,t,n)=>{var r=n(30980),o=n(56017),s=n(23007);e.exports=function(e){if(e instanceof r)return e.clone();var t=new o(e.__wrapped__,e.__chain__);return t.__actions__=s(e.__actions__),t.__index__=e.__index__,t.__values__=e.__values__,t}},64626:(e,t,n)=>{var r=n(66977);e.exports=function(e,t,n){return t=n?void 0:t,t=e&&null==t?e.length:t,r(e,128,void 0,void 0,void 0,void 0,t)}},84058:(e,t,n)=>{var r=n(14792),n=n(45539)(function(e,t,n){return t=t.toLowerCase(),e+(n?r(t):t)});e.exports=n},14792:(e,t,n)=>{var r=n(13222),o=n(55808);e.exports=function(e){return o(r(e).toLowerCase())}},32629:(e,t,n)=>{var r=n(9999);e.exports=function(e){return r(e,4)}},37334:e=>{e.exports=function(e){return function(){return e}}},49747:(e,t,n)=>{var r=n(66977);function o(e,t,n){e=r(e,8,void 0,void 0,void 0,void 0,void 0,t=n?void 0:t);return e.placeholder=o.placeholder,e}o.placeholder={},e.exports=o},38221:(e,t,n)=>{var v=n(23805),b=n(10124),w=n(99374),E=Math.max,S=Math.min;e.exports=function(r,n,e){var o,s,i,a,l,c,u=0,p=!1,h=!1,t=!0;if("function"!=typeof r)throw new TypeError("Expected a function");function d(e){var t=o,n=s;return o=s=void 0,u=e,a=r.apply(n,t)}function f(e){var t=e-c;return void 0===c||n<=t||t<0||h&&i<=e-u}function m(){var e,t=b();if(f(t))return g(t);l=setTimeout(m,(e=n-((t=t)-c),h?S(e,i-(t-u)):e))}function g(e){return l=void 0,t&&o?d(e):(o=s=void 0,a)}function y(){var e=b(),t=f(e);if(o=arguments,s=this,c=e,t){if(void 0===l)return u=e=c,l=setTimeout(m,n),p?d(e):a;if(h)return clearTimeout(l),l=setTimeout(m,n),d(c)}return void 0===l&&(l=setTimeout(m,n)),a}return n=w(n)||0,v(e)&&(p=!!e.leading,i=(h="maxWait"in e)?E(w(e.maxWait)||0,n):i,t="trailing"in e?!!e.trailing:t),y.cancel=function(){void 0!==l&&clearTimeout(l),o=c=s=l=void(u=0)},y.flush=function(){return void 0===l?a:g(b())},y}},50828:(e,t,n)=>{var r=n(24647),o=n(13222),s=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,i=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g");e.exports=function(e){return(e=o(e))&&e.replace(s,r).replace(i,"")}},75288:e=>{e.exports=function(e,t){return e===t||e!=e&&t!=t}},7309:(e,t,n)=>{n=n(62006)(n(24713));e.exports=n},24713:(e,t,n)=>{var o=n(2523),s=n(15389),i=n(61489),a=Math.max;e.exports=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;n=null==n?0:i(n);return n<0&&(n=a(r+n,0)),o(e,s(t,3),n)}},35970:(e,t,n)=>{var r=n(83120);e.exports=function(e){return null!=e&&e.length?r(e,1):[]}},73424:(e,t,n)=>{var B=n(16962),L=n(2874),q=Array.prototype.push;function $(n,e){return 2==e?function(e,t){return n(e,t)}:function(e){return n(e)}}function z(e){for(var t=e?e.length:0,n=Array(t);t--;)n[t]=e[t];return n}function U(r,o){return function(){var e=arguments.length;if(e){for(var t=Array(e);e--;)t[e]=arguments[e];var n=t[0]=o.apply(void 0,t);return r.apply(void 0,t),n}}}e.exports=function a(n,e,t,l){var c="function"==typeof e,r=e===Object(e);if(r&&(l=t,t=e,e=void 0),null==t)throw new TypeError;var u={cap:!("cap"in(l=l||{}))||l.cap,curry:!("curry"in l)||l.curry,fixed:!("fixed"in l)||l.fixed,immutable:!("immutable"in l)||l.immutable,rearg:!("rearg"in l)||l.rearg},o=c?t:L,p="curry"in l&&l.curry,h="fixed"in l&&l.fixed,s="rearg"in l&&l.rearg,d=c?t.runInContext():void 0,f=c?t:{ary:n.ary,assign:n.assign,clone:n.clone,curry:n.curry,forEach:n.forEach,isArray:n.isArray,isError:n.isError,isFunction:n.isFunction,isWeakMap:n.isWeakMap,iteratee:n.iteratee,keys:n.keys,rearg:n.rearg,toInteger:n.toInteger,toPath:n.toPath},m=f.ary,g=f.assign,y=f.clone,v=f.curry,b=f.forEach,i=f.isArray,w=f.isError,E=f.isFunction,S=f.isWeakMap,x=f.keys,_=f.rearg,k=f.toInteger,A=f.toPath,O=x(B.aryMethod),C={castArray:function(t){return function(){var e=arguments[0];return i(e)?t(z(e)):t.apply(void 0,arguments)}},iteratee:function(r){return function(){var e=arguments[1],t=r(arguments[0],e),n=t.length;return u.cap&&"number"==typeof e?(e=2{s.aliasToReal={each:"forEach",eachRight:"forEachRight",entries:"toPairs",entriesIn:"toPairsIn",extend:"assignIn",extendAll:"assignInAll",extendAllWith:"assignInAllWith",extendWith:"assignInWith",first:"head",conforms:"conformsTo",matches:"isMatch",property:"get",__:"placeholder",F:"stubFalse",T:"stubTrue",all:"every",allPass:"overEvery",always:"constant",any:"some",anyPass:"overSome",apply:"spread",assoc:"set",assocPath:"set",complement:"negate",compose:"flowRight",contains:"includes",dissoc:"unset",dissocPath:"unset",dropLast:"dropRight",dropLastWhile:"dropRightWhile",equals:"isEqual",identical:"eq",indexBy:"keyBy",init:"initial",invertObj:"invert",juxt:"over",omitAll:"omit",nAry:"ary",path:"get",pathEq:"matchesProperty",pathOr:"getOr",paths:"at",pickAll:"pick",pipe:"flow",pluck:"map",prop:"get",propEq:"matchesProperty",propOr:"getOr",props:"at",symmetricDifference:"xor",symmetricDifferenceBy:"xorBy",symmetricDifferenceWith:"xorWith",takeLast:"takeRight",takeLastWhile:"takeRightWhile",unapply:"rest",unnest:"flatten",useWith:"overArgs",where:"conformsTo",whereEq:"isMatch",zipObj:"zipObject"},s.aryMethod={1:["assignAll","assignInAll","attempt","castArray","ceil","create","curry","curryRight","defaultsAll","defaultsDeepAll","floor","flow","flowRight","fromPairs","invert","iteratee","memoize","method","mergeAll","methodOf","mixin","nthArg","over","overEvery","overSome","rest","reverse","round","runInContext","spread","template","trim","trimEnd","trimStart","uniqueId","words","zipAll"],2:["add","after","ary","assign","assignAllWith","assignIn","assignInAllWith","at","before","bind","bindAll","bindKey","chunk","cloneDeepWith","cloneWith","concat","conformsTo","countBy","curryN","curryRightN","debounce","defaults","defaultsDeep","defaultTo","delay","difference","divide","drop","dropRight","dropRightWhile","dropWhile","endsWith","eq","every","filter","find","findIndex","findKey","findLast","findLastIndex","findLastKey","flatMap","flatMapDeep","flattenDepth","forEach","forEachRight","forIn","forInRight","forOwn","forOwnRight","get","groupBy","gt","gte","has","hasIn","includes","indexOf","intersection","invertBy","invoke","invokeMap","isEqual","isMatch","join","keyBy","lastIndexOf","lt","lte","map","mapKeys","mapValues","matchesProperty","maxBy","meanBy","merge","mergeAllWith","minBy","multiply","nth","omit","omitBy","overArgs","pad","padEnd","padStart","parseInt","partial","partialRight","partition","pick","pickBy","propertyOf","pull","pullAll","pullAt","random","range","rangeRight","rearg","reject","remove","repeat","restFrom","result","sampleSize","some","sortBy","sortedIndex","sortedIndexOf","sortedLastIndex","sortedLastIndexOf","sortedUniqBy","split","spreadFrom","startsWith","subtract","sumBy","take","takeRight","takeRightWhile","takeWhile","tap","throttle","thru","times","trimChars","trimCharsEnd","trimCharsStart","truncate","union","uniqBy","uniqWith","unset","unzipWith","without","wrap","xor","zip","zipObject","zipObjectDeep"],3:["assignInWith","assignWith","clamp","differenceBy","differenceWith","findFrom","findIndexFrom","findLastFrom","findLastIndexFrom","getOr","includesFrom","indexOfFrom","inRange","intersectionBy","intersectionWith","invokeArgs","invokeArgsMap","isEqualWith","isMatchWith","flatMapDepth","lastIndexOfFrom","mergeWith","orderBy","padChars","padCharsEnd","padCharsStart","pullAllBy","pullAllWith","rangeStep","rangeStepRight","reduce","reduceRight","replace","set","slice","sortedIndexBy","sortedLastIndexBy","transform","unionBy","unionWith","update","xorBy","xorWith","zipWith"],4:["fill","setWith","updateWith"]},s.aryRearg={2:[1,0],3:[2,0,1],4:[3,2,0,1]},s.iterateeAry={dropRightWhile:1,dropWhile:1,every:1,filter:1,find:1,findFrom:1,findIndex:1,findIndexFrom:1,findKey:1,findLast:1,findLastFrom:1,findLastIndex:1,findLastIndexFrom:1,findLastKey:1,flatMap:1,flatMapDeep:1,flatMapDepth:1,forEach:1,forEachRight:1,forIn:1,forInRight:1,forOwn:1,forOwnRight:1,map:1,mapKeys:1,mapValues:1,partition:1,reduce:2,reduceRight:2,reject:1,remove:1,some:1,takeRightWhile:1,takeWhile:1,times:1,transform:2},s.iterateeRearg={mapKeys:[1],reduceRight:[1,0]},s.methodRearg={assignInAllWith:[1,0],assignInWith:[1,2,0],assignAllWith:[1,0],assignWith:[1,2,0],differenceBy:[1,2,0],differenceWith:[1,2,0],getOr:[2,1,0],intersectionBy:[1,2,0],intersectionWith:[1,2,0],isEqualWith:[1,2,0],isMatchWith:[2,1,0],mergeAllWith:[1,0],mergeWith:[1,2,0],padChars:[2,1,0],padCharsEnd:[2,1,0],padCharsStart:[2,1,0],pullAllBy:[2,1,0],pullAllWith:[2,1,0],rangeStep:[1,2,0],rangeStepRight:[1,2,0],setWith:[3,1,2,0],sortedIndexBy:[2,1,0],sortedLastIndexBy:[2,1,0],unionBy:[1,2,0],unionWith:[1,2,0],updateWith:[3,1,2,0],xorBy:[1,2,0],xorWith:[1,2,0],zipWith:[1,2,0]},s.methodSpread={assignAll:{start:0},assignAllWith:{start:0},assignInAll:{start:0},assignInAllWith:{start:0},defaultsAll:{start:0},defaultsDeepAll:{start:0},invokeArgs:{start:2},invokeArgsMap:{start:2},mergeAll:{start:0},mergeAllWith:{start:0},partial:{start:1},partialRight:{start:1},without:{start:1},zipAll:{start:0}},s.mutate={array:{fill:!0,pull:!0,pullAll:!0,pullAllBy:!0,pullAllWith:!0,pullAt:!0,remove:!0,reverse:!0},object:{assign:!0,assignAll:!0,assignAllWith:!0,assignIn:!0,assignInAll:!0,assignInAllWith:!0,assignInWith:!0,assignWith:!0,defaults:!0,defaultsAll:!0,defaultsDeep:!0,defaultsDeepAll:!0,merge:!0,mergeAll:!0,mergeAllWith:!0,mergeWith:!0},set:{set:!0,setWith:!0,unset:!0,update:!0,updateWith:!0}},s.realToAlias=function(){var e,t=Object.prototype.hasOwnProperty,n=s.aliasToReal,r={};for(e in n){var o=n[e];t.call(r,o)?r[o].push(e):r[o]=[e]}return r}(),s.remap={assignAll:"assign",assignAllWith:"assignWith",assignInAll:"assignIn",assignInAllWith:"assignInWith",curryN:"curry",curryRightN:"curryRight",defaultsAll:"defaults",defaultsDeepAll:"defaultsDeep",findFrom:"find",findIndexFrom:"findIndex",findLastFrom:"findLast",findLastIndexFrom:"findLastIndex",getOr:"get",includesFrom:"includes",indexOfFrom:"indexOf",invokeArgs:"invoke",invokeArgsMap:"invokeMap",lastIndexOfFrom:"lastIndexOf",mergeAll:"merge",mergeAllWith:"mergeWith",padChars:"pad",padCharsEnd:"padEnd",padCharsStart:"padStart",propertyOf:"get",rangeStep:"range",rangeStepRight:"rangeRight",restFrom:"rest",spreadFrom:"spread",trimChars:"trim",trimCharsEnd:"trimEnd",trimCharsStart:"trimStart",zipAll:"zip"},s.skipFixed={castArray:!0,flow:!0,flowRight:!0,iteratee:!0,mixin:!0,rearg:!0,runInContext:!0},s.skipRearg={add:!0,assign:!0,assignIn:!0,bind:!0,bindKey:!0,concat:!0,difference:!0,divide:!0,eq:!0,gt:!0,gte:!0,isEqual:!0,lt:!0,lte:!0,matchesProperty:!0,merge:!0,multiply:!0,overArgs:!0,partial:!0,partialRight:!0,propertyOf:!0,random:!0,range:!0,rangeRight:!0,subtract:!0,zip:!0,zipObject:!0,zipObjectDeep:!0}},47934:(e,t,n)=>{e.exports={ary:n(64626),assign:n(74733),clone:n(32629),curry:n(49747),forEach:n(83729),isArray:n(56449),isError:n(23546),isFunction:n(1882),isWeakMap:n(47886),iteratee:n(33855),keys:n(88984),rearg:n(84195),toInteger:n(61489),toPath:n(42072)}},56367:(e,t,n)=>{e.exports=n(77731)},79920:(e,t,n)=>{var r=n(73424),o=n(47934);e.exports=function(e,t,n){return r(o,e,t,n)}},2874:e=>{e.exports={}},77731:(e,t,n)=>{var r=n(79920)("set",n(63560));r.placeholder=n(2874),e.exports=r},58156:(e,t,n)=>{var r=n(47422);e.exports=function(e,t,n){e=null==e?void 0:r(e,t);return void 0===e?n:e}},80631:(e,t,n)=>{var r=n(28077),o=n(49326);e.exports=function(e,t){return null!=e&&o(e,t,r)}},83488:e=>{e.exports=function(e){return e}},72428:(e,t,n)=>{var r=n(27534),o=n(40346),n=Object.prototype,s=n.hasOwnProperty,i=n.propertyIsEnumerable,n=r(function(){return arguments}())?r:function(e){return o(e)&&s.call(e,"callee")&&!i.call(e,"callee")};e.exports=n},56449:e=>{var t=Array.isArray;e.exports=t},64894:(e,t,n)=>{var r=n(1882),o=n(30294);e.exports=function(e){return null!=e&&o(e.length)&&!r(e)}},83693:(e,t,n)=>{var r=n(64894),o=n(40346);e.exports=function(e){return o(e)&&r(e)}},53812:(e,t,n)=>{var r=n(72552),o=n(40346);e.exports=function(e){return!0===e||!1===e||o(e)&&"[object Boolean]"==r(e)}},3656:(e,t,n)=>{e=n.nmd(e);var r=n(9325),n=n(89935),t=t&&!t.nodeType&&t,o=t&&e&&!e.nodeType&&e,o=o&&o.exports===t?r.Buffer:void 0,t=(o?o.isBuffer:void 0)||n;e.exports=t},62193:(e,t,n)=>{var r=n(88984),o=n(5861),s=n(72428),i=n(56449),a=n(64894),l=n(3656),c=n(55527),u=n(37167),p=Object.prototype.hasOwnProperty;e.exports=function(e){if(null==e)return!0;if(a(e)&&(i(e)||"string"==typeof e||"function"==typeof e.splice||l(e)||u(e)||s(e)))return!e.length;var t,n=o(e);if("[object Map]"==n||"[object Set]"==n)return!e.size;if(c(e))return!r(e).length;for(t in e)if(p.call(e,t))return!1;return!0}},2404:(e,t,n)=>{var r=n(60270);e.exports=function(e,t){return r(e,t)}},23546:(e,t,n)=>{var r=n(72552),o=n(40346),s=n(11331);e.exports=function(e){if(!o(e))return!1;var t=r(e);return"[object Error]"==t||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!s(e)}},1882:(e,t,n)=>{var r=n(72552),o=n(23805);e.exports=function(e){if(!o(e))return!1;e=r(e);return"[object Function]"==e||"[object GeneratorFunction]"==e||"[object AsyncFunction]"==e||"[object Proxy]"==e}},30294:e=>{e.exports=function(e){return"number"==typeof e&&-1{var r=n(29172),o=n(27301),n=n(86009),n=n&&n.isMap,o=n?o(n):r;e.exports=o},5187:e=>{e.exports=function(e){return null===e}},98023:(e,t,n)=>{var r=n(72552),o=n(40346);e.exports=function(e){return"number"==typeof e||o(e)&&"[object Number]"==r(e)}},23805:e=>{e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},40346:e=>{e.exports=function(e){return null!=e&&"object"==typeof e}},11331:(e,t,n)=>{var r=n(72552),o=n(28879),s=n(40346),n=Function.prototype,i=Object.prototype,a=n.toString,l=i.hasOwnProperty,c=a.call(Object);e.exports=function(e){if(!s(e)||"[object Object]"!=r(e))return!1;e=o(e);if(null===e)return!0;e=l.call(e,"constructor")&&e.constructor;return"function"==typeof e&&e instanceof e&&a.call(e)==c}},38440:(e,t,n)=>{var r=n(16038),o=n(27301),n=n(86009),n=n&&n.isSet,o=n?o(n):r;e.exports=o},85015:(e,t,n)=>{var r=n(72552),o=n(56449),s=n(40346);e.exports=function(e){return"string"==typeof e||!o(e)&&s(e)&&"[object String]"==r(e)}},44394:(e,t,n)=>{var r=n(72552),o=n(40346);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},37167:(e,t,n)=>{var r=n(4901),o=n(27301),n=n(86009),n=n&&n.isTypedArray,o=n?o(n):r;e.exports=o},47886:(e,t,n)=>{var r=n(5861),o=n(40346);e.exports=function(e){return o(e)&&"[object WeakMap]"==r(e)}},33855:(e,t,n)=>{var r=n(9999),o=n(15389);e.exports=function(e){return o("function"==typeof e?e:r(e,1))}},95950:(e,t,n)=>{var r=n(70695),o=n(88984),s=n(64894);e.exports=function(e){return(s(e)?r:o)(e)}},37241:(e,t,n)=>{var r=n(70695),o=n(72903),s=n(64894);e.exports=function(e){return s(e)?r(e,!0):o(e)}},68090:e=>{e.exports=function(e){var t=null==e?0:e.length;return t?e[t-1]:void 0}},50104:(e,t,n)=>{var i=n(53661);function a(r,o){if("function"!=typeof r||null!=o&&"function"!=typeof o)throw new TypeError("Expected a function");function s(){var e=arguments,t=o?o.apply(this,e):e[0],n=s.cache;return n.has(t)?n.get(t):(e=r.apply(this,e),s.cache=n.set(t,e)||n,e)}return s.cache=new(a.Cache||i),s}a.Cache=i,e.exports=a},55364:(e,t,n)=>{var r=n(85250),n=n(20999)(function(e,t,n){r(e,t,n)});e.exports=n},6048:e=>{e.exports=function(t){if("function"!=typeof t)throw new TypeError("Expected a function");return function(){var e=arguments;switch(e.length){case 0:return!t.call(this);case 1:return!t.call(this,e[0]);case 2:return!t.call(this,e[0],e[1]);case 3:return!t.call(this,e[0],e[1],e[2])}return!t.apply(this,e)}}},63950:e=>{e.exports=function(){}},10124:(e,t,n)=>{var r=n(9325);e.exports=function(){return r.Date.now()}},90179:(e,t,n)=>{var s=n(34932),i=n(9999),a=n(19931),l=n(31769),c=n(21791),u=n(53138),r=n(38816),p=n(83349),n=r(function(t,e){var n={};if(null==t)return n;var r=!1;e=s(e,function(e){return e=l(e,t),r=r||1{var r=n(47237),o=n(17255),s=n(28586),i=n(77797);e.exports=function(e){return s(e)?r(i(e)):o(e)}},84195:(e,t,n)=>{var r=n(66977),n=n(38816)(function(e,t){return r(e,256,void 0,void 0,void 0,t)});e.exports=n},40860:(e,t,n)=>{var s=n(40882),i=n(80909),a=n(15389),l=n(85558),c=n(56449);e.exports=function(e,t,n){var r=c(e)?s:l,o=arguments.length<3;return r(e,a(t,4),n,o,i)}},63560:(e,t,n)=>{var r=n(73170);e.exports=function(e,t,n){return null==e?e:r(e,t,n)}},42426:(e,t,n)=>{var o=n(14248),s=n(15389),i=n(90916),a=n(56449),l=n(36800);e.exports=function(e,t,n){var r=a(e)?o:i;return n&&l(e,t,n)&&(t=void 0),r(e,s(t,3))}},63345:e=>{e.exports=function(){return[]}},89935:e=>{e.exports=function(){return!1}},17400:(e,t,n)=>{var r=n(99374);e.exports=function(e){return e?(e=r(e))===1/0||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}},61489:(e,t,n)=>{var r=n(17400);e.exports=function(e){var e=r(e),t=e%1;return e==e?t?e-t:e:0}},80218:(e,t,n)=>{var r=n(13222);e.exports=function(e){return r(e).toLowerCase()}},99374:(e,t,n)=>{var r=n(54128),o=n(23805),s=n(44394),i=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,l=/^0o[0-7]+$/i,c=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(s(e))return NaN;if(o(e)&&(t="function"==typeof e.valueOf?e.valueOf():e,e=o(t)?t+"":t),"string"!=typeof e)return 0===e?e:+e;e=r(e);var t=a.test(e);return t||l.test(e)?c(e.slice(2),t?2:8):i.test(e)?NaN:+e}},42072:(e,t,n)=>{var r=n(34932),o=n(23007),s=n(56449),i=n(44394),a=n(61802),l=n(77797),c=n(13222);e.exports=function(e){return s(e)?r(e,l):i(e)?[e]:o(a(c(e)))}},69884:(e,t,n)=>{var r=n(21791),o=n(37241);e.exports=function(e){return r(e,o(e))}},13222:(e,t,n)=>{var r=n(77556);e.exports=function(e){return null==e?"":r(e)}},55808:(e,t,n)=>{n=n(12507)("toUpperCase");e.exports=n},66645:(e,t,n)=>{var r=n(1733),o=n(45434),s=n(13222),i=n(22225);e.exports=function(e,t,n){return e=s(e),void 0===(t=n?void 0:t)?(o(e)?i:r)(e):e.match(t)||[]}},53758:(e,t,n)=>{var r=n(30980),o=n(56017),s=n(94033),i=n(56449),a=n(40346),l=n(80257),c=Object.prototype.hasOwnProperty;function u(e){if(a(e)&&!i(e)&&!(e instanceof r)){if(e instanceof o)return e;if(c.call(e,"__wrapped__"))return l(e)}return new o(e)}u.prototype=s.prototype,e.exports=u.prototype.constructor=u},47248:(e,t,n)=>{var r=n(16547),o=n(51234);e.exports=function(e,t){return o(e||[],t||[],r)}},43768:(e,t,n)=>{"use strict";var u=n(45981),p=n(85587),o=(t.highlight=h,t.highlightAuto=function(e,t){var n,r,o,s,i=t||{},a=i.subset||u.listLanguages(),i=i.prefix,l=a.length,c=-1;if(null==i&&0,"string"!=typeof e)throw p("Expected `string` for value, got `%s`",e);for(r={relevance:0,language:null,value:[]},n={relevance:0,language:null,value:[]};++cr.relevance&&(r=o),o.relevance>n.relevance&&(r=n,n=o));return r.language&&(n.secondBest=r),n},t.registerLanguage=function(e,t){u.registerLanguage(e,t)},t.listLanguages=function(){return u.listLanguages()},t.registerAlias=function(e,t){var n,r=e;for(n in t&&((r={})[e]=t),r)u.registerAliases(r[n],{languageName:n})},s.prototype.addText=function(e){var t,n=this.stack;""!==e&&((t=(n=n[n.length-1]).children[n.children.length-1])&&"text"===t.type?t.value+=e:n.children.push({type:"text",value:e}))},s.prototype.addKeyword=function(e,t){this.openNode(t),this.addText(e),this.closeNode()},s.prototype.addSublanguage=function(e,t){var n=this.stack,n=n[n.length-1],e=e.rootNode.children;n.children=n.children.concat(t?{type:"element",tagName:"span",properties:{className:[t]},children:e}:e)},s.prototype.openNode=function(e){var t=this.stack,e={type:"element",tagName:"span",properties:{className:[this.options.classPrefix+e]},children:[]};t[t.length-1].children.push(e),t.push(e)},s.prototype.closeNode=function(){this.stack.pop()},s.prototype.closeAllNodes=r,s.prototype.finalize=r,s.prototype.toHTML=function(){return""},"hljs-");function h(e,t,n){var r=u.configure({}),n=(n||{}).prefix;if("string"!=typeof e)throw p("Expected `string` for name, got `%s`",e);if(!u.getLanguage(e))throw p("Unknown language: `%s` is not registered",e);if("string"!=typeof t)throw p("Expected `string` for value, got `%s`",t);if(u.configure({__emitter:s,classPrefix:n=null==n?o:n}),n=u.highlight(t,{language:e,ignoreIllegals:!0}),u.configure(r||{}),n.errorRaised)throw n.errorRaised;return{relevance:n.relevance,language:n.language,value:n.emitter.rootNode.children}}function s(e){this.options=e,this.rootNode={children:[]},this.stack=[this.rootNode]}function r(){}},92340:(e,t,n)=>{const r=n(6048);function o(t){return"string"==typeof t?e=>e.element===t:t.constructor&&t.extend?e=>e instanceof t:t}class s{constructor(e){this.elements=e||[]}toValue(){return this.elements.map(e=>e.toValue())}map(e,t){return this.elements.map(e,t)}flatMap(e,t){return this.map(e,t).reduce((e,t)=>e.concat(t),[])}compactMap(t,n){const r=[];return this.forEach(e=>{e=t.bind(n)(e);e&&r.push(e)}),r}filter(e,t){return e=o(e),new s(this.elements.filter(e,t))}reject(e,t){return e=o(e),new s(this.elements.filter(r(e),t))}find(e,t){return e=o(e),this.elements.find(e,t)}forEach(e,t){this.elements.forEach(e,t)}reduce(e,t){return this.elements.reduce(e,t)}includes(t){return this.elements.some(e=>e.equals(t))}shift(){return this.elements.shift()}unshift(e){this.elements.unshift(this.refract(e))}push(e){return this.elements.push(this.refract(e)),this}add(e){this.push(e)}get(e){return this.elements[e]}getValue(e){const t=this.elements[e];if(t)return t.toValue()}get length(){return this.elements.length}get isEmpty(){return 0===this.elements.length}get first(){return this.elements[0]}}"undefined"!=typeof Symbol&&(s.prototype[Symbol.iterator]=function(){return this.elements[Symbol.iterator]()}),e.exports=s},55973:e=>{e.exports=class t{constructor(e,t){this.key=e,this.value=t}clone(){const e=new t;return this.key&&(e.key=this.key.clone()),this.value&&(e.value=this.value.clone()),e}}},3110:(e,t,n)=>{const r=n(5187),o=n(85015),s=n(98023),i=n(53812),a=n(23805),l=n(85105),c=n(86804);class u{constructor(e){this.elementMap={},this.elementDetection=[],this.Element=c.Element,this.KeyValuePair=c.KeyValuePair,e&&e.noDefault||this.useDefault(),this._attributeElementKeys=[],this._attributeElementArrayKeys=[]}use(e){return e.namespace&&e.namespace({base:this}),e.load&&e.load({base:this}),this}useDefault(){return this.register("null",c.NullElement).register("string",c.StringElement).register("number",c.NumberElement).register("boolean",c.BooleanElement).register("array",c.ArrayElement).register("object",c.ObjectElement).register("member",c.MemberElement).register("ref",c.RefElement).register("link",c.LinkElement),this.detect(r,c.NullElement,!1).detect(o,c.StringElement,!1).detect(s,c.NumberElement,!1).detect(i,c.BooleanElement,!1).detect(Array.isArray,c.ArrayElement,!1).detect(a,c.ObjectElement,!1),this}register(e,t){return this._elements=void 0,this.elementMap[e]=t,this}unregister(e){return this._elements=void 0,delete this.elementMap[e],this}detect(e,t,n){return void 0===n||n?this.elementDetection.unshift([e,t]):this.elementDetection.push([e,t]),this}toElement(t){if(t instanceof this.Element)return t;let n;for(let e=0;e{var t=e[0].toUpperCase()+e.substr(1);this._elements[t]=this.elementMap[e]})),this._elements}get serialiser(){return new l(this)}}l.prototype.Namespace=u,e.exports=u},10866:(e,t,n)=>{const r=n(6048),o=n(92340);class s extends o{map(t,n){return this.elements.map(e=>t.bind(n)(e.value,e.key,e))}filter(t,n){return new s(this.elements.filter(e=>t.bind(n)(e.value,e.key,e)))}reject(e,t){return this.filter(r(e.bind(t)))}forEach(n,r){return this.elements.forEach((e,t)=>{n.bind(r)(e.value,e.key,e,t)})}keys(){return this.map((e,t)=>t.toValue())}values(){return this.map(e=>e.toValue())}}e.exports=s},86804:(e,t,n)=>{const r=n(10316),o=n(41067),s=n(71167),i=n(40239),a=n(12242),l=n(6233),c=n(87726),u=n(61045),p=n(86303),h=n(14540),d=n(92340),f=n(10866),m=n(55973);function g(e){return e instanceof r?e:"string"==typeof e?new s(e):"number"==typeof e?new i(e):"boolean"==typeof e?new a(e):null===e?new o:Array.isArray(e)?new l(e.map(g)):"object"==typeof e?new u(e):e}r.prototype.ObjectElement=u,r.prototype.RefElement=h,r.prototype.MemberElement=c,r.prototype.refract=g,d.prototype.refract=g,e.exports={Element:r,NullElement:o,StringElement:s,NumberElement:i,BooleanElement:a,ArrayElement:l,MemberElement:c,ObjectElement:u,LinkElement:p,RefElement:h,refract:g,ArraySlice:d,ObjectSlice:f,KeyValuePair:m}},86303:(e,t,n)=>{n=n(10316);e.exports=class extends n{constructor(e,t,n){super(e||[],t,n),this.element="link"}get relation(){return this.attributes.get("relation")}set relation(e){this.attributes.set("relation",e)}get href(){return this.attributes.get("href")}set href(e){this.attributes.set("href",e)}}},14540:(e,t,n)=>{n=n(10316);e.exports=class extends n{constructor(e,t,n){super(e||[],t,n),this.element="ref",this.path||(this.path="element")}get path(){return this.attributes.get("path")}set path(e){this.attributes.set("path",e)}}},34035:(e,t,n)=>{var r=n(3110),o=n(86804);t.g$=r,t.KeyValuePair=n(55973),t.G6=o.ArraySlice,t.ot=o.ObjectSlice,t.Hg=o.Element,t.Om=o.StringElement,t.kT=o.NumberElement,t.bd=o.BooleanElement,t.Os=o.NullElement,t.wE=o.ArrayElement,t.Sh=o.ObjectElement,t.Pr=o.MemberElement,t.sI=o.RefElement,t.Ft=o.LinkElement,t.e=o.refract,n(85105),n(75147)},6233:(e,t,n)=>{const r=n(6048),o=n(10316),s=n(92340);class i extends o{constructor(e,t,n){super(e||[],t,n),this.element="array"}primitive(){return"array"}get(e){return this.content[e]}getValue(e){const t=this.get(e);if(t)return t.toValue()}getIndex(e){return this.content[e]}set(e,t){return this.content[e]=this.refract(t),this}remove(e){e=this.content.splice(e,1);return e.length?e[0]:null}map(e,t){return this.content.map(e,t)}flatMap(e,t){return this.map(e,t).reduce((e,t)=>e.concat(t),[])}compactMap(t,n){const r=[];return this.forEach(e=>{e=t.bind(n)(e);e&&r.push(e)}),r}filter(e,t){return new s(this.content.filter(e,t))}reject(e,t){return this.filter(r(e),t)}reduce(t,e){let n,r;r=void 0!==e?(n=0,this.refract(e)):(n=1,"object"===this.primitive()?this.first.value:this.first);for(let e=n;e{n.bind(r)(e,this.refract(t))})}shift(){return this.content.shift()}unshift(e){this.content.unshift(this.refract(e))}push(e){return this.content.push(this.refract(e)),this}add(e){this.push(e)}findElements(r,e){const t=e||{},o=!!t.recursive,s=void 0===t.results?[]:t.results;return this.forEach((e,t,n)=>{o&&void 0!==e.findElements&&e.findElements(r,{results:s,recursive:o}),r(e,t,n)&&s.push(e)}),s}find(e){return new s(this.findElements(e,{recursive:!0}))}findByElement(t){return this.find(e=>e.element===t)}findByClass(t){return this.find(e=>e.classes.includes(t))}getById(t){return this.find(e=>e.id.toValue()===t).first}includes(t){return this.content.some(e=>e.equals(t))}contains(e){return this.includes(e)}empty(){return new this.constructor([])}"fantasy-land/empty"(){return this.empty()}concat(e){return new this.constructor(this.content.concat(e.content))}"fantasy-land/concat"(e){return this.concat(e)}"fantasy-land/map"(e){return new this.constructor(this.map(e))}"fantasy-land/chain"(t){return this.map(e=>t(e),this).reduce((e,t)=>e.concat(t),this.empty())}"fantasy-land/filter"(e){return new this.constructor(this.content.filter(e))}"fantasy-land/reduce"(e,t){return this.content.reduce(e,t)}get length(){return this.content.length}get isEmpty(){return 0===this.content.length}get first(){return this.getIndex(0)}get second(){return this.getIndex(1)}get last(){return this.getIndex(this.length-1)}}i["fantasy-land/empty"]=i.empty=function(){return new this},"undefined"!=typeof Symbol&&(i.prototype[Symbol.iterator]=function(){return this.content[Symbol.iterator]()}),e.exports=i},12242:(e,t,n)=>{n=n(10316);e.exports=class extends n{constructor(e,t,n){super(e,t,n),this.element="boolean"}primitive(){return"boolean"}}},10316:(e,t,n)=>{const r=n(2404),a=n(55973),l=n(92340);e.exports=class o{constructor(e,t,n){t&&(this.meta=t),n&&(this.attributes=n),this.content=e}freeze(){Object.isFrozen(this)||(this._meta&&(this.meta.parent=this).meta.freeze(),this._attributes&&(this.attributes.parent=this).attributes.freeze(),this.children.forEach(e=>{e.parent=this,e.freeze()},this),this.content&&Array.isArray(this.content)&&Object.freeze(this.content),Object.freeze(this))}primitive(){}clone(){const e=new this.constructor;return e.element=this.element,this.meta.length&&(e._meta=this.meta.clone()),this.attributes.length&&(e._attributes=this.attributes.clone()),this.content?this.content.clone?e.content=this.content.clone():Array.isArray(this.content)?e.content=this.content.map(e=>e.clone()):e.content=this.content:e.content=this.content,e}toValue(){return this.content instanceof o?this.content.toValue():this.content instanceof a?{key:this.content.key.toValue(),value:this.content.value?this.content.value.toValue():void 0}:this.content&&this.content.map?this.content.map(e=>e.toValue(),this):this.content}toRef(e){if(""===this.id.toValue())throw Error("Cannot create reference to an element that does not contain an ID");const t=new this.RefElement(this.id.toValue());return e&&(t.path=e),t}findRecursive(...r){if(1(e.push(t),e),i=(e,t)=>{t.element===o&&e.push(t);const n=t.findRecursive(o);return n&&n.reduce(s,e),t.content instanceof a&&(t.content.key&&i(e,t.content.key),t.content.value&&i(e,t.content.value)),e};return this.content&&(this.content.element&&i(e,this.content),Array.isArray(this.content)&&this.content.reduce(i,e)),e=r.isEmpty?e:e.filter(e=>{let t=e.parents.map(e=>e.element);for(const e in r){var n=r[e];if(-1===(n=t.indexOf(n)))return!1;t=t.splice(0,n)}return!0})}set(e){return this.content=e,this}equals(e){return r(this.toValue(),e)}getMetaProperty(e,t){if(!this.meta.hasKey(e)){if(this.isFrozen){const e=this.refract(t);return e.freeze(),e}this.meta.set(e,t)}return this.meta.get(e)}setMetaProperty(e,t){this.meta.set(e,t)}get element(){return this._storedElement||"element"}set element(e){this._storedElement=e}get content(){return this._content}set content(t){if(t instanceof o)this._content=t;else if(t instanceof l)this.content=t.elements;else if("string"==typeof t||"number"==typeof t||"boolean"==typeof t||"null"===t||null==t)this._content=t;else if(t instanceof a)this._content=t;else if(Array.isArray(t))this._content=t.map(this.refract);else{if("object"!=typeof t)throw new Error("Cannot set content to given value");this._content=Object.keys(t).map(e=>new this.MemberElement(e,t[e]))}}get meta(){if(!this._meta){if(this.isFrozen){const e=new this.ObjectElement;return e.freeze(),e}this._meta=new this.ObjectElement}return this._meta}set meta(e){e instanceof this.ObjectElement?this._meta=e:this.meta.set(e||{})}get attributes(){if(!this._attributes){if(this.isFrozen){const e=new this.ObjectElement;return e.freeze(),e}this._attributes=new this.ObjectElement}return this._attributes}set attributes(e){e instanceof this.ObjectElement?this._attributes=e:this.attributes.set(e||{})}get id(){return this.getMetaProperty("id","")}set id(e){this.setMetaProperty("id",e)}get classes(){return this.getMetaProperty("classes",[])}set classes(e){this.setMetaProperty("classes",e)}get title(){return this.getMetaProperty("title","")}set title(e){this.setMetaProperty("title",e)}get description(){return this.getMetaProperty("description","")}set description(e){this.setMetaProperty("description",e)}get links(){return this.getMetaProperty("links",[])}set links(e){this.setMetaProperty("links",e)}get isFrozen(){return Object.isFrozen(this)}get parents(){let e=this.parent;const t=new l;for(;e;)t.push(e),e=e.parent;return t}get children(){if(Array.isArray(this.content))return new l(this.content);if(this.content instanceof a){const e=new l([this.content.key]);return this.content.value&&e.push(this.content.value),e}return this.content instanceof o?new l([this.content]):new l}get recursiveChildren(){const t=new l;return this.children.forEach(e=>{t.push(e),e.recursiveChildren.forEach(e=>{t.push(e)})}),t}}},87726:(e,t,n)=>{const o=n(55973),r=n(10316);e.exports=class extends r{constructor(e,t,n,r){super(new o,n,r),this.element="member",this.key=e,this.value=t}get key(){return this.content.key}set key(e){this.content.key=this.refract(e)}get value(){return this.content.value}set value(e){this.content.value=this.refract(e)}}},41067:(e,t,n)=>{n=n(10316);e.exports=class extends n{constructor(e,t,n){super(e||null,t,n),this.element="null"}primitive(){return"null"}set(){return new Error("Cannot set the value of null")}}},40239:(e,t,n)=>{n=n(10316);e.exports=class extends n{constructor(e,t,n){super(e,t,n),this.element="number"}primitive(){return"number"}}},61045:(e,t,n)=>{const r=n(6048),o=n(23805),s=n(6233),i=n(87726),a=n(10866);e.exports=class extends s{constructor(e,t,n){super(e||[],t,n),this.element="object"}primitive(){return"object"}toValue(){return this.content.reduce((e,t)=>(e[t.key.toValue()]=t.value?t.value.toValue():void 0,e),{})}get(e){e=this.getMember(e);if(e)return e.value}getMember(t){if(void 0!==t)return this.content.find(e=>e.key.toValue()===t)}remove(t){let n=null;return this.content=this.content.filter(e=>e.key.toValue()!==t||(n=e,!1)),n}getKey(e){e=this.getMember(e);if(e)return e.key}set(t,e){if(o(t))return Object.keys(t).forEach(e=>{this.set(e,t[e])}),this;const n=t,r=this.getMember(n);return r?r.value=e:this.content.push(new i(n,e)),this}keys(){return this.content.map(e=>e.key.toValue())}values(){return this.content.map(e=>e.value.toValue())}hasKey(t){return this.content.some(e=>e.key.equals(t))}items(){return this.content.map(e=>[e.key.toValue(),e.value.toValue()])}map(t,n){return this.content.map(e=>t.bind(n)(e.value,e.key,e))}compactMap(r,o){const s=[];return this.forEach((e,t,n)=>{e=r.bind(o)(e,t,n);e&&s.push(e)}),s}filter(e,t){return new a(this.content).filter(e,t)}reject(e,t){return this.filter(r(e),t)}forEach(t,n){return this.content.forEach(e=>t.bind(n)(e.value,e.key,e))}}},71167:(e,t,n)=>{n=n(10316);e.exports=class extends n{constructor(e,t,n){super(e,t,n),this.element="string"}primitive(){return"string"}get length(){return this.content.length}}},75147:(e,t,n)=>{n=n(85105);e.exports=class extends n{serialise(t){if(!(t instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${t}\` is not an Element instance`);let n;t._attributes&&t.attributes.get("variable")&&(n=t.attributes.get("variable"));const r={element:t.element};t._meta&&0{e.content&&e.content.element&&e.content.attributes.remove("typeAttributes")}),e.content&&0!==n.length&&o.unshift(e.content),(o=o.map(e=>e instanceof this.namespace.elements.Array?[e]:new this.namespace.elements.Array([e.content]))).length&&t.set("samples",o),0{const t=e.clone();return t.attributes.remove("typeAttributes"),this.serialise(t)})}if(e.content){const n=e.content.clone();return n.attributes.remove("typeAttributes"),[this.serialise(n)]}return[]}deserialise(t){if("string"==typeof t)return new this.namespace.elements.String(t);if("number"==typeof t)return new this.namespace.elements.Number(t);if("boolean"==typeof t)return new this.namespace.elements.Boolean(t);if(null===t)return new this.namespace.elements.Null;if(Array.isArray(t))return new this.namespace.elements.Array(t.map(this.deserialise,this));const r=this.namespace.getElementClass(t.element),o=new r,s=(o.element!==t.element&&(o.element=t.element),t.meta&&this.deserialiseObject(t.meta,o.meta),t.attributes&&this.deserialiseObject(t.attributes,o.attributes),this.deserialiseContent(t.content));if(void 0===s&&null!==o.content||(o.content=s),"enum"===o.element){o.content&&o.attributes.set("enumerations",o.content);let n=o.attributes.get("samples");if(o.attributes.remove("samples"),n){const s=n;n=new this.namespace.elements.Array,s.forEach(e=>{e.forEach(e=>{const t=new r(e);t.element=o.element,n.push(t)})});t=n.shift();o.content=t?t.content:void 0,o.attributes.set("samples",n)}else o.content=void 0;let e=o.attributes.get("default");if(e&&0this.shouldRefract(e)||"default"===t?this.serialise(e):"array"===e.element||"object"===e.element||"enum"===e.element?e.children.map(e=>this.serialise(e)):e.toValue()):"object"===e.element?(e.content||[]).map(this.serialise,this):e.toValue()}serialiseEnum(e){return e.children.map(e=>this.serialise(e))}serialiseObject(e){const n={};return e.forEach((e,t)=>{e&&(t=t.toValue(),n[t]=this.convertKeyToRefract(t,e))}),n}deserialiseObject(t,n){Object.keys(t).forEach(e=>{n.set(e,this.deserialise(t[e]))})}}},85105:e=>{e.exports=class{constructor(e){this.namespace=e||new this.Namespace}serialise(e){if(!(e instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${e}\` is not an Element instance`);const t={element:e.element};e._meta&&0{e&&(n[t.toValue()]=this.serialise(e))}),0!==Object.keys(n).length)return n}deserialiseObject(t,n){Object.keys(t).forEach(e=>{n.set(e,this.deserialise(t[e]))})}}},58859:(n,r,w)=>{var e="function"==typeof Map&&Map.prototype,t=Object.getOwnPropertyDescriptor&&e?Object.getOwnPropertyDescriptor(Map.prototype,"size"):null,E=e&&t&&"function"==typeof t.get?t.get:null,S=e&&Map.prototype.forEach,t="function"==typeof Set&&Set.prototype,e=Object.getOwnPropertyDescriptor&&t?Object.getOwnPropertyDescriptor(Set.prototype,"size"):null,x=t&&e&&"function"==typeof e.get?e.get:null,_=t&&Set.prototype.forEach,k="function"==typeof WeakMap&&WeakMap.prototype?WeakMap.prototype.has:null,A="function"==typeof WeakSet&&WeakSet.prototype?WeakSet.prototype.has:null,O="function"==typeof WeakRef&&WeakRef.prototype?WeakRef.prototype.deref:null,X=Boolean.prototype.valueOf,s=Object.prototype.toString,Q=Function.prototype.toString,Z=String.prototype.match,C=String.prototype.slice,j=String.prototype.replace,i=String.prototype.toUpperCase,P=String.prototype.toLowerCase,u=RegExp.prototype.test,N=Array.prototype.concat,I=Array.prototype.join,ee=Array.prototype.slice,o=Math.floor,T="function"==typeof BigInt?BigInt.prototype.valueOf:null,p=Object.getOwnPropertySymbols,R="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?Symbol.prototype.toString:null,M="function"==typeof Symbol&&"object"==typeof Symbol.iterator,D="function"==typeof Symbol&&Symbol.toStringTag&&(Symbol.toStringTag,1)?Symbol.toStringTag:null,F=Object.prototype.propertyIsEnumerable,B=("function"==typeof Reflect?Reflect:Object).getPrototypeOf||([].__proto__===Array.prototype?function(e){return e.__proto__}:null);function L(e,t){if(e===1/0||e===-1/0||e!=e||e&&-1e3 0, or `null`');if(K(i,"numericSeparator")&&"boolean"!=typeof i.numericSeparator)throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var t=i.numericSeparator;if(void 0===n)return"undefined";if(null===n)return"null";if("boolean"==typeof n)return n?"true":"false";if("string"==typeof n)return function e(t,n){{var r;if(t.length>n.maxStringLength)return r=t.length-n.maxStringLength,r="... "+r+" more character"+(1"}if(U(n)){if(0===n.length)return"[]";var b=Y(n,m);return a&&!function(e){for(var t=0;t "+m(e,n))}),oe("Map",E.call(n),u,a)):function(e){if(x&&e&&"object"==typeof e)try{x.call(e);try{E.call(e)}catch(e){return 1}return e instanceof Set}catch(e){}}(n)?(p=[],_&&_.call(n,function(e){p.push(m(e,n))}),oe("Set",x.call(n),p,a)):function(e){if(k&&e&&"object"==typeof e)try{k.call(e,k);try{A.call(e,A)}catch(e){return 1}return e instanceof WeakMap}catch(e){}}(n)?H("WeakMap"):function(e){if(A&&e&&"object"==typeof e)try{A.call(e,A);try{k.call(e,k)}catch(e){return 1}return e instanceof WeakSet}catch(e){}}(n)?H("WeakSet"):function(e){if(O&&e&&"object"==typeof e)try{return O.call(e),1}catch(e){}}(n)?H("WeakRef"):"[object Number]"!==W(h=n)||D&&"object"==typeof h&&D in h?function(e){if(e&&"object"==typeof e&&T)try{return T.call(e),1}catch(e){}}(n)?J(m(T.call(n))):"[object Boolean]"!==W(t=n)||D&&"object"==typeof t&&D in t?"[object String]"!==W(e=n)||D&&"object"==typeof e&&D in e?"undefined"!=typeof window&&n===window?"{ [object Window] }":n===w.g?"{ [object globalThis] }":("[object Date]"!==W(t=n)||D&&"object"==typeof t&&D in t)&&!V(n)?(e=Y(n,m),t=B?B(n)===Object.prototype:n instanceof Object||n.constructor===Object,d=n instanceof Object?"":"null prototype",f=!t&&D&&Object(n)===n&&D in n?C.call(W(n),8,-1):d?"Object":"",t=(!t&&"function"==typeof n.constructor&&n.constructor.name?n.constructor.name+" ":"")+(f||d?"["+I.call(N.call([],f||[],d||[]),": ")+"] ":""),0===e.length?t+"{}":a?t+"{"+G(e,a)+"}":t+"{ "+I.call(e,", ")+" }"):String(n):J(m(String(n))):J(X.call(n)):J(m(Number(n)))};var a=Object.prototype.hasOwnProperty||function(e){return e in this};function K(e,t){return a.call(e,t)}function W(e){return s.call(e)}function ne(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0,r=e.length;n{var n,r,e=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function i(t){if(n===setTimeout)return setTimeout(t,0);if((n===o||!n)&&setTimeout)return(n=setTimeout)(t,0);try{return n(t,0)}catch(e){try{return n.call(null,t,0)}catch(e){return n.call(this,t,0)}}}try{n="function"==typeof setTimeout?setTimeout:o}catch(e){n=o}try{r="function"==typeof clearTimeout?clearTimeout:s}catch(e){r=s}var a,l=[],c=!1,u=-1;function p(){c&&a&&(c=!1,a.length?l=a.concat(l):u=-1,l.length&&h())}function h(){if(!c){var e=i(p);c=!0;for(var t=l.length;t;){for(a=l,l=[];++u{"use strict";var i=n(6925);function r(){}function o(){}o.resetWarningCache=r,e.exports=function(){function e(e,t,n,r,o,s){if(s!==i)throw(s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")).name="Invariant Violation",s}function t(){return e}var n={array:e.isRequired=e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:r};return n.PropTypes=n}},5556:(e,t,n)=>{e.exports=n(2694)()},6925:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},74765:e=>{"use strict";var t=String.prototype.replace,n=/%20/g;e.exports={default:"RFC3986",formatters:{RFC1738:function(e){return t.call(e,n,"+")},RFC3986:function(e){return String(e)}},RFC1738:"RFC1738",RFC3986:"RFC3986"}},55373:(e,t,n)=>{"use strict";var r=n(98636),o=n(62642),n=n(74765);e.exports={formats:n,parse:o,stringify:r}},62642:(e,t,n)=>{"use strict";function y(e,t){return e&&"string"==typeof e&&t.comma&&-1{"use strict";function I(e,t){r.apply(e,D(t)?t:[t])}function T(e,t,n,r,o,s,i,a,l,c,u,p,h,d,f,m){for(var g=e,y=m,v=0,b=!1;void 0!==(y=y.get(L))&&!b;){var w=y.get(e);if(v+=1,void 0!==w){if(w===v)throw new RangeError("Cyclic object value");b=!0}void 0===y.get(L)&&(v=0)}if("function"==typeof a?g=a(t,g):g instanceof Date?g=u(g):"comma"===n&&D(g)&&(g=M.maybeMap(g,function(e){return e instanceof Date?u(e):e})),null===g){if(o)return i&&!d?i(t,B.encoder,f,"key",p):t;g=""}if("string"==typeof(E=g)||"number"==typeof E||"boolean"==typeof E||"symbol"==typeof E||"bigint"==typeof E||M.isBuffer(g)){if(i){var E=d?t:i(t,B.encoder,f,"key",p);if("comma"===n&&d){for(var S=F.call(String(g),","),x="",_=0;_{"use strict";function a(e,t){for(var n=t&&t.plainObjects?Object.create(null):{},r=0;r>6]+u[128|63&l]:l<55296||57344<=l?i+=u[224|l>>12]+u[128|l>>6&63]+u[128|63&l]:(a+=1,l=65536+((1023&l)<<10|1023&s.charCodeAt(a)),i+=u[240|l>>18]+u[128|l>>12&63]+u[128|l>>6&63]+u[128|63&l])}return i},isBuffer:function(e){return!(!e||"object"!=typeof e||!(e.constructor&&e.constructor.isBuffer&&e.constructor.isBuffer(e)))},isRegExp:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)},maybeMap:function(e,t){if(m(e)){for(var n=[],r=0;r{"use strict";var s=Object.prototype.hasOwnProperty;function i(e){try{return decodeURIComponent(e.replace(/\+/g," "))}catch(e){return null}}function a(e){try{return encodeURIComponent(e)}catch(e){return null}}t.stringify=function(e,t){var n,r,o=[];for(r in"string"!=typeof(t=t||"")&&(t="?"),e)if(s.call(e,r)){if((n=e[r])||null!=n&&!isNaN(n)||(n=""),r=a(r),n=a(n),null===r||null===n)continue;o.push(r+"="+n)}return o.length?t+o.join("&"):""},t.parse=function(e){for(var t=/([^=?#&]+)=?([^&]*)/g,n={};o=t.exec(e);){var r=i(o[1]),o=i(o[2]);null===r||null===o||r in n||(n[r]=o)}return n}},41859:(e,t,n)=>{const i=n(27096),a=n(78004),l=i.types;e.exports=class r{constructor(e,t){if(this._setDefaults(e),e instanceof RegExp)this.ignoreCase=e.ignoreCase,this.multiline=e.multiline,e=e.source;else{if("string"!=typeof e)throw new Error("Expected a regexp or string");this.ignoreCase=t&&-1!==t.indexOf("i"),this.multiline=t&&-1!==t.indexOf("m")}this.tokens=i(e)}_setDefaults(e){this.max=null!=e.max?e.max:null!=r.prototype.max?r.prototype.max:100,this.defaultRange=e.defaultRange||this.defaultRange.clone(),e.randInt&&(this.randInt=e.randInt)}gen(){return this._gen(this.tokens,[])}_gen(e,t){var n,r,o,s,i;switch(e.type){case l.ROOT:case l.GROUP:if(e.followedBy||e.notFollowedBy)return"";for(e.remember&&void 0===e.groupNumber&&(e.groupNumber=t.push(null)-1),r="",s=0,i=(n=e.options?this._randSelect(e.options):e.stack).length;s{"use strict";var o=n(65606),s=n(92861).Buffer,i=n.g.crypto||n.g.msCrypto;i&&i.getRandomValues?e.exports=function(e,t){if(4294967295{"use strict";function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.CopyToClipboard=void 0;var l=r(n(96540)),c=r(n(17965)),u=["text","onCopy","options","children"];function r(e){return e&&e.__esModule?e:{default:e}}function o(t,e){var n,r=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,n)),r}function p(t){for(var e=1;e{"use strict";n=n(25264).CopyToClipboard;n.CopyToClipboard=n,e.exports=n},81214:(e,t,n)=>{"use strict";function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.DebounceInput=void 0;var l=r(n(96540)),c=r(n(20181)),u=["element","onChange","value","minLength","debounceTimeout","forceNotifyByEnter","forceNotifyOnBlur","onKeyDown","onBlur","inputRef"];function r(e){return e&&e.__esModule?e:{default:e}}function o(t,e){var n,r=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,n)),r}function p(t){for(var e=1;e=r?o.notify(t):n.length>e.length&&o.notify(p(p({},t),{},{target:p(p({},t.target),{},{value:""})}))})}),g(f(o),"onKeyDown",function(e){"Enter"===e.key&&o.forceNotify(e);var t=o.props.onKeyDown;t&&(e.persist(),t(e))}),g(f(o),"onBlur",function(e){o.forceNotify(e);var t=o.props.onBlur;t&&(e.persist(),t(e))}),g(f(o),"createNotifier",function(e){var t;e<0?o.notify=function(){return null}:0===e?o.notify=o.doNotify:(t=(0,c.default)(function(e){o.isDebouncing=!1,o.doNotify(e)},e),o.notify=function(e){o.isDebouncing=!0,t(e)},o.flush=function(){return t.flush()},o.cancel=function(){o.isDebouncing=!1,t.cancel()})}),g(f(o),"doNotify",function(){o.props.onChange.apply(void 0,arguments)}),g(f(o),"forceNotify",function(e){var t,n=o.props.debounceTimeout;!o.isDebouncing&&0=t?o.doNotify(e):o.doNotify(p(p({},e),{},{target:p(p({},e.target),{},{value:n})})))}),o.isDebouncing=!1,o.state={value:void 0===e.value||null===e.value?"":e.value};e=o.props.debounceTimeout;return o.createNotifier(e),o}return t=i,(e=[{key:"componentDidUpdate",value:function(e){var t,n,r,o;this.isDebouncing||(t=(n=this.props).value,n=n.debounceTimeout,r=e.debounceTimeout,e=e.value,o=this.state.value,void 0!==t&&e!==t&&o!==t&&this.setState({value:t}),n!==r&&this.createNotifier(n))}},{key:"componentWillUnmount",value:function(){this.flush&&this.flush()}},{key:"render",value:function(){var e=this.props,t=e.element,n=(e.onChange,e.value,e.minLength,e.debounceTimeout,e.forceNotifyByEnter),r=e.forceNotifyOnBlur,o=e.onKeyDown,s=e.onBlur,i=e.inputRef,e=function(e,t){if(null==e)return{};var n,r=function(e,t){if(null==e)return{};for(var n,r={},o=Object.keys(e),s=0;s{"use strict";n=n(81214).DebounceInput;n.DebounceInput=n,e.exports=n},22551:(l,f,e)=>{"use strict";var O=e(96540),n=e(69982);function q(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n