diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md new file mode 100644 index 000000000..beb340ad6 --- /dev/null +++ b/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,67 @@ + + + +{{ if .Versions -}} + +# Unreleased + +{{ if .Unreleased.CommitGroups -}} +{{ range .Unreleased.CommitGroups -}} +## {{ .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 -}} +{{ end -}} + +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ range .CommitGroups -}} + +## {{ .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 +{{ range .RevertCommits -}} +* {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .MergeCommits -}} +## Pull Requests + +{{ range .MergeCommits -}} +* {{ .Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +## {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- if .Versions }} +[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD +{{ range .Versions -}} +{{ if .Tag.Previous -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} diff --git a/.chglog/config.yml b/.chglog/config.yml new file mode 100644 index 000000000..4b78ec16e --- /dev/null +++ b/.chglog/config.yml @@ -0,0 +1,37 @@ +style: github +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://github.com/aws-powertools/powertools-lambda-java +options: + commits: + filters: + Type: + - feat + - fix + - perf + - refactor + - docs + - chore + - revert + commit_groups: + title_maps: + feat: Features + fix: Bug Fixes + perf: Performance Improvements + refactor: Code Refactoring + docs: Documentation + chore: Maintenance + revert: Regression + header: + pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$" + pattern_maps: + - Type + - Scope + - Subject + notes: + keywords: + - BREAKING CHANGE + # issues: + # prefix: + # - # diff --git a/.github/DISCUSSION_TEMPLATE/rfcs.yml b/.github/DISCUSSION_TEMPLATE/rfcs.yml new file mode 100644 index 000000000..55909514f --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/rfcs.yml @@ -0,0 +1,107 @@ +title: "RFC: " +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 area does this RFC relate to? + options: + - Tracer + - Logger + - Metrics + - Parameters + - Large Messages + - Batch Processing + - Validation + - Idempotency + - Custom Resources + - Serialization + - 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 RFC meets [Powertools for AWS Lambda (Java) Tenets](https://docs.powertools.aws.dev/lambda/java/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [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**: After creating an RFC, please wait until it is reviewed and signed-off by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. + + Metadata information for admin purposes, please leave them empty. + + * RFC PR: + * Approved by: '' + * Reviewed by: '' + - type: input + id: notes + attributes: + label: Future readers + description: Please not edit this field + value: "Please react with 👍 and your use case to help us understand customer demand." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index abd8faa56..000000000 --- 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: '' - ---- - -<!--- Provide a general summary of the issue in the Title above --> -<!--- How has this issue affected you? What are you trying to accomplish? --> - -**What were you trying to accomplish?** - -## Expected Behavior -<!--- If you're describing a bug, tell us what should happen --> -<!--- If you're suggesting a change/improvement, tell us how it should work --> - -## Current Behavior -<!--- If describing a bug, tell us what happens instead of the expected behavior --> -<!--- If suggesting a change/improvement, explain the difference from current behavior --> - -## Possible Solution -<!--- Not obligatory, but suggest a fix/reason for the bug, --> -<!--- or ideas how to implement the addition or change --> - -## Steps to Reproduce (for bugs) -<!--- Provide a link to a live example, or an unambiguous set of steps to --> -<!--- reproduce this bug. Include code to reproduce, if relevant --> -1. -2. -3. -4. - -## Environment - -* **Powertools for AWS Lambda (Java) version used**: -* **Packaging format (Layers, Maven/Gradle)**: -* **AWS Lambda function runtime:** -* **Debugging logs** - -> [How to enable debug mode](https://docs.powertools.aws.dev/lambda-java/#debug-mode)** - -```text -# paste logs here -``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..2b0ae71e8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,82 @@ +name: Bug report +description: Report a reproducible bug to help us improve +title: "Bug: TITLE" +type: "Bug" +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://docs.powertools.aws.dev/lambda/java/processes/maintainers/#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: java + 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 (Java) version + placeholder: "latest, 2.0.1" + value: latest + validations: + required: true + - type: dropdown + id: runtime + attributes: + label: AWS Lambda function runtime + options: + - "Java 8" + - "Java 11" + - "Java 17" + - "Java 21" + - "provided.al2023" + validations: + required: true + - type: textarea + id: logs + attributes: + label: Debugging logs + description: If available, please share [debugging logs](https://docs.powertools.aws.dev/lambda/lambda/#debug-mode) + render: java + 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. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9eae2e167..01a8d495b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,4 +2,7 @@ blank_issues_enabled: false contact_links: - name: Ask a question url: https://github.com/aws-powertools/powertools-lambda-java/discussions/new - about: Ask a general question about Lambda Powertools + about: Ask a general question about Powertools for AWS Lambda + - name: Join Community Discord Server + url: https://discord.gg/B8zZKbbyET + about: "Check out the #java channel" diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index b837b7ad5..000000000 --- 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.** -<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] --> - -**Describe the solution you'd like** -<!-- A clear and concise description of what you want to happen. --> - -**Describe alternatives you've considered** -<!-- A clear and concise description of any alternative solutions or features you've considered. --> - -**Additional context** -<!-- Add any other context or screenshots about the feature request here. --> diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..6aaef0718 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,52 @@ +name: Feature request +description: Suggest an idea for Powertools for AWS Lambda +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 (Java) project. + - 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 (Java) Tenets](https://docs.powertools.aws.dev/lambda/java/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [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**: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. + - type: input + id: notes + attributes: + label: Future readers + description: Please not edit this field + value: "Please react with 👍 and your use case to help us understand customer demand." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml index 843f5103c..1a84ed7ef 100644 --- a/.github/ISSUE_TEMPLATE/maintenance.yml +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -57,11 +57,11 @@ body: options: - label: This request meets [Powertools for AWS Lambda (Java) Tenets](https://docs.powertools.aws.dev/lambda-java/#tenets) required: true - - label: Should this be considered in other Powertools for AWS Lambda (Java) languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/) + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/) 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. + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md deleted file mode 100644 index 84fce71df..000000000 --- a/.github/ISSUE_TEMPLATE/rfc.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -name: RFC -about: Feature design and proposals -title: 'RFC: ' -labels: RFC, triage -assignees: '' - ---- - -## Key information - -* RFC PR: (leave this empty) -* Related issue(s), if known: -* Area: (i.e. Tracer, Metrics, Logger, etc.) -* Meet [tenets](https://docs.powertools.aws.dev/lambda-java/#tenets): (Yes/no) - -## 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 for AWS Lambda (Java) to understand it, and for somebody familiar with the implementation to implement it. - -> This should get into specifics and corner-cases, and include examples of how the feature is used. Any new terminology should be defined here. - -## 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/share_your_work.yml b/.github/ISSUE_TEMPLATE/share_your_work.yml index 228ee8281..f0f879225 100644 --- a/.github/ISSUE_TEMPLATE/share_your_work.yml +++ b/.github/ISSUE_TEMPLATE/share_your_work.yml @@ -1,7 +1,7 @@ name: I Made This (showcase your work) description: Share what you did with Powertools for AWS Lambda (Java) 💞💞. Blog post, workshops, presentation, sample apps, etc. title: "[I Made This]: <TITLE>" -labels: ["community-content"] +labels: ["community-content", "triage"] body: - type: markdown attributes: @@ -53,4 +53,4 @@ body: label: Acknowledgment options: - label: I understand this content may be removed from Powertools for AWS Lambda (Java) documentation if it doesn't conform with the [Code of Conduct](https://aws.github.io/code-of-conduct) - required: true + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/support_powertools.yml b/.github/ISSUE_TEMPLATE/support_powertools.yml index 2b66d830d..9067d47ec 100644 --- a/.github/ISSUE_TEMPLATE/support_powertools.yml +++ b/.github/ISSUE_TEMPLATE/support_powertools.yml @@ -1,7 +1,7 @@ name: Support Powertools for AWS Lambda (Java) (become a reference) description: Add your organization's name or logo to the Powertools for AWS Lambda (Java) documentation title: "[Support Powertools for AWS Lambda (Java)]: <your organization name>" -labels: ["customer_reference"] +labels: ["customer-reference", "triage"] body: - type: markdown attributes: @@ -48,9 +48,9 @@ body: - type: checkboxes id: other_languages attributes: - label: Also using other Powertools for AWS Lambda (Java) languages? + label: Also using other Powertools for AWS Lambda languages? options: - - label: Python + - label: Java required: false - label: TypeScript required: false @@ -59,6 +59,6 @@ body: - type: markdown attributes: value: | - *By raising a Support Powertools for AWS Lambda (Java) 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.* + *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.* \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/tech_debt.yml b/.github/ISSUE_TEMPLATE/tech_debt.yml new file mode 100644 index 000000000..56cd4b8c7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tech_debt.yml @@ -0,0 +1,60 @@ +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 + - 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. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [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. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d4b6d17ff..f30703bb4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,25 +1,28 @@ -**Issue #, if available:** +## Summary -## Description of changes: +### Changes -<!--- One or two sentences as a summary of what's being changed --> +> Please provide a summary of what's being changed -**Checklist** +<!-- What is this PR solving? Write a clear description or reference the issue(s) it addresses. --> -<!--- Leave unchecked if your change doesn't seem to apply --> +> Please add the issue number below, if no issue is present the PR might get blocked and not be reviewed -* [ ] [Meet tenets criteria](https://docs.powertools.aws.dev/lambda-java/#tenets) -* [ ] Update tests -* [ ] Update docs -* [ ] PR title follows [conventional commit semantics](https://www.conventionalcommits.org/en/v1.0.0/) +**Issue number:** -## Breaking change checklist +<!------- +Before creating the pull request, please make sure you do the following: -<!--- Ignore if it's not a breaking change --> +- Read the Contributing Guidelines at https://github.com/aws-powertools/powertools-lambda-java/blob/main/CONTRIBUTING.md#sending-a-pull-request +- Check that there isn't already a PR that addresses the same issue. If you find a duplicate, please leave a comment under the existing PR so we can discuss how to move forward +- Check that the change meets the project's tenets https://docs.powertools.aws.dev/lambda/java/latest/#tenets +- Add a PR title that follows the conventional commit semantics - https://www.conventionalcommits.org/en/v1.0.0/ +- If relevant, add tests that prove that the change is effective and works +- Whenever relevant, make sure to comment functions/methods/types and make appropriate changes to the documentation +-------> -**RFC issue #**: - -* [ ] Migration process documented -* [ ] Implement warnings (if it can live side by side) +--- 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. \ No newline at end of file diff --git a/.github/actions/gradle/action.yml b/.github/actions/gradle/action.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.github/actions/restore/action.yml b/.github/actions/restore/action.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.github/actions/seal/action.yml b/.github/actions/seal/action.yml new file mode 100644 index 000000000..079496c8c --- /dev/null +++ b/.github/actions/seal/action.yml @@ -0,0 +1,78 @@ +name: Seal and hash source code +description: | + Seals and creates a SHA256SUM of an artifact for storage + + Process: + 1. Create a unique name based on environment details + 2. Compress work directory or specified path + 3. Hash compressed file + 4. Upload archive using `actions/upload-artifact` + + Usage: + ```yml + - id: seal + name: Seal + uses: .github/actions/seal + with: + prefix: foo + ``` + +inputs: + prefix: + description: Prefix to use when exporting artifact + required: true +outputs: + hash: + description: SHA256SUM hash of compressed files + value: ${{ steps.hash.outputs.hash }} + artifact_name: + description: Artifact name + value: ${{ steps.artifact_name.outputs.artifact_name }} + +runs: + using: composite + steps: + - id: adjust_path + name: Adjust path + shell: bash + run: echo "${{ github.action_path }}" >> $GITHUB_PATH + + - id: artifact_name + name: Export final artifact name + env: + GITHUB_RUN_ID: ${{ github.run_id }} + ARTIFACT_PREFIX: ${{ inputs.prefix }} + shell: bash + run: | + echo "artifact_name=${ARTIFACT_PREFIX}-${GITHUB_RUN_ID}" >> "$GITHUB_OUTPUT" + + - id: compress + name: Create tarball for entire source + env: + ARTIFACT_NAME: ${{ steps.artifact_name.outputs.artifact_name }} + shell: bash + run: | + tar --exclude-vcs -cvf "${ARTIFACT_NAME}".tar * + + - id: hash + name: Hash + env: + ARTIFACT_NAME: ${{ steps.artifact_name.outputs.artifact_name }} + shell: bash + run: | + echo "hash=$(openssl dgst -sha256 -binary "${{ ARTIFACT_NAME }}".tar | openssl enc -base64)" >> "$GITHUB_OUTPUT" + + - name: Upload artifacts + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + with: + if-no-files-found: error + name: ${{ steps.artifact_name.outputs.artifact_name }} + path: ${{ steps.artifact_name.outputs.artifact_name }}.tar + retention-days: 1 + + - name: Remove archive + env: + ARTIFACT_NAME: ${{ steps.artifact_name.outputs.artifact_name }} + shell: bash + run: | + rm -f "${ARTIFACT_NAME}.tar" \ No newline at end of file diff --git a/.github/actions/version/action.yml b/.github/actions/version/action.yml new file mode 100644 index 000000000..f0f0516ee --- /dev/null +++ b/.github/actions/version/action.yml @@ -0,0 +1,53 @@ +name: Version Java Project +description: | + Versions the maven project using an input + + Process: + 1. Grab current version from project.version variable from maven + 2. Set new version using maven-versions-plugin + + Usage: + ```yml + - id: version + name: version + uses: .github/actions/version + with: + new_version: 1.20.0 + snapshot: 'false' + ``` + +inputs: + new_version: + description: New package version, expressed as SemVer (1.x.y) + required: true + snapshot: + description: New version is a SNAPSHOT release + required: true + default: 'false' + +outputs: + old_version: + description: Current version of project + value: ${{ steps.current_version.outputs.current_version}} + +runs: + using: composite + steps: + - id: current_version + name: Get current version + shell: bash + run: | + echo "current_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT + + - id: replace_version + name: Replace current version + shell: bash + run: | + mvn versions:set -DnewVersion=${{ inputs.new_version }} -DprocessAllModules=true -DallowSnapshots=true + + - id: asset_version + name: Replace version for assets + if: ${{ inputs.snapshot == 'false' }} + shell: bash + run: | + grep "${{ steps.current_version.outputs.current_version }}" -r . --include build.gradle --include build.gradle.kts --include mkdocs.yml --include README.md -l | xargs sed -i 's#${{ steps.current_version.outputs.current_version }}#${{ inputs.new_version }}#' \ No newline at end of file diff --git a/.github/auto_assign-issues.yml b/.github/auto_assign-issues.yml deleted file mode 100644 index fb160ed94..000000000 --- a/.github/auto_assign-issues.yml +++ /dev/null @@ -1,9 +0,0 @@ -addAssignees: true - -# The list of users to assign to new issues. -# If empty or not provided, the repository owner is assigned -assignees: - - scottgerring - - jeromevdl - - mriccia - - msailes diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4832b1c49..caa9934ca 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,5 +1,22 @@ version: 2 updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: chore + + - package-ecosystem: docker + directories: + - "/powertools-e2e-tests/src/test/resources/docker" + - "/docs" + - "/examples/**" + schedule: + interval: daily + commit-message: + prefix: chore + - package-ecosystem: "maven" directory: "/" schedule: @@ -7,7 +24,3 @@ updates: labels: - "maven" - "dependencies" - ignore: - # Ignore Mockito 5.X.X as it does not support Java 8 - - dependency-name: "org.mockito:mockito-*" - update-types: ["version-update:semver-major"] \ No newline at end of file diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml new file mode 100644 index 000000000..2ea218503 --- /dev/null +++ b/.github/dependency-review-config.yml @@ -0,0 +1,32 @@ +allow-licenses: + - 'Apache-1.1' + - 'Apache-2.0' + - 'ISC' + - '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' + # TT: D290816995 + - 'UPL-1.0' +comment-summary-in-pr: on-failure diff --git a/.github/pmd-ruleset.xml b/.github/pmd-ruleset.xml new file mode 100644 index 000000000..1bc5f3020 --- /dev/null +++ b/.github/pmd-ruleset.xml @@ -0,0 +1,647 @@ +<?xml version="1.0"?> +<ruleset name="dogfood7" + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd"> + <description>Rules to check Powertools for Lambda</description> + <!-- + Originally copied from: https://github.com/pmd/build-tools/blob/main/src/main/resources/net/sourceforge/pmd/pmd-dogfood-config.xml + --> + + <!-- + Note: Eventually, this ruleset should include all rules and exclude those, + which we know are explicitly decided as not applicable to PMD itself. + This is the most encompassing form of RuleSet. + + For starting, we add rule by rule and use an incremental approach. + See [#361](https://github.com/pmd/pmd/issues/361). + + The original dogfood ruleset is available at: + https://github.com/pmd/pmd/blob/f9ef2c8c4bc23f1349597b827c8f072b7cade8a5/pmd-core/src/main/resources/rulesets/internal/dogfood.xml + --> + + <!-- <rule ref="category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AccessorClassGeneration" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AccessorMethodGeneration" /> --> + <!-- <rule ref="category/java/bestpractices.xml/ArrayIsStoredDirectly" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AvoidPrintStackTrace" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AvoidReassigningParameters" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AvoidStringBufferField" /> --> + <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/CheckResultSet"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/ConstantsInInterface"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitch"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach" /> --> + <!-- <rule ref="category/java/bestpractices.xml/GuardLogStatement" /> --> + <!-- <rule ref="category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldUseAfterAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldUseBeforeAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldUseTestAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestAssertionsShouldIncludeMessage" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestContainsTooManyAsserts" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldIncludeAssert" /> --> + <!-- <rule ref="category/java/bestpractices.xml/JUnitUseExpected" /> --> + <rule ref="category/java/bestpractices.xml/LooseCoupling"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray" /> --> + <rule ref="category/java/bestpractices.xml/MissingOverride"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/PreserveStackTrace" /> --> + <rule ref="category/java/bestpractices.xml/PrimitiveWrapperInstantiation"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator" /> --> + <!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap" /> --> + <!-- <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList" /> --> + <!-- <rule ref="category/java/bestpractices.xml/SimplifiableTestAssertion" /> --> + <rule ref="category/java/bestpractices.xml/NonExhaustiveSwitch"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/SystemPrintln" /> --> + <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/UnusedImports" /> --> + <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/UnusedPrivateField"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/UseVarargs" /> --> + + <!-- <rule ref="category/java/codestyle.xml/AtLeastOneConstructor" /> --> + <!-- <rule ref="category/java/codestyle.xml/AvoidDollarSigns" /> --> + <!-- <rule ref="category/java/codestyle.xml/AvoidFinalLocalVariable" /> --> + <rule ref="category/java/codestyle.xml/AvoidProtectedFieldInFinalClass"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/AvoidProtectedMethodInFinalClassNotExtending"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/AvoidUsingNativeCode"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/BooleanGetMethodName" /> --> + <!-- <rule ref="category/java/codestyle.xml/CallSuperInConstructor" /> --> + <!-- <rule ref="category/java/codestyle.xml/ClassNamingConventions" /> --> + <!-- <rule ref="category/java/codestyle.xml/CommentDefaultAccessModifier" /> --> + <!-- <rule ref="category/java/codestyle.xml/ConfusingTernary" /> --> + <rule ref="category/java/codestyle.xml/ControlStatementBraces"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/DefaultPackage" /> --> + <!-- <rule ref="category/java/codestyle.xml/DontImportJavaLang" /> --> + <!-- <rule ref="category/java/codestyle.xml/DuplicateImports" /> --> + <!-- <rule ref="category/java/codestyle.xml/EmptyMethodInAbstractClassShouldBeAbstract" /> --> + <rule ref="category/java/codestyle.xml/ExtendsObject"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/FieldDeclarationsShouldBeAtStartOfClass" /> --> + <!-- <rule ref="category/java/codestyle.xml/FieldNamingConventions" /> --> + <rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/codestyle.xml/GenericsNaming" /> --> + <rule ref="category/java/codestyle.xml/IdenticalCatchBranches"> + <priority>3</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/LinguisticNaming" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalHomeNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalInterfaceSessionNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalVariableNamingConventions" /> --> + <!-- <rule ref="category/java/codestyle.xml/LongVariable" /> --> + <!-- <rule ref="category/java/codestyle.xml/MDBAndSessionBeanNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal" /> --> + <!-- <rule ref="category/java/codestyle.xml/MethodNamingConventions" /> --> + <!-- <rule ref="category/java/codestyle.xml/NoPackage" /> --> + <!-- <rule ref="category/java/codestyle.xml/OnlyOneReturn" /> --> + <!-- <rule ref="category/java/codestyle.xml/PackageCase" /> --> + <!-- <rule ref="category/java/codestyle.xml/PrematureDeclaration" /> --> + <!-- <rule ref="category/java/codestyle.xml/RemoteInterfaceNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/RemoteSessionInterfaceNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/ShortClassName" /> --> + <!-- <rule ref="category/java/codestyle.xml/ShortMethodName" /> --> + <!-- <rule ref="category/java/codestyle.xml/ShortVariable" /> --> + <!-- <rule ref="category/java/codestyle.xml/TooManyStaticImports" /> --> + <rule ref="category/java/codestyle.xml/UnnecessaryAnnotationValueElement"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UnnecessaryConstructor"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName" /> --> + <rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UnnecessaryModifier"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UnnecessaryReturn"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UseDiamondOperator"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UselessParentheses"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UselessQualifiedThis"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/design.xml/AbstractClassWithoutAnyMethod" /> --> + <!-- <rule ref="category/java/design.xml/AvoidCatchingGenericException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidDeeplyNestedIfStmts" /> --> + <!-- <rule ref="category/java/design.xml/AvoidRethrowingException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidThrowingNewInstanceOfSameException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidThrowingNullPointerException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidThrowingRawExceptionTypes" /> --> + <rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/CollapsibleIfStatements" /> --> + <!-- <rule ref="category/java/design.xml/CouplingBetweenObjects" /> --> + <!-- <rule ref="category/java/design.xml/CyclomaticComplexity" /> --> + <!-- <rule ref="category/java/design.xml/DataClass" /> --> + <!-- <rule ref="category/java/design.xml/DoNotExtendJavaLangError" /> --> + <!-- <rule ref="category/java/design.xml/ExceptionAsFlowControl" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveClassLength" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveImports" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveMethodLength" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveParameterList" /> --> + <!-- <rule ref="category/java/design.xml/ExcessivePublicCount" /> --> + <rule ref="category/java/design.xml/FinalFieldCouldBeStatic"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/GodClass" /> --> + <!-- <rule ref="category/java/design.xml/ImmutableField" /> --> + <!-- <rule ref="category/java/design.xml/LawOfDemeter" /> --> + <rule ref="category/java/design.xml/LogicInversion"> + <priority>1</priority> + </rule> + <!-- Don't let the Language specific APIs leak out --> + <!-- + <rule ref="category/java/design.xml/LoosePackageCoupling"> + <properties> + <property name="packages"><value>net.sourceforge.pmd.lang,net.sourceforge.pmd.lang.java,net.sourceforge.pmd.lang.jsp,net.sourceforge.pmd.lang.ecmascript,net.sourceforge.pmd.lang.cpp</value></property> + <property name="classes"> + <value>net.sourceforge.pmd.lang.Language,net.sourceforge.pmd.lang.LanguageVersion,net.sourceforge.pmd.lang.LanguageVersionDiscoverer,net.sourceforge.pmd.lang.LanguageVersionHandler,net.sourceforge.pmd.lang.Parser,net.sourceforge.pmd.lang.ast.Node</value> + </property> + </properties> + </rule> + --> + <!-- <rule ref="category/java/design.xml/ModifiedCyclomaticComplexity" /> --> + <!-- <rule ref="category/java/design.xml/NcssConstructorCount" /> --> + <!-- <rule ref="category/java/design.xml/NcssCount" /> --> + <!-- <rule ref="category/java/design.xml/NcssMethodCount" /> --> + <!-- <rule ref="category/java/design.xml/NcssTypeCount" /> --> + <!-- <rule ref="category/java/design.xml/NPathComplexity" /> --> + <!-- <rule ref="category/java/design.xml/SignatureDeclareThrowsException" /> --> + <rule ref="category/java/design.xml/SimplifiedTernary"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/SimplifyBooleanExpressions" /> --> + <rule ref="category/java/design.xml/SimplifyBooleanReturns"> + <priority>1</priority> + </rule> + <rule ref="category/java/design.xml/SimplifyConditional"> + <priority>1</priority> + </rule> + <rule ref="category/java/design.xml/SingularField"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/StdCyclomaticComplexity" /> --> + <!-- <rule ref="category/java/design.xml/SwitchDensity" /> --> + <!-- <rule ref="category/java/design.xml/TooManyFields" /> --> + <!-- <rule ref="category/java/design.xml/TooManyMethods" /> --> + <rule ref="category/java/design.xml/UselessOverridingMethod"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/UseObjectForClearerAPI" /> --> + <rule ref="category/java/design.xml/UseUtilityClass"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/documentation.xml/CommentContent" /> --> + <!-- <rule ref="category/java/documentation.xml/CommentRequired" /> --> + <!-- <rule ref="category/java/documentation.xml/CommentSize" /> --> + <rule ref="category/java/documentation.xml/UncommentedEmptyConstructor"> + <priority>1</priority> + </rule> + <rule ref="category/java/documentation.xml/UncommentedEmptyMethodBody"> + <priority>1</priority> + </rule> + + <rule ref="category/java/errorprone.xml/AssignmentInOperand"> + <priority>1</priority> + <properties> + <property name="allowWhile" value="true"/> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidAccessibilityAlteration" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidAssertAsIdentifier" /> --> + <rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidCallingFinalize" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidCatchingNPE" /> --> + <rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingMethodName" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingTypeName" /> --> + <rule ref="category/java/errorprone.xml/AvoidInstanceofChecksInCatchClause"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidLiteralsInIfCondition" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidLosingExceptionInformation" /> --> + <rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/AvoidUsingOctalValues"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/BeanMembersShouldSerialize" /> --> + <rule ref="category/java/errorprone.xml/BrokenNullCheck"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/CallSuperFirst" /> --> + <!-- <rule ref="category/java/errorprone.xml/CallSuperLast" /> --> + <rule ref="category/java/errorprone.xml/CheckSkipResult"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloneMethodMustBePublic"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloneMethodMustImplementCloneable"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloneMethodReturnTypeMustMatchClassName"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloseResource"> + <priority>1</priority> + <properties> + <property name="closeTargets" value="close,IOUtils.closeQuietly,IOUtil.closeQuietly" /> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/CompareObjectsWithEquals"> + <priority>1</priority> + <properties> + <property name="typesThatCompareByReference" value="java.lang.Enum,java.lang.Class,net.sourceforge.pmd.lang.ast.Node,net.sourceforge.pmd.lang.ast.GenericToken"/> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/ComparisonWithNaN"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod" /> --> + <!-- <rule ref="category/java/errorprone.xml/DataflowAnomalyAnalysis" /> --> + <rule ref="category/java/errorprone.xml/DoNotCallGarbageCollectionExplicitly"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/DoNotCallSystemExit" /> --> + <!-- <rule ref="category/java/errorprone.xml/DoNotExtendJavaLangThrowable" /> --> + <!-- <rule ref="category/java/errorprone.xml/DoNotHardCodeSDCard" /> --> + <!-- <rule ref="category/java/errorprone.xml/DoNotThrowExceptionInFinally" /> --> + <rule ref="category/java/errorprone.xml/DontImportSun"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/EmptyCatchBlock"> + <priority>1</priority> + <properties> + <property name="allowCommentedBlocks" value="true" /> + </properties> + </rule> + <!-- <rule ref="category/java/errorprone.xml/EmptyFinalizer" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyFinallyBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyIfStmt" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyInitializer" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyStatementBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptySwitchStatements" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptySynchronizedBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyTryBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyWhileStmt" /> --> + <rule ref="category/java/errorprone.xml/EqualsNull"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/FinalizeDoesNotCallSuperFinalize" /> --> + <!-- <rule ref="category/java/errorprone.xml/FinalizeOnlyCallsSuperFinalize" /> --> + <!-- <rule ref="category/java/errorprone.xml/FinalizeOverloaded" /> --> + <!-- <rule ref="category/java/errorprone.xml/FinalizeShouldBeProtected" /> --> + <rule ref="category/java/errorprone.xml/IdempotentOperations"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/ImplicitSwitchFallThrough"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ImportFromSamePackage" /> --> + <rule ref="category/java/errorprone.xml/InstantiationToGetClass"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/InvalidSlf4jMessageFormat" /> --> + <rule ref="category/java/errorprone.xml/JumbledIncrementer"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/JUnitSpelling" /> --> + <!-- <rule ref="category/java/errorprone.xml/JUnitStaticSuite" /> --> + <!-- <rule ref="category/java/errorprone.xml/LoggerIsNotStaticFinal" /> --> + <!-- <rule ref="category/java/errorprone.xml/MethodWithSameNameAsEnclosingClass" /> --> + <rule ref="category/java/errorprone.xml/MisplacedNullCheck"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/MissingSerialVersionUID" /> --> + <rule ref="category/java/errorprone.xml/MissingStaticMethodInNonInstantiatableClass"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/MoreThanOneLogger" /> --> + <rule ref="category/java/errorprone.xml/NonCaseLabelInSwitch"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/NonStaticInitializer"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/NullAssignment" /> --> + <rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/ProperCloneImplementation"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ProperLogger" /> --> + <rule ref="category/java/errorprone.xml/ReturnEmptyCollectionRatherThanNull"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ReturnEmptyCollectionRatherThanNull" /> --> + <rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/SimpleDateFormatNeedsLocale" /> --> + <rule ref="category/java/errorprone.xml/SingleMethodSingleton"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/SingletonClassReturningNewInstance"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/StaticEJBFieldShouldBeFinal" /> --> + <!-- <rule ref="category/java/errorprone.xml/StringBufferInstantiationWithChar" /> --> + <!-- <rule ref="category/java/errorprone.xml/SuspiciousEqualsMethodName" /> --> + <!-- <rule ref="category/java/errorprone.xml/SuspiciousHashcodeMethodName" /> --> + <rule ref="category/java/errorprone.xml/SuspiciousOctalEscape"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/TestClassWithoutTestCases" /> --> + <rule ref="category/java/errorprone.xml/UnconditionalIfStatement"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/UnnecessaryBooleanAssertion" /> --> + <!-- <rule ref="category/java/errorprone.xml/UnnecessaryCaseChange" /> --> + <rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/UseCorrectExceptionLogging" /> --> + <!-- <rule ref="category/java/errorprone.xml/UseEqualsToCompareStrings" /> --> + <rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/UseLocaleWithCaseConversions"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/UseProperClassLoader" /> --> + + <!-- <rule ref="category/java/multithreading.xml/AvoidSynchronizedAtMethodLevel" /> --> + <rule ref="category/java/multithreading.xml/AvoidThreadGroup"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/AvoidUsingVolatile"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/multithreading.xml/DoNotUseThreads" /> --> + <rule ref="category/java/multithreading.xml/DontCallThreadRun"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/DoubleCheckedLocking"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/UnsynchronizedStaticFormatter"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/multithreading.xml/UseConcurrentHashMap" /> --> + <rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/performance.xml/AddEmptyString" /> --> + <!-- <rule ref="category/java/performance.xml/AppendCharacterWithChar" /> --> + <!-- <rule ref="category/java/performance.xml/AvoidArrayLoops" /> --> + <rule ref="category/java/performance.xml/AvoidFileStream"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/AvoidInstantiatingObjectsInLoops" /> --> + <rule ref="category/java/performance.xml/BigIntegerInstantiation"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse" /> --> + <!-- <rule ref="category/java/performance.xml/ConsecutiveLiteralAppends" /> --> + <!-- <rule ref="category/java/performance.xml/InefficientEmptyStringCheck" /> --> + <!-- <rule ref="category/java/performance.xml/InefficientStringBuffering" /> --> + <!-- <rule ref="category/java/performance.xml/InsufficientStringBufferDeclaration" /> --> + <rule ref="category/java/performance.xml/OptimizableToArrayCall"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/RedundantFieldInitializer" /> --> + <!-- <rule ref="category/java/performance.xml/SimplifyStartsWith" /> --> + <!-- <rule ref="category/java/performance.xml/StringInstantiation" /> --> + <!-- <rule ref="category/java/performance.xml/StringToString" /> --> + <rule ref="category/java/performance.xml/TooFewBranchesForSwitch"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation" /> --> + <!-- <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector" /> --> + <!-- <rule ref="category/java/performance.xml/UseArraysAsList" /> --> + <!-- <rule ref="category/java/performance.xml/UseIndexOfChar" /> --> + <!-- <rule ref="category/java/performance.xml/UselessStringValueOf" /> --> + <!-- <rule ref="category/java/performance.xml/UseStringBufferForStringAppends" /> --> + <!-- <rule ref="category/java/performance.xml/UseStringBufferLength" /> --> + + <!-- <rule ref="category/java/security.xml/InsecureCryptoIv" /> --> + + + <!-- Note: These are the custom rule ported to PMD 7 --> + + <!-- PMD specific custom rules --> + <rule name="UseInstanceofToCompareClasses" + language="java" + since="5.0" + message="replace o.getClass().equals(MyClass.class) with o instanceof MyClass" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>replace o.getClass().equals(MyClass.class) with o instanceof MyClass. Make sure MyClass doesn't have descendants</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//MethodCall[pmd-java:matchesSig("_#equals(java.lang.Object)")] + [MethodCall[pmd-java:matchesSig("_#getClass()")]] + [ArgumentList/ClassLiteral] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="ReversedUseInstanceofToCompareClasses" + language="java" + since="5.0" + message="replace MyClass.class.equals(o.getClass()) with o instanceof MyClass" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>replace MyClass.class.equals(o.getClass()) with o instanceof MyClass. Make sure MyClass doesn't have descendants</description> + <priority>3</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//MethodCall[pmd-java:matchesSig("_#equals(java.lang.Object)")] + [ClassLiteral] + [ArgumentList/MethodCall[pmd-java:matchesSig("_#getClass()")]] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="DontCallSuperVisitWhenUsingRuleChain" + language="java" + message="Don't call super.visit() when using the rulechain" + typeResolution="true" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Calling super.visit breaks the rulechain, by starting a full visitor run from the passed node downwards. Add all needed nodes to the rulechain instead.</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +( +(: java rule chain rule :) +//ClassDeclaration[pmd-java:typeIs('net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule')] +| +(: generic rule that implements buildTargetSelector :) +//ClassDeclaration[pmd-java:typeIs('net.sourceforge.pmd.lang.rule.AbstractRule')] + [*/MethodDeclaration[@Name = 'buildTargetSelector']] +) + (: but calling super.visit! :) + [*/MethodDeclaration//MethodCall[SuperExpression][@MethodName = 'visit']] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="AlwaysCallSuperWhenNotUsingRuleChain" + language="java" + message="Always call super.visit() when not using rulechain" + typeResolution="true" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Just returning without calling super stops visiting of nested nodes like inner classes.</description> + <priority>3</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//ClassDeclaration + [ExtendsList/ClassType[pmd-java:typeIs('net.sourceforge.pmd.lang.rule.AbstractRule')]] + [not(ExtendsList/ClassType[pmd-java:typeIs('net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule')])] + [not(*/MethodDeclaration[@Name = 'buildTargetSelector'])] + [*/MethodDeclaration[@Name = 'visit'][not(.//MethodCall[@MethodName = 'visit']/SuperExpression)]] +]]> + </value> + </property> + </properties> + </rule> + + <!-- Idea from https://github.com/pmd/pmd/pull/3609#discussion_r748292071 --> + <rule name="ReuseInvocationMatcher" + language="java" + message="Reuse InvocationMatcher" + typeResolution="true" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Share the invocation matcher and not create a new one every time</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//MethodCall[pmd-java:matchesSig("net.sourceforge.pmd.lang.java.types.InvocationMatcher#matchesCall(_)")] + [MethodCall[pmd-java:matchesSig("net.sourceforge.pmd.lang.java.types.InvocationMatcher#parse(java.lang.String)")]] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="DoNotUseJavaUtilLogging" + language="java" + since="7.0.0" + message="Use slf4j: LoggerFactory.getLogger(MyClass.class)" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Use slf4j: LoggerFactory.getLogger(MyClass.class)</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//ClassDeclaration[//ImportDeclaration[@ImportedName = 'java.util.logging.Logger']] +]]> + </value> + </property> + </properties> + </rule> +</ruleset> diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml deleted file mode 100644 index 21b5e20e5..000000000 --- a/.github/workflows/auto-merge.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Auto merge if dependabot PR - -on: - workflow_run: - workflows: ["Build"] - types: [completed] - -permissions: - pull-requests: write - issues: write - repository-projects: write - contents: write - -jobs: - merge-me: - name: Merge me! - runs-on: ubuntu-latest - if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' && github.actor == 'dependabot[bot]' - steps: - - uses: actions/checkout@v3 - - uses: ahmadnassri/action-workflow-run-wait@v1 - with: - timeout: 300000 - - name: 'Download artifact' - uses: actions/github-script@v3.1.0 - with: - script: | - var artifacts = await github.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{github.event.workflow_run.id }}, - }); - var matchArtifact = artifacts.data.artifacts.filter((artifact) => { - return artifact.name == "pr" - })[0]; - var download = await github.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - var fs = require('fs'); - fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - - run: unzip pr.zip - - name: Create review - uses: actions/github-script@v3 - with: - script: | - var fs = require('fs'); - var issue_number = Number(fs.readFileSync('./NR')); - - github.pulls.createReview({ - owner: context.payload.repository.owner.login, - repo: context.payload.repository.name, - pull_number: issue_number, - event: 'APPROVE' - }) - - github.pulls.merge({ - owner: context.payload.repository.owner.login, - repo: context.payload.repository.name, - pull_number: issue_number, - merge_method: 'squash' - }) - - github-token: ${{ secrets.AUTOMERGE }} diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index f4a9c4d3f..a94ace711 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,38 +1,88 @@ -name: Build Docs +# Build Latest Docs +# +# Description: +# Builds the latest docs and stores them in S3 to be served by our docs platform +# +# The workflow allows us to build to the main location (/lambda/java/) and to an alias +# (i.e. /lambda/java/preview/) if needed +# +# Triggers: +# - workflow_dispatch +# +# Inputs: +# alias – subdirectory to store the docs in for previews or in progress work on: - pull_request: - branches: - - main - - v2 - paths: - - 'docs/**' - - 'mkdocs.yml' - - 'Makefile' + workflow_dispatch: + inputs: + version: + description: "Version to build and publish docs (1.28.0, develop)" + required: true + type: string - push: - branches: - - main - paths: - - 'docs/**' - - 'mkdocs.yml' - - 'Makefile' +name: Build Latest Docs +run-name: Build Latest Docs - ${{ inputs.version }} jobs: docs: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: Docs steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: - python-version: "3.8" - - name: Capture branch and tag - id: branch_name + fetch-depth: 0 + - name: Build run: | - echo "SOURCE_BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV - echo "SOURCE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - - name: Build docs website + mkdir -p dist + docker build -t squidfunk/mkdocs-material ./docs/ + docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: "latest" + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.VERSION }}/ + - name: Deploy Docs (Alias) + env: + VERSION: ${{ inputs.version }} + ALIAS: "latest" + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.ALIAS }}/ + - name: Deploy Docs (Version JSON) + env: + VERSION: ${{ inputs.version }} + ALIAS: "latest" + # 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: | - echo "GIT_PYTHON_REFRESH=quiet" - make build-docs-website \ No newline at end of file + aws s3 cp \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/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-java/versions.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 9ed871a6c..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Build - -on: - pull_request: - branches: - - main - - v2 - paths: - - 'powertools-cloudformation/**' - - 'powertools-core/**' - - 'powertools-serialization/**' - - 'powertools-logging/**' - - 'powertools-sqs/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'powertools-idempotency/**' - - 'powertools-parameters/**' - - 'powertools-metrics/**' - - 'powertools-test-suite/**' - - 'powertools-e2e-tests/**' - - 'examples/**' - - 'pom.xml' - - 'examples/pom.xml' - - '.github/workflows/**' - push: - branches: - - main - paths: - - 'powertools-cloudformation/**' - - 'powertools-core/**' - - 'powertools-serialization/**' - - 'powertools-logging/**' - - 'powertools-sqs/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'powertools-idempotency/**' - - 'powertools-parameters/**' - - 'powertools-metrics/**' - - 'powertools-test-suite/**' - - 'powertools-e2e-tests/**' - - 'examples/**' - - 'pom.xml' - - 'examples/pom.xml' - - '.github/workflows/**' -jobs: - build-corretto: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - java: [8, 11, 15, 16, 17, 18, 19, 20 ] - name: Java ${{ matrix.java }} - env: - JAVA: ${{ matrix.java }} - AWS_REGION: eu-west-1 - steps: - - uses: actions/checkout@v3 - - name: Setup java - uses: actions/setup-java@v3 - with: - distribution: 'corretto' - java-version: ${{ matrix.java }} - cache: 'maven' - - name: Build with Maven - run: mvn -B install --file pom.xml - - name: Upload coverage to Codecov - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # 3.1.1 - if: ${{ matrix.java == '11' }} # publish results once - with: - files: ./powertools-cloudformation/target/site/jacoco/jacoco.xml,./powertools-core/target/site/jacoco/jacoco.xml,./powertools-idempotency/target/site/jacoco/jacoco.xml,./powertools-logging/target/site/jacoco/jacoco.xml,./powertools-metrics/target/site/jacoco/jacoco.xml,./powertools-parameters/target/site/jacoco/jacoco.xml,./powertools-serialization/target/site/jacoco/jacoco.xml,./powertools-sqs/target/site/jacoco/jacoco.xml,./powertools-tracing/target/site/jacoco/jacoco.xml,./powertools-validation/target/site/jacoco/jacoco.xml - savepr: - runs-on: ubuntu-latest - name: Save PR number if running on PR by dependabot - if: github.actor == 'dependabot[bot]' - steps: - - name: Create Directory and save issue - run: | - mkdir -p ./pr - echo ${{ github.event.number }} - echo ${{ github.event.number }} > ./pr/NR - - uses: actions/upload-artifact@v2 - name: Upload artifact - with: - name: pr - path: pr/ diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml new file mode 100644 index 000000000..339d6fab8 --- /dev/null +++ b/.github/workflows/check-build.yml @@ -0,0 +1,147 @@ +# Check Build +# +# Description: +# Runs the build for every java version we support +# +# Triggers: +# - pull_request: when a PR is sent to us +# - push: when code is pushed to a specified branch +# +# Notes: +# Builds against Java 11, 17, and 21 which are the supported versions. + +on: + workflow_dispatch: + pull_request: + paths: + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-kafka/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'examples/**' + - 'pom.xml' + - 'examples/pom.xml' + - '.github/workflows/**' + push: + branches: + - main + paths: + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-kafka/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'pom.xml' + - 'examples/**' + - 'examples/pom.xml' + - '.github/workflows/**' + +name: Build +permissions: + contents: read +run-name: Build - ${{ github.event_name }} + +jobs: + java-build: + runs-on: ubuntu-latest + strategy: + matrix: + java: + - 11 + - 17 + - 21 + - 25 + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: ${{ matrix.java }} + cache: maven + - id: build-maven + name: Build (Maven) + run: | + mvn -B -q install --file pom.xml + + graalvm-build: + runs-on: aws-powertools_ubuntu-latest_8-core + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 + with: + files: | + powertools-*/** + pom.xml + - name: Setup GraalVM + uses: graalvm/setup-graalvm@790e28947b79a9c09c3391c0f18bf8d0f102ed69 # v1.4.4 + with: + java-version: "21" + distribution: "graalvm" + cache: maven + - id: graalvm-native-test + name: GraalVM Native Test + if: steps.changed-files.outputs.any_changed == 'true' + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + # Build the entire project first to ensure test-jar dependencies are available + echo "::group::Building project dependencies" + mvn -B -q install -DskipTests + echo "::endgroup::" + + echo "Changes detected in powertools modules: $CHANGED_FILES" + + # Find modules with graalvm-native profile and run tests + find . -name "pom.xml" -path "./powertools-*" | while read module; do + if grep -q "<id>graalvm-native</id>" "$module"; then + module_dir=$(dirname "$module") + module_name=$(basename "$module_dir") + + # Check if this specific module or common dependencies changed + if echo "$CHANGED_FILES" | grep -q "$module_name/" || \ + echo " $CHANGED_FILES " | grep -q " pom.xml " || \ + echo "$CHANGED_FILES" | grep -q "powertools-common/"; then + echo "::group::Building $module_name with GraalVM" + echo "Changes detected in $module_name - running GraalVM tests" + echo "Regenerating GraalVM metadata for $module_dir" + mvn -B -q -f "$module" -Pgenerate-graalvm-files clean test + echo "Running GraalVM native tests for $module_dir" + mvn -B -q -f "$module" -Pgraalvm-native test + echo "::endgroup::" + else + echo "No changes detected in $module_name - skipping GraalVM tests" + fi + fi + done diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml new file mode 100644 index 000000000..378d48a60 --- /dev/null +++ b/.github/workflows/check-e2e.yml @@ -0,0 +1,106 @@ +# Run E2E tests for a branch +# +# Description: +# Runs E2E tests for a specified branch +# +# Triggers: +# - push +# +# Secrets: +# - E2E.AWS_IAM_ROLE + +on: + workflow_dispatch: + + push: + branches: + - main + paths: # add other modules when there are under e2e tests + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'pom.xml' + +name: E2E Tests +run-name: E2E Tests - ${{ github.event_name }} + +permissions: + contents: read + +jobs: + e2e: + name: End-to-end Tests (Java ${{ matrix.java }}) + runs-on: ubuntu-latest + permissions: + id-token: write + environment: E2E + strategy: + fail-fast: false + max-parallel: 4 + matrix: + java: + - 11 + - 17 + - 21 + - 25 + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: maven + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + - name: Run e2e test with Maven + env: + JAVA_VERSION: ${{ matrix.java }} + run: mvn -DskipTests -ntp install --file pom.xml && mvn -Pe2e -B -ntp verify --file powertools-e2e-tests/pom.xml + + e2e-graal: + name: End-to-end GraalVM Tests (Java ${{ matrix.java }}) + runs-on: ubuntu-latest + permissions: + id-token: write + environment: E2E + strategy: + fail-fast: false + max-parallel: 1 + matrix: + java: + - 25 + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: maven + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + - name: Run e2e-graal test with Maven + env: + JAVA_VERSION: ${{ matrix.java }} + run: mvn -DskipTests -ntp install --file pom.xml && mvn -Pe2e-graal -B -ntp verify --file powertools-e2e-tests/pom.xml diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml new file mode 100644 index 000000000..7e7dce429 --- /dev/null +++ b/.github/workflows/check-pmd.yml @@ -0,0 +1,42 @@ +# Runs PMD for a Pull Request +# +# Description: +# Runs PMD (pmd.github.io) for a pull request and daily. +# This does not error on failure yet, our rules are too strong and would fail on every run +# +# Triggers: +# - pull_request +# - workflow_dispatch +# - cron: every day at 12:00PM + +on: + pull_request: + workflow_dispatch: + schedule: + - cron: '0 12 * * *' # Run daily at 12:00 UTC + +name: PMD +run-name: PMD - ${{ github.event_name }} + +permissions: + contents: read + +jobs: + pmd_analyse: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + java-version: 21 + distribution: corretto + cache: maven + - uses: pmd/pmd-github-action@d9c1f3c5940cbf5923f1354e83fa858b4496ebaa # v2.0.0 + with: + rulesets: '.github/pmd-ruleset.xml' + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml new file mode 100644 index 000000000..c5c8197f9 --- /dev/null +++ b/.github/workflows/check-spotbugs.yml @@ -0,0 +1,51 @@ +# Check for Spotbug errors +# +# Description: +# Runs Spotbugs for a pull request. +# This does not error on failure yet, our rules are too strong and would fail on every run +# +# Triggers: +# - pull_request +on: + pull_request: + branches: + - main + paths: + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-kafka/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'powertools-test-suite/**' + - 'pom.xml' + - '.github/workflows/**' + +name: SpotBugs +run-name: SpotBugs + +permissions: + contents: read + +jobs: + codecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'corretto' + java-version: 21 + - name: Build with Maven for spotbugs check to mark build as fail if voilations found + run: mvn -Pbuild-with-spotbugs -B install --file pom.xml -DskipTests -Dmaven.javadoc.skip=true -Dspotbugs.failOnError=true diff --git a/.github/workflows/dispatch_analytics.yml b/.github/workflows/dispatch_analytics.yml deleted file mode 100644 index 49a276f6f..000000000 --- a/.github/workflows/dispatch_analytics.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Dispatch analytics - -on: - workflow_dispatch: - - schedule: - - cron: '0 * * * *' - -permissions: - id-token: write - actions: read - checks: read - contents: read - deployments: read - issues: read - discussions: read - packages: read - pages: read - pull-requests: read - repository-projects: read - security-events: read - statuses: read - -jobs: - dispatch_token: - concurrency: - group: analytics - runs-on: ubuntu-latest - environment: analytics - steps: - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef - with: - aws-region: eu-central-1 - role-to-assume: ${{ secrets.AWS_ANALYTICS_ROLE_ARN }} - - - name: Invoke Lambda function - run: | - payload=$(echo -n '{"githubToken": "${{ secrets.GITHUB_TOKEN }}"}' | base64) - aws lambda invoke \ - --function-name ${{ secrets.AWS_ANALYTICS_DISPATCHER_ARN }} \ - --payload "$payload" response.json - cat response.json diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 9c09e294d..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Docs - -on: - release: - types: - - published - workflow_dispatch: {} - -permissions: - id-token: write - contents: write - pages: write - -jobs: - docs: - runs-on: ubuntu-latest - environment: Docs - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.8" - - name: Capture branch and tag - id: branch_name - run: | - echo "SOURCE_BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV - echo "SOURCE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - - name: Build docs website - run: | - make build-docs-website - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef - with: - aws-region: us-east-1 - role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} - - name: Deploy Docs - run: | - aws s3 sync \ - dist \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/ diff --git a/.github/workflows/post_release.js b/.github/workflows/post_release.js deleted file mode 100644 index 648236421..000000000 --- a/.github/workflows/post_release.js +++ /dev/null @@ -1,112 +0,0 @@ -const STAGED_LABEL = "status/staged-next-release"; - -/** - * Fetch issues using GitHub REST API - * - * @param {object} gh_client - Pre-authenticated REST client (Octokit) - * @param {string} org - GitHub Organization - * @param {string} repository - GitHub repository - * @param {string} state - GitHub issue state (open, closed) - * @param {string} label - Comma-separated issue labels to fetch - * @return {Object[]} issues - Array of issues matching params - * @see {@link https://octokit.github.io/rest.js/v18#usage|Octokit client} - */ -const fetchIssues = async ({ - gh_client, - org, - repository, - state = "open", - label = STAGED_LABEL, - }) => { - - try { - const { data: issues } = await gh_client.rest.issues.listForRepo({ - owner: org, - repo: repository, - state: state, - labels: label, - }); - - return issues; - - } catch (error) { - console.error(error); - throw new Error("Failed to fetch issues") - } - -}; - -/** - * Notify new release and close staged GitHub issue - * - * @param {object} gh_client - Pre-authenticated REST client (Octokit) - * @param {string} owner - GitHub Organization - * @param {string} repository - GitHub repository - * @param {string} release_version - GitHub Release version - * @see {@link https://octokit.github.io/rest.js/v18#usage|Octokit client} - */ -const notifyRelease = async ({ - gh_client, - owner, - repository, - release_version, - }) => { - const release_url = `https://github.com/${owner}/${repository}/releases/tag/v${release_version}`; - - const issues = await fetchIssues({ - gh_client: gh_client, - org: owner, - repository: repository, - }); - - issues.forEach(async (issue) => { - console.info(`Updating issue number ${issue.number}`); - - const comment = `This is now released under [${release_version}](${release_url}) version!`; - try { - await gh_client.rest.issues.createComment({ - owner: owner, - repo: repository, - body: comment, - issue_number: issue.number, - }); - } catch (error) { - console.error(error); - throw new Error(`Failed to update issue ${issue.number} about ${release_version} release`) - } - - - // Close issue and remove staged label; keep existing ones - const labels = issue.labels - .filter((label) => label.name != STAGED_LABEL) - .map((label) => label.name); - - try { - await gh_client.rest.issues.update({ - repo: repository, - owner: owner, - issue_number: issue.number, - state: "closed", - labels: labels, - }); - } catch (error) { - console.error(error); - throw new Error("Failed to close issue") - } - - console.info(`Issue number ${issue.number} closed and updated`); - }); -}; - -// 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`); - - await notifyRelease({ - gh_client: github, - owner: context.repo.owner, - repository: context.repo.repo, - release_version: RELEASE_TAG_VERSION, - }); -}; \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 2068c09c5..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Publish package to the Maven Central Repository -on: - release: - types: - - published - workflow_dispatch: {} -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Maven Central Repository - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: 8 - server-id: ossrh - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} # Value of the GPG private key to import - gpg-passphrase: GPG_PASSPHRASE # env variable for GPG private key passphrase - - 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: Publish package - run: mvn -Prelease clean deploy -DskipTests - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - 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}) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 5b27fd671..39d453ced 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -1,15 +1,27 @@ -name: Release Drafter +# Generates release notes +# +# Description: +# Generates release notes based on pull request history. This is based on the config +# stored in .github/release-drafter.yml +# +# Triggers: +# - push: main on: push: - # branches to consider in the event; optional, defaults to all - branches: - - main + branches: [ main ] + +name: Release Drafter +run-name: Release Drafter jobs: - update_release_draft: - runs-on: ubuntu-latest + update_release: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write steps: - - uses: release-drafter/release-drafter@v5 + - name: Relase Drafter + uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml deleted file mode 100644 index 345bd2a10..000000000 --- a/.github/workflows/release-prep.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Prepare for maven central release -on: - workflow_dispatch: - inputs: - targetRelease: - description: 'Release number to upgrade to. For example X.X.X. Follow Semantic Versioning when deciding on next version.' - required: true - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Get current date - id: date - run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - - name: Set current release version env variable - run: | - echo "CURRENT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV - - name: Find and Replace ${{ env.CURRENT_VERSION }} with ${{ github.event.inputs.targetRelease }} in mkdocs.yml - uses: jacobtomlinson/gha-find-replace@v2 - with: - find: 'version: ${{ env.CURRENT_VERSION }}' - replace: 'version: ${{ github.event.inputs.targetRelease }}' - regex: false - include: "mkdocs.yml" - - name: Find and Replace ${{ env.CURRENT_VERSION }} with ${{ github.event.inputs.targetRelease }} in main pom.xml - uses: jacobtomlinson/gha-find-replace@v2 - with: - find: ${{ env.CURRENT_VERSION }} - replace: ${{ github.event.inputs.targetRelease }} - regex: false - include: "pom.xml" - - name: Find and Replace ${{ env.CURRENT_VERSION }} with ${{ github.event.inputs.targetRelease }} in modules pom.xml - uses: jacobtomlinson/gha-find-replace@v2 - with: - find: ${{ env.CURRENT_VERSION }} - replace: ${{ github.event.inputs.targetRelease }} - regex: false - include: "**/*pom.xml" - - name: Find and Replace ${{ env.CURRENT_VERSION }} with ${{ github.event.inputs.targetRelease }} in build.gradle - uses: jacobtomlinson/gha-find-replace@v2 - with: - find: ${{ env.CURRENT_VERSION }} - replace: ${{ github.event.inputs.targetRelease }} - regex: false - include: "**/*build.gradle" - - name: Find and Replace ${{ env.CURRENT_VERSION }} with ${{ github.event.inputs.targetRelease }} in README.md - uses: jacobtomlinson/gha-find-replace@v2 - with: - find: ${{ env.CURRENT_VERSION }} - replace: ${{ github.event.inputs.targetRelease }} - regex: false - include: "README.md" - - name: Create changelog placeholder for ${{ github.event.inputs.targetRelease }} - uses: jacobtomlinson/gha-find-replace@v2 - with: - find: '## [Unreleased]' - replace: | - ## [Unreleased] - - ## [${{ github.event.inputs.targetRelease }}] - ${{ steps.date.outputs.date }} - - <PLEASE REMEBER TO UPDATE CHANGE LOG> - - regex: false - include: CHANGELOG.md - - name: Create Release Pull Request - uses: peter-evans/create-pull-request@v3 - with: - commit-message: chore:prep release ${{ github.event.inputs.targetRelease }} - token: ${{ secrets.RELEASE }} - signoff: false - branch: prep-release-${{ github.event.inputs.targetRelease }} - delete-branch: true - title: chore:Prep release ${{ github.event.inputs.targetRelease }} - body: | - This is automated release prep. Remember to update [CHANGELOG.md](https://github.com/aws-powertools/powertools-lambda-java/blob/prep-release-${{ github.event.inputs.targetRelease }}/CHANGELOG.md) to capture changes in this release. Please review changes carefully before merging. - - * [ ] Updated CHANGELOG.md \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..630b91321 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,329 @@ +# Release +# +# Description: +# Creates a release for the project +# +# 1. Runs a setup job to set needed variables (build_matrix & version) +# 2. Versions to the project and stores as an artifact +# 3. Run quality checks +# 4. Build +# 5. Publish to Maven Central +# 6. Create PR +# 7. Publish docs +# +# Inputs: +# - version (string): SemVer of the new release (X.Y.Z) +# - snapshot (bool): If it's a snapshot release, this skips versioning assets like docs +# - skip_checks (bool): Don't run quality checks if it's an emergency release +# - skip_publish (bool): Don't publish to maven central +# - continue_on_error (bool): Don't fail the workflow if a quality check fails +# +# Triggers: +# - workflow_dispatch +# +# Secrets: +# - RELEASE.GPG_SIGNING_KEY +# - RELEASE.OSSRH_JIRA_USERNAME +# - RELEASE.OSSRH_JIRA_PASSWORD +# - RELEASE.GPG_PASSPHRASE +# - DOCS.AWS_DOCS_ROLE_ARN +# - DOCS.AWS_DOCS_BUCKET + +on: + workflow_dispatch: + inputs: + version: + type: string + description: Semver version to release + snapshot: + type: boolean + description: Create snapshot release + default: false + skip_checks: + type: boolean + description: Skip quality checks + default: false + skip_publish: + type: boolean + description: Skip publish to Maven Central + default: false + continue_on_error: + type: boolean + description: Continue to build if there's an error in quality checks + default: false + +name: Release +run-name: Release – ${{ inputs.version }} + +permissions: + contents: read + +env: + RELEASE_COMMIT: ${{ github.sha }} + RELEASE_TAG_VERSION: ${{ inputs.version }} + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + version: ${{ format('{0}{1}', steps.version_release.outputs.version, steps.version_snapshot.outputs.version) }} + build_matrix: ${{ format('{0}{1}', steps.build_matrix_v1.outputs.build_matrix, steps.build_matrix_v2.outputs.build_matrix) }} + steps: + - id: version_snapshot + if: ${{ inputs.snapshot }} + name: Version + run: | + echo version="$(grep -q "SNAPSHOT" <<< "${{ inputs.version }}" && echo "${{ inputs.version }}" || echo "${{ inputs.version }}-SNAPSHOT")" >> "$GITHUB_OUTPUT" + - id: version_release + if: ${{ !inputs.snapshot }} + name: Version + run: | + echo version="${{ inputs.version }}" >> "$GITHUB_OUTPUT" + - id: base + name: Base + run: | + echo build_version=$(test ${{ github.ref_name }} == "main" && echo "v2" || echo "v1") >> $GITHUB_OUTPUT + - id: build_matrix_v1 + name: Build matrix (v1) + if: ${{ steps.base.outputs.build_version == 'v1' }} + run: | + echo build_matrix='["8", "11", "17", "21"]' >> "$GITHUB_OUTPUT" + - id: build_matrix_v2 + name: Build matrix (v2) + if: ${{ steps.base.outputs.build_version == 'v2' }} + run: | + echo build_matrix='["11", "17", "21"]'>> "$GITHUB_OUTPUT" + + version_seal: + runs-on: ubuntu-latest + needs: + - setup + outputs: + source_hash: ${{ steps.upload_source.outputs.artifact-digest }} + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - id: version + name: version + uses: ./.github/actions/version + with: + new_version: ${{ needs.setup.outputs.version }} + snapshot: ${{ inputs.snapshot}} + - id: upload_source + name: Upload artifacts + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + if-no-files-found: error + name: source + path: | + * + !.git/* + include-hidden-files: true + retention-days: 1 + + quality: + runs-on: aws-powertools_ubuntu-latest_8-core + needs: + - version_seal + if: ${{ inputs.skip_checks == false }} + permissions: + contents: write + id-token: write + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: 21 + cache: maven + # non-exhuastive, but gives a fair indication if the final build will succeed, tests will run when we build later + - name: Run unit tests + run: mvn -B test --file pom.xml + continue-on-error: ${{ inputs.continue_on_error }} + - name: Run Spotbugs + run: mvn -Pbuild-with-spotbugs -B install --file pom.xml -DskipTests -Dmaven.javadoc.skip=true -Dspotbugs.failOnError=true + continue-on-error: ${{ inputs.continue_on_error }} + - uses: pmd/pmd-github-action@d9c1f3c5940cbf5923f1354e83fa858b4496ebaa # v2.0.0 + with: + rulesets: '.github/pmd-ruleset.xml' + token: ${{ secrets.GITHUB_TOKEN }} + uploadSarifReport: false + + build: + runs-on: aws-powertools_ubuntu-latest_8-core + needs: + - setup + - quality + - version_seal + if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + strategy: + matrix: + java: ${{ fromJson(needs.setup.outputs.build_matrix) }} + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: ${{ matrix.java }} + cache: maven + - id: build-maven + name: Build (Maven) + run: | + mvn -B install --file pom.xml + + publish: + runs-on: aws-powertools_ubuntu-latest_8-core + if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' && inputs.skip_publish == false && always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + needs: + - build + environment: Release + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: 21 + cache: maven + gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} + gpg-passphrase: GPG_PASSPHRASE + server-id: central + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Publish package + run: mvn -Prelease clean deploy -DskipTests + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + + create_pr: + runs-on: ubuntu-latest + if: ${{ inputs.snapshot == false && always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + needs: + - build + - publish + permissions: + pull-requests: write + contents: write + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + ref: ${{ env.RELEASE_COMMIT }} + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - id: setup-git + name: Git client setup and refresh tip + run: | + git config user.name "Powertools for AWS Lambda (Java) Bot" + git config user.email "151832416+aws-powertools-bot@users.noreply.github.com" + git config pull.rebase true + git config remote.origin.url >&- + - id: tag + name: Create tag + run: | + git tag -a v${{ inputs.version }} -m "Release v${{ inputs.version }}" + git push origin v${{ inputs.version }} + - id: branch + name: Create branch and update change log + run: | + git checkout -b ci-${{ github.run_id }} + docker run -v "${PWD}":/workdir quay.io/git-chglog/git-chglog@sha256:c791b1e8264387690cce4ce32e18b4f59ca3ffd8d55cb4093dc6de74529493f4 > CHANGELOG.md + git commit -am "chore(ci): bump version to ${{ inputs.version }}" + git push origin ci-${{ github.run_id }} + - id: create_pr + name: Create PR + run: | + gh pr create \ + --title "chore(ci): bump version to ${{ inputs.version }}" \ + --body "This is an automated PR created from the following workflow: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + docs: + runs-on: ubuntu-latest + if: ${{ inputs.snapshot == false }} + needs: + - create_pr + permissions: + contents: read + id-token: write + environment: Docs + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + # Checkout PR branch to make sure we build the version-bumped docs + ref: ci-${{ github.run_id }} + - name: Build + run: | + mkdir -p dist + docker build -t squidfunk/mkdocs-material ./docs/ + docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: 'latest' + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.VERSION }}/ + - name: Deploy Docs (Alias) + env: + VERSION: ${{ inputs.version }} + ALIAS: 'latest' + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.ALIAS }}/ + - name: Deploy Docs (Version JSON) + env: + VERSION: ${{ inputs.version }} + ALIAS: 'latest' + # 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-java/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-java/versions.json diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml deleted file mode 100644 index 36c9dd97a..000000000 --- a/.github/workflows/run-e2e-tests.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Run end-to-end tests - -on: - workflow_dispatch: - - push: - branches: - - main - - v2 - paths: # add other modules when there are under e2e tests - - 'powertools-e2e-tests/**' - - 'powertools-core/**' - - 'powertools-serialization/**' - - 'powertools-logging/**' - - 'powertools-tracing/**' - - 'powertools-idempotency/**' - - 'powertools-parameters/**' - - 'powertools-metrics/**' - - 'powertools-large-messages/**' - - 'pom.xml' - - '.github/workflows/**' - - pull_request: - branches: - - main - paths: - - 'powertools-e2e-tests/**' - -jobs: - e2e: - runs-on: ubuntu-latest - strategy: - max-parallel: 3 - matrix: - java: [ 8, 11, 17 ] - name: End-to-end tests java${{ matrix.java }} - env: - JAVA_VERSION: ${{ matrix.java }} - AWS_DEFAULT_REGION: eu-west-1 - permissions: - id-token: write # needed to interact with GitHub's OIDC Token endpoint. - contents: read - steps: - - uses: actions/checkout@v3 - - name: Setup java - uses: actions/setup-java@v3 - with: - distribution: 'corretto' - java-version: ${{ matrix.java }} - cache: maven - - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@v1.6.1 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} - aws-region: ${{ env.AWS_DEFAULT_REGION }} - - name: Run e2e test with Maven - run: mvn -DskipTests install --file pom.xml && mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml \ No newline at end of file diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml new file mode 100644 index 000000000..6729fd304 --- /dev/null +++ b/.github/workflows/security-dependencies-check.yml @@ -0,0 +1,31 @@ +# Dependency checks +# +# Description: +# Verifies that dependencies are compatible with our project +# by checking licenses and their security posture +# +# Triggers: +# - pull_request + +on: + pull_request: + +name: Verify Dependencies +run-name: Verify Dependencies – ${{ github.event_name }} + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Verify Contents + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 + with: + config-file: './.github/dependency-review-config.yml' diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml new file mode 100644 index 000000000..b91e78c69 --- /dev/null +++ b/.github/workflows/security-scorecard.yml @@ -0,0 +1,57 @@ +# Runs OSSF +# +# Description: +# Runs OpenSSF Scorecard scan on the project +# +# Triggers: +# - branch_protection_rule +# - cron: 09:00AM +# - push +# - workflow_dispatch +# +# Secrets: +# - Security.SCORECARD_TOKEN + +on: + branch_protection_rule: + schedule: + - cron: "0 9 * * *" + push: + branches: [main] + workflow_dispatch: {} + +name: OpenSSF Scorecard +run-name: OpenSSF Scorecard + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + environment: Security + permissions: + security-events: write + id-token: write + steps: + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: Run Analysis + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + repo_token: ${{ secrets.SCORECARD_TOKEN }} + - name: Upload Results + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + - name: Upload to Code-Scanning + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5 + with: + sarif_file: results.sarif diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml deleted file mode 100644 index 0b07bcd81..000000000 --- a/.github/workflows/spotbugs.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: SpotBugs - -on: - pull_request: - branches: - - main - - v2 - paths: - - 'powertools-cloudformation/**' - - 'powertools-core/**' - - 'powertools-serialization/**' - - 'powertools-logging/**' - - 'powertools-sqs/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'powertools-parameters/**' - - 'powertools-idempotency/**' - - 'powertools-metrics/**' - - 'powertools-test-suite/**' - - 'pom.xml' - - '.github/workflows/**' -jobs: - codecheck: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup java JDK 1.8 - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: 8 - # https://github.com/jwgmeligmeyling/spotbugs-github-action/issues/6 - # https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/ - # Avoid complexity of git action with publishing report. Just build with spotbugs profile. -# - name: Build with Maven for spotbugs check to gather reports -# run: mvn -Pbuild-with-spotbugs -B install --file pom.xml -DskipTests -Dmaven.javadoc.skip=true -Dspotbugs.failOnError=false -# - uses: jwgmeligmeyling/spotbugs-github-action@master -# with: -# path: '**/spotbugsXml.xml' -# # Can be simplified post this issue is fixed https://github.com/jwgmeligmeyling/spotbugs-github-action/issues/9 - - name: Build with Maven for spotbugs check to mark build as fail if voilations found - run: mvn -Pbuild-with-spotbugs -B install --file pom.xml -DskipTests -Dmaven.javadoc.skip=true -Dspotbugs.failOnError=true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 04e85211d..eb2ea4f18 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,6 @@ native-libs/ classes/ out/ - ###################### # Eclipse ###################### @@ -99,6 +98,7 @@ Desktop.ini docs/node_modules docs/.cache +.cache docs/public /example/.aws-sam/ /example/HelloWorldFunction/.aws-sam/ @@ -107,3 +107,14 @@ example/HelloWorldFunction/.gradle example/HelloWorldFunction/build /example/.gradle/ /example/.java-version +.gradle +build/ +.terraform* +terraform.tfstate* + +# LLMs +.kiro/ +.claude/ +.amazonq/ +.github/instructions + diff --git a/.mvn/README b/.mvn/README new file mode 100644 index 000000000..a851f5e55 --- /dev/null +++ b/.mvn/README @@ -0,0 +1,2 @@ +This is here purely so that we can get the root directory using maven.multiModuleProjectDirectory + diff --git a/CHANGELOG.md b/CHANGELOG.md index 5619f0bbb..a07df5d3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,249 +1,1115 @@ -# Changelog +<!-- changelog is partially generated, so it doesn't follow headings and required structure, so we disable it. --> +<!-- markdownlint-disable --> + +<a name="unreleased"></a> +# Unreleased + +## Documentation + +* **logger:** Fix logging environment variables names in documentation ([#2161](https://github.com/aws-powertools/powertools-lambda-java/issues/2161)) + +## Features + +* add CRaC priming support to powertools-kafka module ([#2145](https://github.com/aws-powertools/powertools-lambda-java/issues/2145)) +* **metrics:** introduce Metrics.flushMetrics ([#2154](https://github.com/aws-powertools/powertools-lambda-java/issues/2154)) + +## Maintenance + +* bump aws.sdk.version from 2.35.6 to 2.35.7 ([#2190](https://github.com/aws-powertools/powertools-lambda-java/issues/2190)) +* bump com.networknt:json-schema-validator from 1.5.8 to 1.5.9 ([#2189](https://github.com/aws-powertools/powertools-lambda-java/issues/2189)) +* bump sam/build-java21 ([#2195](https://github.com/aws-powertools/powertools-lambda-java/issues/2195)) +* bump squidfunk/mkdocs-material in /docs ([#2194](https://github.com/aws-powertools/powertools-lambda-java/issues/2194)) +* bump com.github.spotbugs:spotbugs-maven-plugin ([#2192](https://github.com/aws-powertools/powertools-lambda-java/issues/2192)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.214.0 to 2.220.0 ([#2191](https://github.com/aws-powertools/powertools-lambda-java/issues/2191)) +* bump io.github.ascopes:protobuf-maven-plugin ([#2193](https://github.com/aws-powertools/powertools-lambda-java/issues/2193)) +* bump aws.xray.recorder.version from 2.19.0 to 2.20.0 ([#2185](https://github.com/aws-powertools/powertools-lambda-java/issues/2185)) +* bump aws.sdk.version from 2.33.2 to 2.33.5 ([#2132](https://github.com/aws-powertools/powertools-lambda-java/issues/2132)) +* bump org.apache.maven.plugins:maven-javadoc-plugin ([#2186](https://github.com/aws-powertools/powertools-lambda-java/issues/2186)) +* bump org.assertj:assertj-core from 3.27.4 to 3.27.6 ([#2184](https://github.com/aws-powertools/powertools-lambda-java/issues/2184)) +* bump aws.sdk.version from 2.34.9 to 2.35.6 ([#2183](https://github.com/aws-powertools/powertools-lambda-java/issues/2183)) +* bump actions/dependency-review-action from 4.8.0 to 4.8.1 ([#2180](https://github.com/aws-powertools/powertools-lambda-java/issues/2180)) +* bump github/codeql-action from 3.30.5 to 4.30.8 ([#2179](https://github.com/aws-powertools/powertools-lambda-java/issues/2179)) +* bump aws-actions/configure-aws-credentials from 5.0.0 to 5.1.0 ([#2177](https://github.com/aws-powertools/powertools-lambda-java/issues/2177)) +* bump com.google.protobuf:protobuf-java from 4.32.0 to 4.32.1 ([#2175](https://github.com/aws-powertools/powertools-lambda-java/issues/2175)) +* bump aws.sdk.version from 2.34.5 to 2.34.9 ([#2174](https://github.com/aws-powertools/powertools-lambda-java/issues/2174)) +* bump org.apache.commons:commons-lang3 from 3.18.0 to 3.19.0 ([#2172](https://github.com/aws-powertools/powertools-lambda-java/issues/2172)) +* bump org.apache.maven.plugins:maven-artifact-plugin ([#2171](https://github.com/aws-powertools/powertools-lambda-java/issues/2171)) +* Add User-Agent execution interceptors ([#2166](https://github.com/aws-powertools/powertools-lambda-java/issues/2166)) +* bump org.apache.kafka:kafka-clients from 4.0.0 to 4.1.0 ([#2134](https://github.com/aws-powertools/powertools-lambda-java/issues/2134)) +* bump graalvm/setup-graalvm from 1.3.6 to 1.4.1 ([#2168](https://github.com/aws-powertools/powertools-lambda-java/issues/2168)) +* bump ossf/scorecard-action from 2.4.2 to 2.4.3 ([#2165](https://github.com/aws-powertools/powertools-lambda-java/issues/2165)) +* bump squidfunk/mkdocs-material in /docs ([#2164](https://github.com/aws-powertools/powertools-lambda-java/issues/2164)) +* bump log4j.version from 2.25.1 to 2.25.2 ([#2160](https://github.com/aws-powertools/powertools-lambda-java/issues/2160)) +* bump org.apache.maven.plugins:maven-failsafe-plugin ([#2159](https://github.com/aws-powertools/powertools-lambda-java/issues/2159)) +* bump actions/dependency-review-action from 4.7.3 to 4.8.0 ([#2158](https://github.com/aws-powertools/powertools-lambda-java/issues/2158)) +* bump github/codeql-action from 3.30.1 to 3.30.5 ([#2157](https://github.com/aws-powertools/powertools-lambda-java/issues/2157)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.9.0 to 3.10.0 ([#2155](https://github.com/aws-powertools/powertools-lambda-java/issues/2155)) +* bump com.amazonaws:aws-lambda-java-runtime-interface-client ([#2149](https://github.com/aws-powertools/powertools-lambda-java/issues/2149)) +* bump aws.sdk.version from 2.33.2 to 2.34.5 ([#2156](https://github.com/aws-powertools/powertools-lambda-java/issues/2156)) +* bump org.codehaus.mojo:versions-maven-plugin ([#2148](https://github.com/aws-powertools/powertools-lambda-java/issues/2148)) +* bump squidfunk/mkdocs-material in /docs ([#2144](https://github.com/aws-powertools/powertools-lambda-java/issues/2144)) +* bump tj-actions/changed-files from 46.0.5 to 47.0.0 ([#2143](https://github.com/aws-powertools/powertools-lambda-java/issues/2143)) +* bump sam/build-java21 ([#2141](https://github.com/aws-powertools/powertools-lambda-java/issues/2141)) +* bump com.amazonaws:aws-lambda-java-core from 1.3.0 to 1.4.0 ([#2135](https://github.com/aws-powertools/powertools-lambda-java/issues/2135)) +* **deps:** Use mockito 5.20.0 ([#2181](https://github.com/aws-powertools/powertools-lambda-java/issues/2181)) +* **docs:** Add AWS docs meta tags ([#2170](https://github.com/aws-powertools/powertools-lambda-java/issues/2170)) + + +<a name="v2.4.0"></a> +## [v2.4.0] - 2025-09-09 +## Bug Fixes + +* **ci:** Update branch protection output ([#2053](https://github.com/aws-powertools/powertools-lambda-java/issues/2053)) + +## Documentation + +* Add AWS copyright footer. ([#2119](https://github.com/aws-powertools/powertools-lambda-java/issues/2119)) +* Update docs introduction +* Rename wrong POWERTOOLS_DISABLE_METRICS to correct POWERTOOLS_METRICS_DISABLED environment variable. ([#2043](https://github.com/aws-powertools/powertools-lambda-java/issues/2043)) +* update readme ([#2045](https://github.com/aws-powertools/powertools-lambda-java/issues/2045)) + +## Features + +* Support CRaC priming of powertools validation ([#2081](https://github.com/aws-powertools/powertools-lambda-java/issues/2081)) +* **graalvm:** GraalVM support for powertools-cloudformation ([#2090](https://github.com/aws-powertools/powertools-lambda-java/issues/2090)) +* **graalvm:** GraalVM support for Idempotency utility ([#2080](https://github.com/aws-powertools/powertools-lambda-java/issues/2080)) +* **logging:** Log buffering support for Logj42 and Logback ([#2103](https://github.com/aws-powertools/powertools-lambda-java/issues/2103)) + +## Maintenance + +* bump dev.aspectj:aspectj-maven-plugin from 1.13.1 to 1.14.1 ([#2099](https://github.com/aws-powertools/powertools-lambda-java/issues/2099)) +* bump dev.aspectj:aspectj-maven-plugin from 1.14 to 1.14.1 ([#2037](https://github.com/aws-powertools/powertools-lambda-java/issues/2037)) +* bump github/codeql-action from 3.29.8 to 3.29.9 ([#2038](https://github.com/aws-powertools/powertools-lambda-java/issues/2038)) +* bump org.apache.maven.plugins:maven-deploy-plugin ([#2040](https://github.com/aws-powertools/powertools-lambda-java/issues/2040)) +* bump org.yaml:snakeyaml from 2.4 to 2.5 ([#2111](https://github.com/aws-powertools/powertools-lambda-java/issues/2111)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.8.1 to 3.9.0 ([#2114](https://github.com/aws-powertools/powertools-lambda-java/issues/2114)) +* bump aws.sdk.version from 2.32.31 to 2.33.1 ([#2115](https://github.com/aws-powertools/powertools-lambda-java/issues/2115)) +* bump graalvm/setup-graalvm from 1.3.5 to 1.3.6 ([#2116](https://github.com/aws-powertools/powertools-lambda-java/issues/2116)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.213.0 to 2.214.0 ([#2117](https://github.com/aws-powertools/powertools-lambda-java/issues/2117)) +* bump aws-actions/configure-aws-credentials from 4.3.1 to 5.0.0 ([#2120](https://github.com/aws-powertools/powertools-lambda-java/issues/2120)) +* bump com.github.spotbugs:spotbugs-maven-plugin ([#2125](https://github.com/aws-powertools/powertools-lambda-java/issues/2125)) +* bump github/codeql-action from 3.30.0 to 3.30.1 ([#2126](https://github.com/aws-powertools/powertools-lambda-java/issues/2126)) +* bump squidfunk/mkdocs-material in /docs ([#2127](https://github.com/aws-powertools/powertools-lambda-java/issues/2127)) +* bump jackson.version from 2.19.2 to 2.20 ([#2097](https://github.com/aws-powertools/powertools-lambda-java/issues/2097)) +* bump aws.sdk.version from 2.32.18 to 2.32.21 ([#2041](https://github.com/aws-powertools/powertools-lambda-java/issues/2041)) +* bump aws.sdk.version from 2.32.26 to 2.32.31 ([#2098](https://github.com/aws-powertools/powertools-lambda-java/issues/2098)) +* bump github/codeql-action from 3.29.11 to 3.30.0 ([#2106](https://github.com/aws-powertools/powertools-lambda-java/issues/2106)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.212.0 to 2.213.0 ([#2100](https://github.com/aws-powertools/powertools-lambda-java/issues/2100)) +* bump org.apache.maven.plugins:maven-compiler-plugin ([#2094](https://github.com/aws-powertools/powertools-lambda-java/issues/2094)) +* bump actions/checkout from 4.2.2 to 5.0.0 ([#2087](https://github.com/aws-powertools/powertools-lambda-java/issues/2087)) +* bump org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions ([#2088](https://github.com/aws-powertools/powertools-lambda-java/issues/2088)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.8.0 to 3.8.1 ([#2085](https://github.com/aws-powertools/powertools-lambda-java/issues/2085)) +* bump com.github.spotbugs:spotbugs-maven-plugin ([#2084](https://github.com/aws-powertools/powertools-lambda-java/issues/2084)) +* bump sam/build-java21 ([#2083](https://github.com/aws-powertools/powertools-lambda-java/issues/2083)) +* bump aws.sdk.version from 2.32.30 to 2.32.31 ([#2093](https://github.com/aws-powertools/powertools-lambda-java/issues/2093)) +* bump actions/dependency-review-action from 4.7.2 to 4.7.3 ([#2092](https://github.com/aws-powertools/powertools-lambda-java/issues/2092)) +* bump aws.sdk.version from 2.32.28 to 2.32.30 ([#2089](https://github.com/aws-powertools/powertools-lambda-java/issues/2089)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.210.0 to 2.211.0 ([#2042](https://github.com/aws-powertools/powertools-lambda-java/issues/2042)) +* bump aws.sdk.version from 2.32.21 to 2.32.22 ([#2046](https://github.com/aws-powertools/powertools-lambda-java/issues/2046)) +* bump com.google.protobuf:protobuf-java from 4.31.1 to 4.32.0 ([#2050](https://github.com/aws-powertools/powertools-lambda-java/issues/2050)) +* bump aws.sdk.version from 2.32.23 to 2.32.25 ([#2054](https://github.com/aws-powertools/powertools-lambda-java/issues/2054)) +* bump squidfunk/mkdocs-material in /docs ([#2074](https://github.com/aws-powertools/powertools-lambda-java/issues/2074)) +* bump github/codeql-action from 3.29.10 to 3.29.11 ([#2073](https://github.com/aws-powertools/powertools-lambda-java/issues/2073)) +* bump log4j.version from 2.25.1 to 2.25.1 ([#2072](https://github.com/aws-powertools/powertools-lambda-java/issues/2072)) +* bump org.apache.maven.plugins:maven-shade-plugin ([#2071](https://github.com/aws-powertools/powertools-lambda-java/issues/2071)) +* bump org.graalvm.buildtools:native-maven-plugin ([#2070](https://github.com/aws-powertools/powertools-lambda-java/issues/2070)) +* bump com.amazonaws:aws-lambda-java-runtime-interface-client ([#2069](https://github.com/aws-powertools/powertools-lambda-java/issues/2069)) +* bump aws.sdk.version from 2.32.2 to 2.32.28 ([#2068](https://github.com/aws-powertools/powertools-lambda-java/issues/2068)) +* bump actions/setup-java from 4.7.1 to 5.0.0 ([#2067](https://github.com/aws-powertools/powertools-lambda-java/issues/2067)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.211.0 to 2.212.0 ([#2066](https://github.com/aws-powertools/powertools-lambda-java/issues/2066)) +* bump org.apache.maven.plugins:maven-javadoc-plugin ([#2065](https://github.com/aws-powertools/powertools-lambda-java/issues/2065)) +* bump aws.sdk.version from 2.32.25 to 2.32.27 ([#2064](https://github.com/aws-powertools/powertools-lambda-java/issues/2064)) +* bump aws.sdk.version from 2.32.22 to 2.32.23 ([#2048](https://github.com/aws-powertools/powertools-lambda-java/issues/2048)) +* bump squidfunk/mkdocs-material in /docs ([#2058](https://github.com/aws-powertools/powertools-lambda-java/issues/2058)) +* bump org.apache.maven.plugins:maven-javadoc-plugin ([#2059](https://github.com/aws-powertools/powertools-lambda-java/issues/2059)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.7.0 to 3.8.0 ([#2057](https://github.com/aws-powertools/powertools-lambda-java/issues/2057)) +* bump actions/checkout from 4.2.2 to 5.0.0 ([#2036](https://github.com/aws-powertools/powertools-lambda-java/issues/2036)) +* bump actions/dependency-review-action from 4.7.1 to 4.7.2 ([#2055](https://github.com/aws-powertools/powertools-lambda-java/issues/2055)) +* bump sam/build-java21 ([#2075](https://github.com/aws-powertools/powertools-lambda-java/issues/2075)) +* bump aws.sdk.version from 2.32.19 to 2.32.26 ([#2060](https://github.com/aws-powertools/powertools-lambda-java/issues/2060)) +* bump github/codeql-action from 3.29.9 to 3.29.10 ([#2056](https://github.com/aws-powertools/powertools-lambda-java/issues/2056)) +* **ci:** Add powertools-e2e-tests/handlers as module to capture it in GitHub actions version upgrades. ([#2063](https://github.com/aws-powertools/powertools-lambda-java/issues/2063)) +* **ci:** Fix bug where docs were released with old version during release workflow. ([#2076](https://github.com/aws-powertools/powertools-lambda-java/issues/2076)) +* **ci:** Run unit tests for GraalVM as well during build. ([#2047](https://github.com/aws-powertools/powertools-lambda-java/issues/2047)) +* **ci:** Remove non-PR triggers for verify dependencies workflow. ([#2044](https://github.com/aws-powertools/powertools-lambda-java/issues/2044)) +* **ci:** Fix circular dependency in dynamodb-local and maven packaging phases. ([#2129](https://github.com/aws-powertools/powertools-lambda-java/issues/2129)) +* **ci:** Do not use Mockito SNAPSHOT version for release. ([#2137](https://github.com/aws-powertools/powertools-lambda-java/issues/2137)) +* **ci:** Set mockito SNAPSHOT version only for Graal profiles. ([#2138](https://github.com/aws-powertools/powertools-lambda-java/issues/2138)) +* **gitignore:** add .kiro, .claude, .amazonq to prevent deletion ([#2078](https://github.com/aws-powertools/powertools-lambda-java/issues/2078)) + + +<a name="v2.3.0"></a> +## [v2.3.0] - 2025-08-12 +## Documentation + +* **examples:** Add Bazel example for core utilities ([#2022](https://github.com/aws-powertools/powertools-lambda-java/issues/2022)) +* **examples:** Add Logging and Tracing to idempotency example with correct configuration. ([#1993](https://github.com/aws-powertools/powertools-lambda-java/issues/1993)) +* **examples:** Enable end to end tracing for SQS batch example. ([#1995](https://github.com/aws-powertools/powertools-lambda-java/issues/1995)) + +## Features + +* Support CRaC priming of powertools metrics and idempotency-dynamodb ([#1861](https://github.com/aws-powertools/powertools-lambda-java/issues/1861)) + +## Maintenance + +* bump github/codeql-action from 3.29.4 to 3.29.5 ([#1992](https://github.com/aws-powertools/powertools-lambda-java/issues/1992)) +* bump org.assertj:assertj-core from 3.27.3 to 3.27.4 ([#2031](https://github.com/aws-powertools/powertools-lambda-java/issues/2031)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.208.0 to 2.210.0 ([#2030](https://github.com/aws-powertools/powertools-lambda-java/issues/2030)) +* bump aws.sdk.version from 2.32.18 to 2.32.19 ([#2029](https://github.com/aws-powertools/powertools-lambda-java/issues/2029)) +* bump co.elastic.logging:logback-ecs-encoder from 1.6.0 to 1.7.0 ([#2028](https://github.com/aws-powertools/powertools-lambda-java/issues/2028)) +* bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.4.0 to 4.9.3.2 ([#2010](https://github.com/aws-powertools/powertools-lambda-java/issues/2010)) +* bump com.amazonaws:aws-lambda-java-runtime-interface-client ([#2026](https://github.com/aws-powertools/powertools-lambda-java/issues/2026)) +* bump github/codeql-action from 3.29.7 to 3.29.8 ([#2027](https://github.com/aws-powertools/powertools-lambda-java/issues/2027)) +* bump org.crac:crac from 1.4.0 to 1.5.0 ([#2025](https://github.com/aws-powertools/powertools-lambda-java/issues/2025)) +* bump aws.sdk.version from 2.32.6 to 2.32.18 ([#2024](https://github.com/aws-powertools/powertools-lambda-java/issues/2024)) +* bump org.junit.jupiter:junit-jupiter from 5.11.1 to 5.13.4 ([#2023](https://github.com/aws-powertools/powertools-lambda-java/issues/2023)) +* bump org.codehaus.mojo:exec-maven-plugin from 3.3.0 to 3.5.1 ([#2015](https://github.com/aws-powertools/powertools-lambda-java/issues/2015)) +* bump aws.sdk.version from 2.32.10 to 2.32.16 ([#2014](https://github.com/aws-powertools/powertools-lambda-java/issues/2014)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.6.1 to 3.7.0 ([#2016](https://github.com/aws-powertools/powertools-lambda-java/issues/2016)) +* bump actions/download-artifact from 4.3.0 to 5.0.0 ([#2017](https://github.com/aws-powertools/powertools-lambda-java/issues/2017)) +* bump squidfunk/mkdocs-material in /docs ([#1984](https://github.com/aws-powertools/powertools-lambda-java/issues/1984)) +* bump org.apache.maven.plugins:maven-surefire-plugin ([#2013](https://github.com/aws-powertools/powertools-lambda-java/issues/2013)) +* bump aws-actions/configure-aws-credentials from 4.2.1 to 4.3.1 ([#2011](https://github.com/aws-powertools/powertools-lambda-java/issues/2011)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.162.1 to 2.208.0 ([#1990](https://github.com/aws-powertools/powertools-lambda-java/issues/1990)) +* **ci:** Make E2E tests compatible with latest CDK lib version. Improve retry implementation. ([#2008](https://github.com/aws-powertools/powertools-lambda-java/issues/2008)) +* **ci:** Improve reliability of retries in TracingE2ET ([#2018](https://github.com/aws-powertools/powertools-lambda-java/issues/2018)) + + +<a name="v2.2.1"></a> +## [v2.2.1] - 2025-07-29 +## Bug Fixes + +* **parameters:** Correctly check for empty values in AppConfig Parameters Provider. ([#1982](https://github.com/aws-powertools/powertools-lambda-java/issues/1982)) + +## Maintenance + +* bump dependabot/fetch-metadata from 2.3.0 to 2.4.0 ([#1954](https://github.com/aws-powertools/powertools-lambda-java/issues/1954)) +* bump github/codeql-action from 3.29.3 to 3.29.4 ([#1978](https://github.com/aws-powertools/powertools-lambda-java/issues/1978)) +* bump org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions ([#1977](https://github.com/aws-powertools/powertools-lambda-java/issues/1977)) +* bump aws.sdk.version from 2.31.78 to 2.32.6 ([#1976](https://github.com/aws-powertools/powertools-lambda-java/issues/1976)) +* bump com.amazonaws:aws-lambda-java-events from 3.16.0 to 3.16.1 ([#1975](https://github.com/aws-powertools/powertools-lambda-java/issues/1975)) +* bump com.networknt:json-schema-validator from 1.5.1 to 1.5.8 ([#1974](https://github.com/aws-powertools/powertools-lambda-java/issues/1974)) +* bump ossf/scorecard-action from 2.4.0 to 2.4.2 ([#1950](https://github.com/aws-powertools/powertools-lambda-java/issues/1950)) +* bump org.apache.maven.plugins:maven-compiler-plugin ([#1972](https://github.com/aws-powertools/powertools-lambda-java/issues/1972)) +* bump actions/download-artifact from 4.2.1 to 4.3.0 ([#1967](https://github.com/aws-powertools/powertools-lambda-java/issues/1967)) +* bump aws-actions/configure-aws-credentials from 2.2.0 to 4.2.1 ([#1965](https://github.com/aws-powertools/powertools-lambda-java/issues/1965)) +* bump actions/dependency-review-action from 4.5.0 to 4.7.1 ([#1968](https://github.com/aws-powertools/powertools-lambda-java/issues/1968)) +* bump actions/checkout from 3.5.3 to 4.2.2 ([#1963](https://github.com/aws-powertools/powertools-lambda-java/issues/1963)) +* bump sam/build-java21 ([#1962](https://github.com/aws-powertools/powertools-lambda-java/issues/1962)) +* bump squidfunk/mkdocs-material in /docs ([#1961](https://github.com/aws-powertools/powertools-lambda-java/issues/1961)) +* bump actions/upload-artifact from 4.5.0 to 4.6.2 ([#1953](https://github.com/aws-powertools/powertools-lambda-java/issues/1953)) +* bump github/codeql-action from 3.27.9 to 3.29.3 ([#1958](https://github.com/aws-powertools/powertools-lambda-java/issues/1958)) +* bump actions/setup-java from 3.11.0 to 4.7.1 ([#1957](https://github.com/aws-powertools/powertools-lambda-java/issues/1957)) +* **ci:** Add Docker paths via globs to dependabot and update Dockerfiles to pin sha256 ([#1960](https://github.com/aws-powertools/powertools-lambda-java/issues/1960)) +* **ci:** Remove osv workflow. ([#1973](https://github.com/aws-powertools/powertools-lambda-java/issues/1973)) +* **ci:** add new dependabot package ecosystems ([#1948](https://github.com/aws-powertools/powertools-lambda-java/issues/1948)) +* **ci:** Add GraalVM E2E tests and GH workflows ([#1945](https://github.com/aws-powertools/powertools-lambda-java/issues/1945)) + + +<a name="v2.2.0"></a> +## [v2.2.0] - 2025-07-15 +## Bug Fixes + +* **examples:** Fix GraalVM metadata after common runtime client changes ([#1935](https://github.com/aws-powertools/powertools-lambda-java/issues/1935)) + +## Features + +* **batch:** add support for batch execution in parallel with custom Executor ([#1900](https://github.com/aws-powertools/powertools-lambda-java/issues/1900)) +* **serialization:** Add GraalVM metadata configuration ([#1905](https://github.com/aws-powertools/powertools-lambda-java/issues/1905)) + +## Maintenance + +* update issue, PR, and discussion templates ([#1915](https://github.com/aws-powertools/powertools-lambda-java/issues/1915)) +* **ci:** remove v2 dependabot configuration. Restore OSSF scorecard workflow. ([#1924](https://github.com/aws-powertools/powertools-lambda-java/issues/1924)) +* **ci:** Update branch protection rules ([#1914](https://github.com/aws-powertools/powertools-lambda-java/issues/1914)) + + +<a name="v2.1.1"></a> +## [v2.1.1] - 2025-06-20 +## Bug Fixes + +* **kafka:** Handle message indices in proto data also for Glue Schema Registry ([#1907](https://github.com/aws-powertools/powertools-lambda-java/issues/1907)) + +## Maintenance + + + +<a name="v2.1.0"></a> +## [v2.1.0] - 2025-06-19 +## Bug Fixes + +* **ci:** Add maven project description to Kafka utility. ([#1903](https://github.com/aws-powertools/powertools-lambda-java/issues/1903)) +* **kafka:** Add support for confluent message indices. ([#1902](https://github.com/aws-powertools/powertools-lambda-java/issues/1902)) +* **metrics:** Do not flush when no metrics were added to avoid printing root-level _aws dict ([#1891](https://github.com/aws-powertools/powertools-lambda-java/issues/1891)) -All notable changes to this project will be documented in this file. +## Documentation -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). +* Announce deprecation of v1 +* Version documentation ([#1878](https://github.com/aws-powertools/powertools-lambda-java/issues/1878)) +## Features +* **kafka:** New Kafka utility ([#1898](https://github.com/aws-powertools/powertools-lambda-java/issues/1898)) -## [Unreleased] +## Maintenance -## [1.16.1] - 2023-07-19 +* **ci:** Update workflows to make v2 the default ([#1888](https://github.com/aws-powertools/powertools-lambda-java/issues/1888)) -* Fix: idempotency timeout bug (#1285) by @scottgerring -* Fix: ParamManager cannot provide default SSM & Secrets providers (#1282) by @jeromevdl -* Fix: Handle batch failures in FIFO queues correctly (#1183) by @scottgerring -* Deps: Bump third party dependencies to the latest versions. +<a name="v2.0.0"></a> +## [v2.0.0] - 2025-06-12 +## Maintenance -## [1.16.0] - 2023-06-29 -### Added -* Feature: Add AppConfig provider to parameters module (#1104) by @scottgerring +<a name="v2.0.0-RC1"></a> +## [v2.0.0-RC1] - 2025-06-11 +## Bug Fixes -### Maintenance -* Fix: missing idempotency key should not persist any data (#1201) by @jeromevdl -* Fix:Removing env var credentials provider as default. (#1161) by @msailes -* Chore: Swap implementation of `aspectj-maven-plugin` to support Java 17 (#1172) by @mriccia -* Test: end-to-end tests for core modules and idempotency (#970) by @jeromevdl -* Chore: cleanup spotbugs maven profiles (#1236) by @jeromevdl -* Chore: removing logback from all components (#1227) by @jeromevdl -* Chore: Roll SLF4J log4j bindings to v2 (#1190) by @scottgerring -* Deps: Bump third party dependencies to the latest versions. +* workflow paths for examples v2 builds +* add aspectj-rt to batch e2e ([#1410](https://github.com/aws-powertools/powertools-lambda-java/issues/1410)) +* **ci:** Fix failing E2E tests and temporarily exclude TracingE2E ([#1847](https://github.com/aws-powertools/powertools-lambda-java/issues/1847)) +* **ci:** add user/pass to javasetup ([#1832](https://github.com/aws-powertools/powertools-lambda-java/issues/1832)) +* **ci:** Update control flow to allow for better skipping of things ([#1831](https://github.com/aws-powertools/powertools-lambda-java/issues/1831)) +* **ci:** Checkout repo on doc release ([#1869](https://github.com/aws-powertools/powertools-lambda-java/issues/1869)) +* **logging:** Prevent accidental overwriting of reserved keys via structured arguments +* **logging:** Escape double-quotes when serializing strings into JSON. ([#1845](https://github.com/aws-powertools/powertools-lambda-java/issues/1845)) +* **v2:** Fix params builder to provide default transformation manager ([#1549](https://github.com/aws-powertools/powertools-lambda-java/issues/1549)) +## Documentation -## [1.15.0] - 2023-03-20 +* v2 documentation maintenance fixing formatting and dependency issues as well as adding roadmap and llms.txt ([#1819](https://github.com/aws-powertools/powertools-lambda-java/issues/1819)) +* **metrics:** Add upgrade guide for re-designed Metrics utility ([#1868](https://github.com/aws-powertools/powertools-lambda-java/issues/1868)) +* **v2:** Create upgrade guide and versioning policy ([#1856](https://github.com/aws-powertools/powertools-lambda-java/issues/1856)) -### Added -* Feature: Add DynamoDB provider to parameters module (#1091) by @scottgerring -* Feature: Update to powertools-cloudformation to deprecate `Response.success()` and `Response.failed()` methods. New helper methods are added to make it easier to follow best practices `Response.success(String physicalResourceId)` and `Response.failed(String physicalResourceId)`. For a detailed explanation please read the [powertools-cloudformation documentation page](https://docs.powertools.aws.dev/lambda-java/utilities/custom_resources/). (#1082) by @msailes -* Update how a Lambda request handler method is identified (#1058) by @humanzz +## Features -### Maintenance -* Deps: Bump third party dependencies to the latest versions. -* Examples: Import examples from aws-samples/aws-lambda-powertools-examples (#1051) by @scottgerring -* Deprecate withMetricLogger in favor of withMetricsLogger (#1060) by @humanzz -* Update issue templates (#1062) by @machafer -* Send code coverage report (jacoco) to codecov (#1094) by @jeromevdl +* advanced logging ([#1539](https://github.com/aws-powertools/powertools-lambda-java/issues/1539)) +* upgraded embedded metrics library for high resolution metrics ([#1550](https://github.com/aws-powertools/powertools-lambda-java/issues/1550)) +* **cfn-custom-resource:** Add optional 'reason' field for detailed failure reporting ([#1810](https://github.com/aws-powertools/powertools-lambda-java/issues/1810)) +* **idempotency:** Add support for ReturnValuesOnConditionCheckFailure in Idempotency. ([#1821](https://github.com/aws-powertools/powertools-lambda-java/issues/1821)) +* **idempotency:** Add response hook feature ([#1814](https://github.com/aws-powertools/powertools-lambda-java/issues/1814)) +* **metrics:** New metrics module implementation with support for Metrics providers and usage without annotations ([#1863](https://github.com/aws-powertools/powertools-lambda-java/issues/1863)) +* **v2:** Add GraalVM reachability metadata for core utilities ([#1753](https://github.com/aws-powertools/powertools-lambda-java/issues/1753)) +* **v2:** parallel batch processing ([#1620](https://github.com/aws-powertools/powertools-lambda-java/issues/1620)) +* **v2:** batch validation with partial failure ([#1621](https://github.com/aws-powertools/powertools-lambda-java/issues/1621)) +* **v2:** publish snapshots ([#1655](https://github.com/aws-powertools/powertools-lambda-java/issues/1655)) +* **v2:** GraalVM support for parameters module ([#1824](https://github.com/aws-powertools/powertools-lambda-java/issues/1824)) +* **v2:** new logging module ([#1435](https://github.com/aws-powertools/powertools-lambda-java/issues/1435)) +* **v2:** Validation failures return 400s ([#1489](https://github.com/aws-powertools/powertools-lambda-java/issues/1489)) -### Documentation +## Maintenance -* Improve `powertools-cloudformation` docs (#1090) by @mriccia -* Add link to Powertools for AWS Lambda (Java) workshop (#1095) by @scottgerring -* Fix mdocs and git revision plugin integration (#1066) by @machafer +* Support spotbugs running anywhere ([#1537](https://github.com/aws-powertools/powertools-lambda-java/issues/1537)) +* V2 update from main ([#1365](https://github.com/aws-powertools/powertools-lambda-java/issues/1365)) +* remove Java 8 from v2 examples ([#1531](https://github.com/aws-powertools/powertools-lambda-java/issues/1531)) +* fix end 2 end build ([#1534](https://github.com/aws-powertools/powertools-lambda-java/issues/1534)) +* cleanup poms and reduce warning noise ([#1535](https://github.com/aws-powertools/powertools-lambda-java/issues/1535)) +* [V2] rename 'core' module to 'common' ([#1364](https://github.com/aws-powertools/powertools-lambda-java/issues/1364)) +* update v2 ([#1409](https://github.com/aws-powertools/powertools-lambda-java/issues/1409)) +* remove aspectj-rt from the library ([#1408](https://github.com/aws-powertools/powertools-lambda-java/issues/1408)) +* Start V2 branch ([#1346](https://github.com/aws-powertools/powertools-lambda-java/issues/1346)) +* **automation:** Update automation workflows ([#1779](https://github.com/aws-powertools/powertools-lambda-java/issues/1779)) ([#1830](https://github.com/aws-powertools/powertools-lambda-java/issues/1830)) +* **ci:** Set snapshot repository to "central" server ID +* **ci:** Publish to Maven Central instead of OSSRH instance ([#1858](https://github.com/aws-powertools/powertools-lambda-java/issues/1858)) +* **v2:** Merge down from main ([#1574](https://github.com/aws-powertools/powertools-lambda-java/issues/1574)) +* **v2:** Split parameters module up by parameter provider ([#1403](https://github.com/aws-powertools/powertools-lambda-java/issues/1403)) +* **v2:** Fix IaC lint ([#1576](https://github.com/aws-powertools/powertools-lambda-java/issues/1576)) +* **v2:** e2e tests ([#1571](https://github.com/aws-powertools/powertools-lambda-java/issues/1571)) +* **v2:** clean examples ([#1495](https://github.com/aws-powertools/powertools-lambda-java/issues/1495)) +* **v2:** document use of aws-crt-client ([#1092](https://github.com/aws-powertools/powertools-lambda-java/issues/1092)) ([#1605](https://github.com/aws-powertools/powertools-lambda-java/issues/1605)) +* **v2:** remove java 1.8 relics from the code ([#1659](https://github.com/aws-powertools/powertools-lambda-java/issues/1659)) +* **v2:** remove deprecated code ([#1624](https://github.com/aws-powertools/powertools-lambda-java/issues/1624)) +* **v2:** Remove rule preventing production release of 2.0.0 ([#1867](https://github.com/aws-powertools/powertools-lambda-java/issues/1867)) +* **v2:** Split powertools idempotency module (without redis impl) ([#1559](https://github.com/aws-powertools/powertools-lambda-java/issues/1559)) +## Pull Requests -## [1.14.0] - 2023-02-17 +* Merge pull request [#1608](https://github.com/aws-powertools/powertools-lambda-java/issues/1608) from aws-powertools/chore/v2-merge-main-down +* Merge pull request [#1525](https://github.com/aws-powertools/powertools-lambda-java/issues/1525) from aws-powertools/chore/main-into-v2 +* Merge pull request [#1494](https://github.com/aws-powertools/powertools-lambda-java/issues/1494) from aws-powertools/chore/merge-main-into-v2 +* Merge pull request [#1492](https://github.com/aws-powertools/powertools-lambda-java/issues/1492) from aws-powertools/main-into-v2-again +* Merge pull request [#1477](https://github.com/aws-powertools/powertools-lambda-java/issues/1477) from aws-powertools/chore/main-into-v2 -### Added -* Feature: Introduce `MetricsUtils.withMetricsLogger()` utility method (#1000) by @humanzz +<a name="v1.20.2"></a> +## [v1.20.2] - 2025-05-20 +## Bug Fixes -#### Maintenance +* **ci:** update release workflow ([#1854](https://github.com/aws-powertools/powertools-lambda-java/issues/1854)) +* **ci:** minor fixes for workflows ([#1829](https://github.com/aws-powertools/powertools-lambda-java/issues/1829)) -* Update logic for recording documentation pages views to use correct runtime name (#1047) by @kozub -* Deps: Bump third party dependencies to the latest versions. +## Documentation -### Documentation +* Add version policy page and llms.txt, enable privacy plugin, fix formatting ([#1823](https://github.com/aws-powertools/powertools-lambda-java/issues/1823)) -* Docs: Update Powertools for AWS Lambda (Java) definition by @heitorlessa -* Docs: Add information about other supported langauges to README and docs (#1033) by @kozub +## Maintenance -## [1.13.0] - 2022-12-14 +* **automation:** Update automation workflows ([#1779](https://github.com/aws-powertools/powertools-lambda-java/issues/1779)) -### Added -* Feature: Idempotency - Handle Lambda timeout scenarios for INPROGRESS records (#933) by @jeromevdl +<a name="v1.20.1"></a> +## [v1.20.1] - 2025-04-08 +## Bug Fixes -### Bug Fixes +* Load version.properties file as resource stream to fix loading when packaged as jar. ([#1813](https://github.com/aws-powertools/powertools-lambda-java/issues/1813)) -* Fix: Envelope is not taken into account with built-in types (#960) by @jeromevdl -* Fix: Code suggestion from CodeGuru (#984) by @kozub -* Fix: Compilation warning with SqsLargeMessageAspect on gradle (#998) by @jeromevdl -* Fix: Log message processing exceptions as occur (#1011) by @nem0-97 +## Documentation -### Documentation +* fix 2 typos +* Correct XML formatting for Maven configuration in Large Messages utility docs -* Docs: Add missing grammar article (#976) by @fsmiamoto +## Maintenance -## [1.12.3] - 2022-07-12 +* Prep release 1.20.1 ([#1817](https://github.com/aws-powertools/powertools-lambda-java/issues/1817)) -#### Maintenance -* Fixes to resolve vulnerable transitive dependencies ([919](https://github.com/aws-powertools/powertools-lambda-java/issues/919)) +<a name="v1.20.0"></a> +## [v1.20.0] - 2025-03-25 +## Features +* **cfn-custom-resource:** Add optional 'reason' field for detailed failure reporting ([#1758](https://github.com/aws-powertools/powertools-lambda-java/issues/1758)) -## [1.12.2] - 2022-04-29 +## Maintenance -### Bug Fixes +* Prep release 1.20.0 ([#1811](https://github.com/aws-powertools/powertools-lambda-java/issues/1811)) -* **SQS Large message processing**: Classpath conflict on `PayloadS3Pointer` when consumer application depends on `payloadoffloading-common`, introduced in [v1.8.0](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v1.8.0). ([#851](https://github.com/aws-powertools/powertools-lambda-java/pull/851)) +<a name="v1.19.0"></a> +## [v1.19.0] - 2025-03-07 +## Bug Fixes -## [1.12.1] - 2022-04-21 +* add workflow dispatch to OSV +* Allow empty responses as well as null response in AppConfig ([#1673](https://github.com/aws-powertools/powertools-lambda-java/issues/1673)) +* **ci:** Add workflow_dispatch to build script ([#1792](https://github.com/aws-powertools/powertools-lambda-java/issues/1792)) +* **ci:** add permissions to release workflow +* **ci:** Permissions ([#1771](https://github.com/aws-powertools/powertools-lambda-java/issues/1771)) +* **ci:** OSSF Changes ([#1769](https://github.com/aws-powertools/powertools-lambda-java/issues/1769)) -### Bug Fixes +## Documentation -* **Idempotency**: thread-safety issue of MessageDigest ([#817](https://github.com/aws-powertools/powertools-lambda-java/pull/817)) -* **Idempotency**: disable dynamodb client creation in persistent store when disabling idempotency ([#796](https://github.com/aws-powertools/powertools-lambda-java/pull/796)) +* add roadmap page and include roadmap for 2025 +* improve tracing doc for sdk instrumentation ([#1687](https://github.com/aws-powertools/powertools-lambda-java/issues/1687)) +* add link to Powertools for AWS Lambda workshop ([#1641](https://github.com/aws-powertools/powertools-lambda-java/issues/1641)) +* HelloWorldStreamFunction in examples fails with sam ([#1532](https://github.com/aws-powertools/powertools-lambda-java/issues/1532)) +## Features -### Maintenance +* **build:** remove java 8 support in v2 ([#1606](https://github.com/aws-powertools/powertools-lambda-java/issues/1606)) +* **ci:** Add OSV -* **deps**: Bump third party dependencies to the latest versions. +## Maintenance +* deprecate java1.8 al1 ([#1706](https://github.com/aws-powertools/powertools-lambda-java/issues/1706)) +* Testing java21 aspectj pre-release ([#1519](https://github.com/aws-powertools/powertools-lambda-java/issues/1519)) +* Remove build cruft +* SAM and Terraform IaC extracted from pr_build and simplified approach. ([#1533](https://github.com/aws-powertools/powertools-lambda-java/issues/1533)) +* Update netty version ([#1768](https://github.com/aws-powertools/powertools-lambda-java/issues/1768)) +* Set versions of transitive dependencies ([#1767](https://github.com/aws-powertools/powertools-lambda-java/issues/1767)) +* update Jackson +* Remove empty CDK test ([#1542](https://github.com/aws-powertools/powertools-lambda-java/issues/1542)) +* add openssf to repo +* remove auto-merge +* remove unecessary creds acquisition ([#1572](https://github.com/aws-powertools/powertools-lambda-java/issues/1572)) +* update version to next snapshot: 1-19.0-SNAPSHOT ([#1516](https://github.com/aws-powertools/powertools-lambda-java/issues/1516)) +* **ci:** update permissions ([#1764](https://github.com/aws-powertools/powertools-lambda-java/issues/1764)) +* **ci:** Add release environment +* **ci:** Remove RELEASE variable ([#1772](https://github.com/aws-powertools/powertools-lambda-java/issues/1772)) +* **deps:** update JSII to 1.108 ([#1791](https://github.com/aws-powertools/powertools-lambda-java/issues/1791)) +* **deps:** Update deps for jackson ([#1793](https://github.com/aws-powertools/powertools-lambda-java/issues/1793)) +* **docs:** load self hosted mermaid.js -## [1.12.0] - 2022-03-01 +## Pull Requests -### Added - * **Easy Event Deserialization**: Extraction and deserialization of the main content of events (body, messages, ...) [#757](https://github.com/aws-powertools/powertools-lambda-java/pull/757) +* Merge pull request [#1720](https://github.com/aws-powertools/powertools-lambda-java/issues/1720) from aws-powertools/chore/docs_script_self -### Bug Fixes - * Different behavior while using SSMProvider with or without trailing slash in parameter names [#758](https://github.com/aws-powertools/powertools-lambda-java/issues/758) +<a name="v1.18.0"></a> +## [v1.18.0] - 2023-11-16 +## Bug Fixes -## [1.11.0] - 2022-02-16 +* get trace id from system property when env var is not set ([#1503](https://github.com/aws-powertools/powertools-lambda-java/issues/1503)) +* Fix schema validation unit test build issues ([#1456](https://github.com/aws-powertools/powertools-lambda-java/issues/1456)) -### Added - * Powertools for AWS Lambda (Java) Idempotency module: New module to get your Lambda function [Idempotent](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) (#717) - * Powertools for AWS Lambda (Java) Serialization module: New module to handle JSON (de)serialization (Jackson ObjectMapper, JMESPath functions) +## Documentation +* Update gradle configuration readme ([#1359](https://github.com/aws-powertools/powertools-lambda-java/issues/1359)) +* Adding Kotlin example. ([#1454](https://github.com/aws-powertools/powertools-lambda-java/issues/1454)) +* apply line highlight only for default light mode ([#1453](https://github.com/aws-powertools/powertools-lambda-java/issues/1453)) +* Add Serveless Framework example ([#1363](https://github.com/aws-powertools/powertools-lambda-java/issues/1363)) +* Fix link to SQS large message migration guide ([#1422](https://github.com/aws-powertools/powertools-lambda-java/issues/1422)) +* Change link to absolute versioned path for examples ([#1374](https://github.com/aws-powertools/powertools-lambda-java/issues/1374)) +* **customer-reference:** add Vertex Pharmaceuticals as a customer reference ([#1486](https://github.com/aws-powertools/powertools-lambda-java/issues/1486)) +* **logging:** align example cloudwatch example to correct output from code: lambda_request_id --> function_request_id ([#1411](https://github.com/aws-powertools/powertools-lambda-java/issues/1411)) -## [1.10.3] - 2022-02-01 +## Features -### Bug Fixes +* ALC ([#1514](https://github.com/aws-powertools/powertools-lambda-java/issues/1514)) +* Add support for POWERTOOLS_LOGGER_LOG_EVENT ([#1510](https://github.com/aws-powertools/powertools-lambda-java/issues/1510)) +* Terraform example ([#1478](https://github.com/aws-powertools/powertools-lambda-java/issues/1478)) -* **SQS Batch processing**: Prevent message to be marked as success if failed sending to DLQ for non retryable exceptions. [#731](https://github.com/aws-powertools/powertools-lambda-java/pull/731) +## Maintenance -### Documentation +* Addition of Warn Message If Invalid Annotation Key While Tracing [#1511](https://github.com/aws-powertools/powertools-lambda-java/issues/1511) ([#1512](https://github.com/aws-powertools/powertools-lambda-java/issues/1512)) +* artifacts size on good branches ([#1493](https://github.com/aws-powertools/powertools-lambda-java/issues/1493)) +* add missing projects and improve workflow ([#1487](https://github.com/aws-powertools/powertools-lambda-java/issues/1487)) +* java21 support in our build ([#1488](https://github.com/aws-powertools/powertools-lambda-java/issues/1488)) +* Reporting size of the jars in GitHub comments ([#1196](https://github.com/aws-powertools/powertools-lambda-java/issues/1196)) +* secure github actions using hash instead of versions ([#1232](https://github.com/aws-powertools/powertools-lambda-java/issues/1232)) -* **SQS Batch processing**: Improve [documentation](https://docs.powertools.aws.dev/lambda-java/utilities/batch/#iam-permissions) on IAM premissions required by function when using utility with an encrypted SQS queue with customer managed KMS keys. +<a name="v1.17.0"></a> +## [v1.17.0] - 2023-08-21 +## Bug Fixes +* Roll log4j shade transformer forwards ([#1376](https://github.com/aws-powertools/powertools-lambda-java/issues/1376)) +* Rollback doc changes ([#1323](https://github.com/aws-powertools/powertools-lambda-java/issues/1323)) +* use default credentials provider for all provided SDK clients ([#1303](https://github.com/aws-powertools/powertools-lambda-java/issues/1303)) -## [1.10.2] - 2022-01-07 +## Documentation -* **Tracing**: Ability to override object mapper used for serializing method response as trace metadata when enabled. This provides users ability to customize how and what you want to capture as metadata from method response object. [#698](https://github.com/aws-powertools/powertools-lambda-java/pull/698) +* Adding CDK example ([#1321](https://github.com/aws-powertools/powertools-lambda-java/issues/1321)) +* improve contributing guide ([#1334](https://github.com/aws-powertools/powertools-lambda-java/issues/1334)) +* Add maintainers guide ([#1326](https://github.com/aws-powertools/powertools-lambda-java/issues/1326)) +* versioning - fix typo ([#1322](https://github.com/aws-powertools/powertools-lambda-java/issues/1322)) +* add support for docs versioning ([#1239](https://github.com/aws-powertools/powertools-lambda-java/issues/1239)) ([#1293](https://github.com/aws-powertools/powertools-lambda-java/issues/1293)) +* Started cleaning up example doc ([#1291](https://github.com/aws-powertools/powertools-lambda-java/issues/1291)) -## [1.10.1] - 2022-01-06 +## Features -* **Logging**: Upgrade Log4j to version 2.17.1 for [CVE-2021-44832](https://nvd.nist.gov/vuln/detail/CVE-2021-44832) +* Add Batch Processor module ([#1317](https://github.com/aws-powertools/powertools-lambda-java/issues/1317)) +* large message in SQS and SNS ([#1310](https://github.com/aws-powertools/powertools-lambda-java/issues/1310)) -## [1.10.0] - 2021-12-27 +## Maintenance -* **Logging**: Modern log4j configuration to customise structured logging. Refer [docs](https://docs.powertools.aws.dev/lambda-java/core/logging/#upgrade-to-jsontemplatelayout-from-deprecated-lambdajsonlayout-configuration-in-log4j2xml) to start using new config. [#670](https://github.com/aws-powertools/powertools-lambda-java/pull/670) -* **SQS Batch**: Support batch size greater than 10. [#667](https://github.com/aws-powertools/powertools-lambda-java/pull/667) +* Fix missing version change pieces ([#1382](https://github.com/aws-powertools/powertools-lambda-java/issues/1382)) +* apply checkstyle again ([#1339](https://github.com/aws-powertools/powertools-lambda-java/issues/1339)) +* Add powertools specific user-agent-suffix to the AWS SDK v2 clients ([#1306](https://github.com/aws-powertools/powertools-lambda-java/issues/1306)) +* checkstyle formater & linter ([#1316](https://github.com/aws-powertools/powertools-lambda-java/issues/1316)) +* update poms to SNAPSHOT version for dev ([#1299](https://github.com/aws-powertools/powertools-lambda-java/issues/1299)) -## [1.9.0] - 2021-12-21 -* **Logging**: Upgrade Log4j to version 2.17.0 for [CVE-2021-45105](https://nvd.nist.gov/vuln/detail/CVE-2021-45105) -* **Tracing**: add `Service` annotation. [#654](https://github.com/aws-powertools/powertools-lambda-java/issues/654) +<a name="v1.16.1"></a> +## [v1.16.1] - 2023-07-19 +## Bug Fixes -## [1.8.2] - 2021-12-15 +* idempotency timeout bug ([#1285](https://github.com/aws-powertools/powertools-lambda-java/issues/1285)) +* ParamManager cannot provide default SSM & Secrets providers ([#1282](https://github.com/aws-powertools/powertools-lambda-java/issues/1282)) +* Handle batch failures in FIFO queues correctly ([#1183](https://github.com/aws-powertools/powertools-lambda-java/issues/1183)) +* examples shouldn't be deployed to mvn central ([#1253](https://github.com/aws-powertools/powertools-lambda-java/issues/1253)) -## Security +## Documentation -* Upgrading Log4j to version 2.16.0 for [CVE-2021-45046](https://nvd.nist.gov/vuln/detail/CVE-2021-45046) +* update README.md ([#1294](https://github.com/aws-powertools/powertools-lambda-java/issues/1294)) +* adding our customer references ([#1287](https://github.com/aws-powertools/powertools-lambda-java/issues/1287)) +* update documentation for aspectJ ([#1273](https://github.com/aws-powertools/powertools-lambda-java/issues/1273)) -## [1.8.1] - 2021-12-10 +## Maintenance -## Security +* **unit-test:** Add missing unit tests in modules with low coverage ([#1264](https://github.com/aws-powertools/powertools-lambda-java/issues/1264)) -* Upgrading Log4j to version 2.15.0 for [CVE-2021-44228](https://nvd.nist.gov/vuln/detail/CVE-2021-44228) -## [1.8.0] - 2021-11-05 +<a name="v1.16.0"></a> +## [v1.16.0] - 2023-06-29 +## Bug Fixes -### Added +* e2e tests on JDK8 ([#1225](https://github.com/aws-powertools/powertools-lambda-java/issues/1225)) +* codecov URL ([#1222](https://github.com/aws-powertools/powertools-lambda-java/issues/1222)) +* remove GH pages ([#1211](https://github.com/aws-powertools/powertools-lambda-java/issues/1211)) +* update references to other variants +* missing idempotency key should not persist any data ([#1201](https://github.com/aws-powertools/powertools-lambda-java/issues/1201)) +* **docs:** add site_url to docs -* **Powertools for AWS Lambda (Java) Cloudformation module (NEW)**: New module simplifying [AWS Lambda-backed custom resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html) written in Java. [#560](https://github.com/aws-powertools/powertools-lambda-java/pull/560) -* **SQS Large message processing**: Ability to override the default `S3Client` use to fetch payload from S3. [#602](https://github.com/aws-powertools/powertools-lambda-java/pull/602) +## Features -### Regression +* Add AppConfig provider to parameters module ([#1104](https://github.com/aws-powertools/powertools-lambda-java/issues/1104)) +* end-to-end tests for core modules and idempotency ([#970](https://github.com/aws-powertools/powertools-lambda-java/issues/970)) +* **docs:** adds S3 Docs uploader -* **Logging**: `@Logging` annotation now works with `@Tracing` annotation on `RequestStreamHandler` when used in `logEvent` mode. [#567](https://github.com/aws-powertools/powertools-lambda-java/pull/567) +## Maintenance -### Maintenance +* Update docs base origin url ([#1238](https://github.com/aws-powertools/powertools-lambda-java/issues/1238)) +* E2E tests GitHub action ([#1175](https://github.com/aws-powertools/powertools-lambda-java/issues/1175)) +* add all java versions and use corretto for build ([#1191](https://github.com/aws-powertools/powertools-lambda-java/issues/1191)) +* Change repo URL to the new location ([#1171](https://github.com/aws-powertools/powertools-lambda-java/issues/1171)) +* Swap implementation of `aspectj-maven-plugin` to support Java 17 ([#1172](https://github.com/aws-powertools/powertools-lambda-java/issues/1172)) +* update e2e-tests with latest Powertools version ([#1173](https://github.com/aws-powertools/powertools-lambda-java/issues/1173)) +* rename project from Powertools to Powertools for AWS Lambda (Java) ([#1169](https://github.com/aws-powertools/powertools-lambda-java/issues/1169)) +* **ci:** add workflow to dispatch analytics fetching ([#1143](https://github.com/aws-powertools/powertools-lambda-java/issues/1143)) -* **deps**: Bump third party dependencies to the latest versions. -## [1.7.3] - 2021-09-14 +<a name="v1.15.0"></a> +## [v1.15.0] - 2023-03-21 +## Bug Fixes -* **SQS Batch processing**: Ability to move non retryable message to configured dead letter queue(DLQ). [#500](https://github.com/aws-powertools/powertools-lambda-java/pull/500) +* **cloudformation-module:** Use physicalResourceId when not provided by custom resource ([#1082](https://github.com/aws-powertools/powertools-lambda-java/issues/1082)) -## [1.7.2] - 2021-08-03 +## Documentation -* **Powertools for AWS Lambda (Java) All Modules**: Upgrade to the latest(1.14.0) aspectj-maven-plugin which also supports Java 9 and newer versions. -Users no longer need to depend on [com.nickwongdev](https://mvnrepository.com/artifact/com.nickwongdev/aspectj-maven-plugin/1.12.6) as a workaround. [#489](https://github.com/aws-powertools/powertools-lambda-java/pull/489) -* **Logging**: Performance optimisation to improve cold start. [#484](https://github.com/aws-powertools/powertools-lambda-java/pull/484) -* **SQS Batch processing/Large message**: Module now lazy loads default SQS client. [#484](https://github.com/aws-powertools/powertools-lambda-java/pull/484) +* **cloudformation-module:** Improve Docs ([#1090](https://github.com/aws-powertools/powertools-lambda-java/issues/1090)) +* **plugin:** fix mdocs and git revision plugin integration ([#1066](https://github.com/aws-powertools/powertools-lambda-java/issues/1066)) -## [1.7.1] - 2021-07-06 +## Features -* **Powertools for AWS Lambda (Java) All Modules**: Fix static code analysis violations done via [spotbugs](https://github.com/spotbugs/spotbugs) ([#458](https://github.com/aws-powertools/powertools-lambda-java/pull/458)). +* Add DynamoDB provider to parameters module ([#1091](https://github.com/aws-powertools/powertools-lambda-java/issues/1091)) -## [1.7.0] - 2021-07-05 +## Maintenance -### Added +* update the project version to 1.15.0 ([#1097](https://github.com/aws-powertools/powertools-lambda-java/issues/1097)) +* **governance:** update issue templates ([#1062](https://github.com/aws-powertools/powertools-lambda-java/issues/1062)) +* **metrics:** deprecate withMetricLogger in favor of withMetricsLogger ([#1060](https://github.com/aws-powertools/powertools-lambda-java/issues/1060)) -* **Logging**: Support for extracting Correlation id using `@Logging` annotation via `correlationIdPath` attribute and `setCorrelationId()` method in `LoggingUtils`([#448](https://github.com/aws-powertools/powertools-lambda-java/pull/448)). -* **Logging**: New `clearState` attribute on `@Logging` annotation to clear previously added custom keys upon invocation([#453](https://github.com/aws-powertools/powertools-lambda-java/pull/453)). -### Maintenance +<a name="v1.14.0"></a> +## [v1.14.0] - 2023-02-17 +## Documentation -* **deps**: Bump third party dependencies to the latest versions. +* **home:** update powertools definition -## [1.6.0] - 2021-06-21 +## Features -### Added +* **metrics:** introduce MetricsUtils.withMetricsLogger utility ([#1000](https://github.com/aws-powertools/powertools-lambda-java/issues/1000)) -* **Tracing**: Support for Boolean and Number type as value in `TracingUtils.putAnnotation()`([#423](https://github.com/aws-powertools/powertools-lambda-java/pull/432)). -* **Logging**: API to remove any additional custom key from logger entry using `LoggingUtils.removeKeys()`([#395](https://github.com/aws-powertools/powertools-lambda-java/pull/395)). +## Maintenance -### Maintenance +* update the project version to 1.14.0 ([#1052](https://github.com/aws-powertools/powertools-lambda-java/issues/1052)) -* **deps**: Bump third party dependencies to the latest versions. -## [1.5.0] - 2021-03-30 +<a name="v1.13.0"></a> +## [v1.13.0] - 2022-12-14 +## Bug Fixes -* **Metrics**: Ability to set multiple dimensions as default dimensions via `MetricsUtils.defaultDimensions()`. - Introduced in [v1.4.0](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v1.4.0) - `MetricsUtils.defaultDimensionSet()` is deprecated now for better user experience. +* envelope is not taken into account with built-in types ([#960](https://github.com/aws-powertools/powertools-lambda-java/issues/960)) -## [1.4.0] - 2021-03-11 -* **Metrics**: Ability to set default dimension for metrics via `MetricsUtils.defaultDimensionSet()`. - - **Note**: If your monitoring depends on [default dimensions](https://github.com/awslabs/aws-embedded-metrics-java/blob/main/src/main/java/software/amazon/cloudwatchlogs/emf/logger/MetricsLogger.java#L173) captured before via [aws-embedded-metrics-java](https://github.com/awslabs/aws-embedded-metrics-java), - those either need to be updated or has to be explicitly captured via `MetricsUtils.defaultDimensionSet()`. - +## Documentation -* **Metrics**: Remove validation of having minimum one dimension. EMF now support [Dimension set being empty](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html) as well. +* add missing grammar article ([#976](https://github.com/aws-powertools/powertools-lambda-java/issues/976)) -## [1.3.0] - 2021-03-05 +## Features -* **Powertools**: It now works out of the box with [code guru profile handler implementation](https://docs.aws.amazon.com/codeguru/latest/profiler-ug/lambda-custom.html). -* **Logging**: Ability to override object mapper used for logging event. This provides customers ability to customize how and what they want to log from event. -* **Metrics**: Module now by default captures AWS Request id as property if used together with Metrics annotation. It will also capture Xray Trace ID as property if tracing is enabled. This ensures good observability and tracing. -* **Metrics**:`withSingleMetric` from `MetricsUtils can now pick the default namespace specified either on Metrics annotation or via POWERTOOLS_METRICS_NAMESPACE env var, without need to specify explicitly for each call. -* **Metrics**:`Metrics` annotation captures metrics even in case of unhandled exception from Lambda function. -* **Docs**: Migrated from Gatsby to MKdocs documentation system +* **idempotency:** handle lambda timeout scenarios for INPROGRESS records ([#933](https://github.com/aws-powertools/powertools-lambda-java/issues/933)) + +## Maintenance + +* update the project version to 1.13.0 ([#1018](https://github.com/aws-powertools/powertools-lambda-java/issues/1018)) + + +<a name="v1.12.3"></a> +## [v1.12.3] - 2022-07-12 +## Maintenance + +* **ci:** fix build ([#853](https://github.com/aws-powertools/powertools-lambda-java/issues/853)) +* **ci:** Address GitHub workaround for CVE-2022-24765. +* **ci:** upgrade to checkout v3 + + +<a name="v1.12.2"></a> +## [v1.12.2] - 2022-04-29 +## Bug Fixes + +* remove local implementation of PayloadS3Pointer.java and use payloadoffloading-common ([#851](https://github.com/aws-powertools/powertools-lambda-java/issues/851)) + + +<a name="v1.12.1"></a> +## [v1.12.1] - 2022-04-21 +## Bug Fixes + +* disable idempotency doesn't disable dynamodb client creation in persistent store ([#796](https://github.com/aws-powertools/powertools-lambda-java/issues/796)) + +## Maintenance + +* correct bug fix release number +* **docs:** additional rename of project name ([#781](https://github.com/aws-powertools/powertools-lambda-java/issues/781)) ([#789](https://github.com/aws-powertools/powertools-lambda-java/issues/789)) + +## Reverts +* chore: correct bug fix release number + + +<a name="v1.12.0"></a> +## [v1.12.0] - 2022-03-01 +## Bug Fixes + +* **docs:** fix title for custom resources page ([#763](https://github.com/aws-powertools/powertools-lambda-java/issues/763)) + +## Features + +* Easy Event Deserialization ([#757](https://github.com/aws-powertools/powertools-lambda-java/issues/757)) + +## Maintenance + +* remove examples from the project as it was moved to aws-lambda-powertools-examples ([#772](https://github.com/aws-powertools/powertools-lambda-java/issues/772)) +* remove SQS and Idempotency examples ([#754](https://github.com/aws-powertools/powertools-lambda-java/issues/754)) +* downgrade release plugin to validate release fail issue +* downgrade release plugin to validate release fail issue + + +<a name="v1.11.0"></a> +## [v1.11.0] - 2022-02-16 +## Maintenance + +* **docs:** FAQ for Kotlin projects +* **docs:** Clarify test config needed for Tracing module ([#735](https://github.com/aws-powertools/powertools-lambda-java/issues/735)) +* **docs:** typo in CHANGELOG.md + + +<a name="v1.10.3"></a> +## [v1.10.3] - 2022-02-01 +## Bug Fixes + +* Prevent message to be marked as success if failed sending to DLQ ([#731](https://github.com/aws-powertools/powertools-lambda-java/issues/731)) +* **gradle:** Fix gradle example and docs to work with java 12+ ([#703](https://github.com/aws-powertools/powertools-lambda-java/issues/703)) + +## Maintenance + +* move core utilities example to aws-samples/aws-lambda-powertools-examples ([#733](https://github.com/aws-powertools/powertools-lambda-java/issues/733)) +* Update readme to refer examples repo +* **ci:** set release env variable for auto closing issue + + +<a name="v1.10.2"></a> +## [v1.10.2] - 2022-01-07 +## Features + +* **tracing:** ability to override object mapper ([#698](https://github.com/aws-powertools/powertools-lambda-java/issues/698)) + +## Maintenance + +* Automate release prep ([#696](https://github.com/aws-powertools/powertools-lambda-java/issues/696)) +* **automation:** find replace pom.xml at all levels +* **automation:** find replace pom.xml at all levels +* **docs:** Add FAQs section to docs with information about Lombok support. ([#680](https://github.com/aws-powertools/powertools-lambda-java/issues/680)) + + +<a name="v1.10.1"></a> +## [v1.10.1] - 2022-01-06 +## Features + +* **ci:** auto-notify & close issues on release + +## Maintenance + +* prep release 1.10.1 +* action to automate release prep +* action to automate release prep +* **docs:** Latest version of aspectj.post-compile-weaving +* **docs:** Full gradle config in example for each module +* **docs:** use free fair gradle aspect plugin ([#679](https://github.com/aws-powertools/powertools-lambda-java/issues/679)) + + +<a name="v1.10.0"></a> +## [v1.10.0] - 2021-12-27 + +<a name="v1.9.1"></a> +## [v1.9.1] - 2021-12-27 +## Bug Fixes + +* support batch size greater than 10 processing ([#667](https://github.com/aws-powertools/powertools-lambda-java/issues/667)) + +## Features + +* Json layout modern implementation ([#670](https://github.com/aws-powertools/powertools-lambda-java/issues/670)) + +## Maintenance + +* prep release 1.10.0 ([#671](https://github.com/aws-powertools/powertools-lambda-java/issues/671)) +* Fix docs layout + + +<a name="v1.8.3"></a> +## [v1.8.3] - 2021-12-21 + +<a name="v1.9.0"></a> +## [v1.9.0] - 2021-12-21 +## Features + +* **tracing:** add service annotation ([#655](https://github.com/aws-powertools/powertools-lambda-java/issues/655)) + +## Maintenance + +* prep release 1.9.0 ([#666](https://github.com/aws-powertools/powertools-lambda-java/issues/666)) +* localise abstract json layout implementation ([#664](https://github.com/aws-powertools/powertools-lambda-java/issues/664)) +* Update edit url prefix on doc +* Update docs to reflect latest gradle plugin fix ([#656](https://github.com/aws-powertools/powertools-lambda-java/issues/656)) + + +<a name="v1.8.2"></a> +## [v1.8.2] - 2021-12-15 +## Maintenance + +* prep release 1.8.2 ([#653](https://github.com/aws-powertools/powertools-lambda-java/issues/653)) + + +<a name="v1.8.1"></a> +## [v1.8.1] - 2021-12-10 +## Documentation + +* **tenets:** update Idiomatic tenet to Progressive ([#615](https://github.com/aws-powertools/powertools-lambda-java/issues/615)) + +## Maintenance + +* prep release 1.8.1 ([#647](https://github.com/aws-powertools/powertools-lambda-java/issues/647)) + + +<a name="v1.8.0"></a> +## [v1.8.0] - 2021-11-05 +## Bug Fixes + +* LoggingAspect precedence to be last for accepting mutated args ([#567](https://github.com/aws-powertools/powertools-lambda-java/issues/567)) + +## Features + +* create Java cfn-response equivalent ([#558](https://github.com/aws-powertools/powertools-lambda-java/issues/558)) ([#560](https://github.com/aws-powertools/powertools-lambda-java/issues/560)) + +## Maintenance + +* prep release 1.8.0 ([#608](https://github.com/aws-powertools/powertools-lambda-java/issues/608)) +* Fix failing build and auto merge only when build is success +* spotbug check ([#565](https://github.com/aws-powertools/powertools-lambda-java/issues/565)) +* Fix/Ignore spotbugs violations + + +<a name="v1.7.3"></a> +## [v1.7.3] - 2021-09-14 +## Bug Fixes + +* reset cold start only when placed on handler ([#508](https://github.com/aws-powertools/powertools-lambda-java/issues/508)) + +## Documentation + +* **batch-processing:** Support for moving non retryable msg to DLQ ([#531](https://github.com/aws-powertools/powertools-lambda-java/issues/531)) + +## Features + +* **batch-processing:** move non retry-able message to DLQ ([#500](https://github.com/aws-powertools/powertools-lambda-java/issues/500)) + +## Maintenance + +* prep release 1.7.3 + + +<a name="v1.7.2"></a> +## [v1.7.2] - 2021-08-03 +## Documentation + +* status badges + +## Maintenance + +* prep release 1.7.2 ([#490](https://github.com/aws-powertools/powertools-lambda-java/issues/490)) +* Upgrade to latest(1.14.0) aspectj-maven-plugin ([#489](https://github.com/aws-powertools/powertools-lambda-java/issues/489)) +* Logging and SQS utility Optimisations ([#484](https://github.com/aws-powertools/powertools-lambda-java/issues/484)) +* wait for merge until check is green +* wait for merge until check is green +* wait for spotbugs check before automerge + + +<a name="v1.7.1"></a> +## [v1.7.1] - 2021-07-06 +## Maintenance + +* prep release 1.7.1 ([#459](https://github.com/aws-powertools/powertools-lambda-java/issues/459)) + + +<a name="v1.7.0"></a> +## [v1.7.0] - 2021-07-05 +## Features + +* Clear logger state ([#453](https://github.com/aws-powertools/powertools-lambda-java/issues/453)) + +## Maintenance + +* prep release 1.7.0 ([#457](https://github.com/aws-powertools/powertools-lambda-java/issues/457)) + + +<a name="v1.6.0"></a> +## [v1.6.0] - 2021-06-20 +## Features + +* [#421](https://github.com/aws-powertools/powertools-lambda-java/issues/421) Support for Boolean and Number type as value in TracingUtils putAnnotation ([#423](https://github.com/aws-powertools/powertools-lambda-java/issues/423)) +* logger-Remove custom keys interface ([#395](https://github.com/aws-powertools/powertools-lambda-java/issues/395)) + +## Maintenance + +* prep release 1.6.0 ([#436](https://github.com/aws-powertools/powertools-lambda-java/issues/436)) +* setup-java v2 ([#353](https://github.com/aws-powertools/powertools-lambda-java/issues/353)) +* add JDK 16 to build matrix ([#367](https://github.com/aws-powertools/powertools-lambda-java/issues/367)) + + +<a name="v1.5.0"></a> +## [v1.5.0] - 2021-03-31 +## Features + +* remove deprecated attributes on Tracing annotation ([#330](https://github.com/aws-powertools/powertools-lambda-java/issues/330)) + +## Maintenance + +* prep release 1.5.0 ([#345](https://github.com/aws-powertools/powertools-lambda-java/issues/345)) +* rename automerge workflow name +* fix auto merge dependabot PR ([#342](https://github.com/aws-powertools/powertools-lambda-java/issues/342)) + + +<a name="v1.4.0"></a> +## [v1.4.0] - 2021-03-11 +## Bug Fixes + +* null check on dimension + +## Features + +* Default dimensions from powertools instead of emf library ([#317](https://github.com/aws-powertools/powertools-lambda-java/issues/317)) + +## Maintenance + +* prep release 1.4.0 ([#324](https://github.com/aws-powertools/powertools-lambda-java/issues/324)) + + +<a name="v1.3.0"></a> +## [v1.3.0] - 2021-03-05 +## Bug Fixes + +* powertools specific log level env var to not conflict with the system LOG_LEVEL ([#306](https://github.com/aws-powertools/powertools-lambda-java/issues/306)) +* **example:** Update the example to v1.2.0 ([#288](https://github.com/aws-powertools/powertools-lambda-java/issues/288)) + +## Documentation + +* ability to override object mapper used for logging event ([#303](https://github.com/aws-powertools/powertools-lambda-java/issues/303)) + +## Features + +* single metric utility method to pick default namespace ([#305](https://github.com/aws-powertools/powertools-lambda-java/issues/305)) +* ability to override object mapper used for logging event ([#302](https://github.com/aws-powertools/powertools-lambda-java/issues/302)) +* respect code guru profile handler implementation ([#304](https://github.com/aws-powertools/powertools-lambda-java/issues/304)) +* capture metrics even when handler results in exception ([#286](https://github.com/aws-powertools/powertools-lambda-java/issues/286)) + +## Maintenance + +* Prep release 1.3.0 ([#316](https://github.com/aws-powertools/powertools-lambda-java/issues/316)) +* migrate docs from gatsby to mkdocs ([#308](https://github.com/aws-powertools/powertools-lambda-java/issues/308)) +* consistent field names for trace and req id with logger ([#309](https://github.com/aws-powertools/powertools-lambda-java/issues/309)) + + +<a name="v1.2.0"></a> +## [v1.2.0] - 2021-01-13 +## Code Refactoring + +* replace Apache Commons Logging with SLF4J ([#212](https://github.com/aws-powertools/powertools-lambda-java/issues/212)) + +## Documentation + +* shadow sidebar to remain expanded ([#208](https://github.com/aws-powertools/powertools-lambda-java/issues/208)) + +## Features + +* support for env variable in tracing capture modes ([#249](https://github.com/aws-powertools/powertools-lambda-java/issues/249)) +* custom segment names ([#221](https://github.com/aws-powertools/powertools-lambda-java/issues/221)) + +## Maintenance + +* Prep release 1.2.0 ([#250](https://github.com/aws-powertools/powertools-lambda-java/issues/250)) +* Consistent env variable names for tracing ([#251](https://github.com/aws-powertools/powertools-lambda-java/issues/251)) +* update docs dependencies ([#214](https://github.com/aws-powertools/powertools-lambda-java/issues/214)) + + +<a name="v1.1.0"></a> +## [v1.1.0] - 2020-12-03 +## Documentation + +* add source code link in nav bar ([#199](https://github.com/aws-powertools/powertools-lambda-java/issues/199)) + +## Features + +* Parameters injection ([#201](https://github.com/aws-powertools/powertools-lambda-java/issues/201)) + +## Maintenance + +* Prep release 1.1.0 ([#205](https://github.com/aws-powertools/powertools-lambda-java/issues/205)) + + +<a name="v1.0.1"></a> +## [v1.0.1] - 2020-11-26 +## Bug Fixes + +* fixing dependencies security issues ([#169](https://github.com/aws-powertools/powertools-lambda-java/issues/169)) + +## Maintenance + +* Prep for release (1.0.1) ([#198](https://github.com/aws-powertools/powertools-lambda-java/issues/198)) +* upgrade AspectjGradlePlugin to latest and example project to java 11 ([#189](https://github.com/aws-powertools/powertools-lambda-java/issues/189)) + +## Performance Improvements + +* Make UrlConnectionHttpClient default client for params fetch ([#185](https://github.com/aws-powertools/powertools-lambda-java/issues/185)) + + +<a name="v1.0.0"></a> +## [v1.0.0] - 2020-11-04 +## Bug Fixes + +* **docs:** Correct url and not for gradle ([#158](https://github.com/aws-powertools/powertools-lambda-java/issues/158)) + +## Code Refactoring + +* Rename helpers for validation module ([#167](https://github.com/aws-powertools/powertools-lambda-java/issues/167)) +* Rename annotations for GA ([#165](https://github.com/aws-powertools/powertools-lambda-java/issues/165)) + + +<a name="v0.6.0-beta"></a> +## [v0.6.0-beta] - 2020-10-27 +## Documentation + +* Update all the environment variables used ([#127](https://github.com/aws-powertools/powertools-lambda-java/issues/127)) + +## Features + +* log aws request id ([#133](https://github.com/aws-powertools/powertools-lambda-java/issues/133)) + +## Maintenance + +* Prepare for 0.6.0-beta ([#155](https://github.com/aws-powertools/powertools-lambda-java/issues/155)) +* gradle example ([#147](https://github.com/aws-powertools/powertools-lambda-java/issues/147)) + + +<a name="v0.5.0-beta"></a> +## [v0.5.0-beta] - 2020-10-06 +## Features + +* SQS Partial batch Utility ([#120](https://github.com/aws-powertools/powertools-lambda-java/issues/120)) + +## Maintenance + +* Prepare for 0.5.0-beta ([#124](https://github.com/aws-powertools/powertools-lambda-java/issues/124)) + + +<a name="v0.4.0-beta"></a> +## [v0.4.0-beta] - 2020-10-02 +## Bug Fixes + +* Log event via object mapper and not depend on toString ([#113](https://github.com/aws-powertools/powertools-lambda-java/issues/113)) + +## Features + +* integration with CloudWatch ServiceLens [#88](https://github.com/aws-powertools/powertools-lambda-java/issues/88) ([#111](https://github.com/aws-powertools/powertools-lambda-java/issues/111)) + + +<a name="v0.3.1-beta"></a> +## [v0.3.1-beta] - 2020-09-25 +## Bug Fixes + +* Removing Log4J dependencies from the tracing module. ([#106](https://github.com/aws-powertools/powertools-lambda-java/issues/106)) +* Removing v1 Java SDK dependencies for X-Ray ([#105](https://github.com/aws-powertools/powertools-lambda-java/issues/105)) + +## Documentation + +* fixes to the documentation ([#102](https://github.com/aws-powertools/powertools-lambda-java/issues/102)) + + +<a name="v0.3.0-beta"></a> +## [v0.3.0-beta] - 2020-09-22 +## Features + +* Metrics utility ([#91](https://github.com/aws-powertools/powertools-lambda-java/issues/91)) + + +<a name="v0.2.0-beta"></a> +## [v0.2.0-beta] - 2020-09-01 +## Bug Fixes + +* **general:** clean up typos and code ([#62](https://github.com/aws-powertools/powertools-lambda-java/issues/62)) + +## Documentation + +* Readme update ([#72](https://github.com/aws-powertools/powertools-lambda-java/issues/72)) +* Update SQS large payload docs ([#60](https://github.com/aws-powertools/powertools-lambda-java/issues/60)) +* fixing documentation ([#59](https://github.com/aws-powertools/powertools-lambda-java/issues/59)) + +## Features + +* Utility without annotation ([#61](https://github.com/aws-powertools/powertools-lambda-java/issues/61)) +* SQS Large message handling ([#55](https://github.com/aws-powertools/powertools-lambda-java/issues/55)) + + +<a name="v0.1.0-beta"></a> +## v0.1.0-beta - 2020-08-31 +## Bug Fixes + +* Fixing security issues on package.json dependencies ([#22](https://github.com/aws-powertools/powertools-lambda-java/issues/22)) +* Fixing package versions for security purpose ([#16](https://github.com/aws-powertools/powertools-lambda-java/issues/16)) +* Rename Java ArtifactId and GroupId to be compliant with new AWS standard. +* docs pipeline +* Fix Readme.md documentation and remove unecessary parts. + +## Code Refactoring + +* consistent naming of powertools tracing and initial docs. ([#48](https://github.com/aws-powertools/powertools-lambda-java/issues/48)) +* consistent naming of powertools. ([#46](https://github.com/aws-powertools/powertools-lambda-java/issues/46)) +* Split Tracing and Logging packages Dependency split ([#45](https://github.com/aws-powertools/powertools-lambda-java/issues/45)) +* move groupId from software.aws.lambda to softwarte.amazon.lambda ([#23](https://github.com/aws-powertools/powertools-lambda-java/issues/23)) + +## Documentation + +* Adding license to each source file ([#44](https://github.com/aws-powertools/powertools-lambda-java/issues/44)) +* Initial javadocs for PowerLogger class. ([#43](https://github.com/aws-powertools/powertools-lambda-java/issues/43)) +* Implement Logger and Tracer part ([#27](https://github.com/aws-powertools/powertools-lambda-java/issues/27)) +* Initial javadocs for our logging annotation. ([#40](https://github.com/aws-powertools/powertools-lambda-java/issues/40)) +* update maven artifactId and groupId to new one +* Improving README file +* Init project documentation ci: init Github actions flow + +## Pull Requests + +* Merge pull request [#1](https://github.com/aws-powertools/powertools-lambda-java/issues/1) from stevehouel/master + + +[Unreleased]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.4.0...HEAD +[v2.4.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.3.0...v2.4.0 +[v2.3.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.2.1...v2.3.0 +[v2.2.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.2.0...v2.2.1 +[v2.2.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.1.1...v2.2.0 +[v2.1.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.1.0...v2.1.1 +[v2.1.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.0.0...v2.1.0 +[v2.0.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.0.0-RC1...v2.0.0 +[v2.0.0-RC1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.20.2...v2.0.0-RC1 +[v1.20.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.20.1...v1.20.2 +[v1.20.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.20.0...v1.20.1 +[v1.20.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.19.0...v1.20.0 +[v1.19.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.18.0...v1.19.0 +[v1.18.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.17.0...v1.18.0 +[v1.17.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.16.1...v1.17.0 +[v1.16.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.16.0...v1.16.1 +[v1.16.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.15.0...v1.16.0 +[v1.15.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.14.0...v1.15.0 +[v1.14.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.13.0...v1.14.0 +[v1.13.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.3...v1.13.0 +[v1.12.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.2...v1.12.3 +[v1.12.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.1...v1.12.2 +[v1.12.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.0...v1.12.1 +[v1.12.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.11.0...v1.12.0 +[v1.11.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.3...v1.11.0 +[v1.10.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.2...v1.10.3 +[v1.10.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.1...v1.10.2 +[v1.10.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.0...v1.10.1 +[v1.10.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.9.1...v1.10.0 +[v1.9.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.3...v1.9.1 +[v1.8.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.9.0...v1.8.3 +[v1.9.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.2...v1.9.0 +[v1.8.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.1...v1.8.2 +[v1.8.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.0...v1.8.1 +[v1.8.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.3...v1.8.0 +[v1.7.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.2...v1.7.3 +[v1.7.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.1...v1.7.2 +[v1.7.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.0...v1.7.1 +[v1.7.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.6.0...v1.7.0 +[v1.6.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.5.0...v1.6.0 +[v1.5.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.4.0...v1.5.0 +[v1.4.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.3.0...v1.4.0 +[v1.3.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.2.0...v1.3.0 +[v1.2.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.1.0...v1.2.0 +[v1.1.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.0.1...v1.1.0 +[v1.0.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.0.0...v1.0.1 +[v1.0.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.6.0-beta...v1.0.0 +[v0.6.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.5.0-beta...v0.6.0-beta +[v0.5.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.4.0-beta...v0.5.0-beta +[v0.4.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.3.1-beta...v0.4.0-beta +[v0.3.1-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.3.0-beta...v0.3.1-beta +[v0.3.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.2.0-beta...v0.3.0-beta +[v0.2.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.1.0-beta...v0.2.0-beta diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8db303737..080643027 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,6 +21,9 @@ Thank you for your interest in contributing to our project. Whether it's a [bug report](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=bug%2C+triage&projects=&template=bug_report.md&title=Bug%3A+TITLE), [new feature](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=feature-request%2C+triage&projects=&template=feature_request.md&title=Feature+request%3A+TITLE) or [additional documentation](https://github.com/aws-powertools/powertools-lambda-java/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 --> +We encourage contributions from the community and we will work with contributors to merge their pull requests. +Rarely, we may close pull requests that do not meet our guidelines specified in CONTRIBUTING.md, or will require unreasonable effort to meet our quality bar. + 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. @@ -55,8 +58,7 @@ We strongly recommend installing the CheckStyle-IDEA plugin and apply the provid 2. After installing the plugin, open the preferences (`⌘,` on macOS, or `Ctrl+Alt+S` on Windows/Linux) and search for _Code Style_. Click on the gear icon near the scheme and import checkstyle configuration. Click on "Apply" and "OK". ![](docs/media/intellij_checkstyle_1.png) -3. Select the code you've created (module, package, class) and reformat code: `⌘⌥L` (macOS), or `Ctrl+Alt+L` (Windows/Linux): -![](docs/media/intellij_checkstyle_2.png) +3. Select the code you've created (module, package, class) and reformat code: `⌘⌥L` (macOS), or `Ctrl+Alt+L` (Windows/Linux). 4. Apply the reformat, optimize imports, rearrange and cleanup to your code and only to java files: ![](docs/media/intellij_checkstyle_3.png) diff --git a/GraalVM.md b/GraalVM.md new file mode 100644 index 000000000..bbddb5e3b --- /dev/null +++ b/GraalVM.md @@ -0,0 +1,86 @@ +# GraalVM Compatibility for AWS Lambda Powertools Java + +## Table of Contents +- [Overview](#overview) +- [Prerequisites](#prerequisites) +- [General Implementation Steps](#general-implementation-steps) +- [Known Issues and Solutions](#known-issues-and-solutions) +- [Reference Implementation](#reference-implementation) + +## Overview +This documentation provides guidance for adding GraalVM support for AWS Lambda Powertools Java modules and using the modules in Lambda functions. + +## Prerequisites +- GraalVM 21+ installation +- Maven 3.x + +## General Implementation Steps +GraalVM native image compilation requires complete knowledge of an application's dynamic features at build time. The GraalVM reachability metadata (GRM) JSON files are essential because Java applications often use features that are determined at runtime, such as reflection, dynamic proxy classes, resource loading, and JNI (Java Native Interface). The metadata files tell GraalVM which classes need reflection access, which resources need to be included in the native image, and which proxy classes need to be generated. + +In order to generate the metadata reachability files for Powertools for Lambda, follow these general steps. + +1. **Add Maven Profiles** + - Add profile for generating GraalVM reachability metadata files. You can find an example of this in profile `generate-graalvm-files` of this [pom.xml](powertools-common/pom.xml). + - Add another profile for running the tests in the native image. You can find and example of this in profile `graalvm-native` of this [pom.xml](powertools-common/pom.xml). + +2. **Generate Reachability Metadata** + - Set the `JAVA_HOME` environment variable to use GraalVM + - Run tests with `-Pgenerate-graalvm-files` profile. +```shell +mvn -Pgenerate-graalvm-files clean test +``` + +3. **Validate Native Image Tests** + - Set the `JAVA_HOME` environment variable to use GraalVM + - Run tests with `-Pgraalvm-native` profile. This will build a GraalVM native image and run the JUnit tests. +```shell +mvn -Pgraalvm-native clean test +``` + +4. **Clean Up Metadata** + - GraalVM metadata reachability files generated in Step 2 contains references to the test scoped dependencies as well. + - Remove the references in generated metadata files for the following (and any other references to test scoped resources and classes): + - JUnit + - Mockito + - ByteBuddy + +## Known Issues and Solutions +1. **Mockito Compatibility** + - Powertools uses Mockito 5.x which uses “inline mock maker” as the default. This mock maker does not play well with GraalVM. Mockito [recommends](https://github.com/mockito/mockito/releases/tag/v5.0.0) using subclass mock maker with GraalVM. Therefore `generate-graalvm-files` profile uses subclass mock maker instead of inline mock maker. + - Subclass mock maker does not support testing static methods. Tests have therefore been modified to use [JUnit Pioneer](https://junit-pioneer.org/docs/environment-variables/) to inject the environment variables in the scope of the test's execution. + +2. **Log4j Compatibility** + - Version 2.22.1 fails with this error +``` +java.lang.InternalError: com.oracle.svm.core.jdk.UnsupportedFeatureError: Defining hidden classes at runtime is not supported. +``` + - This has been [fixed](https://github.com/apache/logging-log4j2/discussions/2364#discussioncomment-8950077) in Log4j 2.24.x. PT has been updated to use this version of Log4j + +3. **Test Class Organization** + - **Issue**: Anonymous inner classes and lambda expressions in Mockito matchers cause `NoSuchMethodError` in GraalVM native tests + - **Solution**: + - Extract static inner test classes to separate concrete classes in the same package as the class under test + - Replace lambda expressions in `ArgumentMatcher` with concrete implementations + - Use `mockito-subclass` dependency in GraalVM profiles + - **Example**: Replace `argThat(resp -> resp.getStatus() != expectedStatus)` with: + ```java + argThat(new ArgumentMatcher<Response>() { + @Override + public boolean matches(Response resp) { + return resp != null && resp.getStatus() != expectedStatus; + } + }) + ``` + +4. **Package Visibility Issues** + - **Issue**: Test handler classes cannot access package-private methods when placed in subpackages + - **Solution**: Place test handler classes in the same package as the class under test, not in subpackages like `handlers/` + - **Example**: Use `software.amazon.lambda.powertools.cloudformation` instead of `software.amazon.lambda.powertools.cloudformation.handlers` + +5. **Test Stubs Best Practice** + - **Best Practice**: Avoid mocking where possible and use concrete test stubs provided by `powertools-common` package + - **Solution**: Use `TestLambdaContext` and other test stubs from `powertools-common` test-jar instead of Mockito mocks + - **Implementation**: Add `powertools-common` test-jar dependency and replace `mock(Context.class)` with `new TestLambdaContext()` + +## Reference Implementation +Working example is available in the [examples](examples/powertools-examples-core-utilities/sam-graalvm). diff --git a/Priming.md b/Priming.md new file mode 100644 index 000000000..d7597ad8c --- /dev/null +++ b/Priming.md @@ -0,0 +1,59 @@ +# Automatic Priming for AWS Lambda Powertools Java + +## Table of Contents +- [Overview](#overview) +- [Implementation Steps](#general-implementation-steps) +- [Known Issues](#known-issues-and-solutions) +- [Reference Implementation](#reference-implementation) + +## Overview +Priming is the process of preloading dependencies and initializing resources during the INIT phase, rather than during the INVOKE phase to further optimize startup performance with SnapStart. +This is required because Java frameworks that use dependency injection load classes into memory when these classes are explicitly invoked, which typically happens during Lambda’s INVOKE phase. + +This documentation provides guidance for automatic class priming in Powertools for AWS Lambda Java modules. + + +## Implementation Steps +Classes are proactively loaded using Java runtime hooks which are part of the open source [CRaC (Coordinated Restore at Checkpoint) project](https://openjdk.org/projects/crac/). +Implementations across the project use the `beforeCheckpoint()` hook, to prime Snapstart-enabled Java functions via Class Priming. +In order to generate the `classloaded.txt` file for a Java module in this project, follow these general steps. + +1. **Add Maven Profile** + - Add maven test profile with the following VM argument for generating classes loaded files. + ```shell + -Xlog:class+load=info:classesloaded.txt + ``` + - You can find an example of this in `generate-classesloaded-file` profile in this [pom.xml](powertools-metrics/pom.xml). + +2. **Generate classes loaded file** + - Run tests with `-Pgenerate-classesloaded-file` profile. + ```shell + mvn -Pgenerate-classesloaded-file clean test + ``` + - This will generate a file named `classesloaded.txt` in the target directory of the module. + +3. **Cleanup the file** + - The classes loaded file generated in Step 2 has the format + `[0.054s][info][class,load] java.lang.Object source: shared objects file` + but we are only interested in `java.lang.Object` - the fully qualified class name. + - To strip the lines to include only the fully qualified class name, + Use the following regex to replace with empty string. + - `^\[[\[\]0-9.a-z,]+ ` (to replace the left part) + - `( source: )[0-9a-z :/._$-]+` (to replace the right part) + +4. **Add file to resources** + - Move the cleaned-up file to the corresponding `src/main/resources` directory of the module. See [example](powertools-metrics/src/main/resources/classesloaded.txt). + +5. **Register and checkpoint** + - A class, usually the entry point of the module, should register the CRaC resource in the constructor. [Example](powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java) + - Note that AspectJ aspect is not suitable for this purpose, as it does not work with CRaC. + - Add the `beforeCheckpoint()` hook in the same class to invoke `ClassPreLoader.preloadClasses()`. The `ClassPreLoader` class is implemented in `powertools-common` module. + - This will ensure that the classes are already pre-loaded by the Snapstart RESTORE operation leading to a shorter INIT duration. + + +## Known Issues +- This is a manual process at the moment, but it can be automated in the future. +- `classesloaded.txt` file includes test classes as well because the file is generated while running tests. This is not a problem because all the classes that are not found are ignored by `ClassPreLoader.preloadClasses()`. Also `beforeCheckpoint()` hook is not time-sensitive, it only runs once when a new Lambda version gets published. + +## Reference Implementation +Working example is available in the [powertools-metrics](powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java). diff --git a/README.md b/README.md index 70cfab314..4c02e2d1f 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,47 @@ # Powertools for AWS Lambda (Java) -![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/aws-powertools/powertools-lambda-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) [![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-java/branch/main/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-java) +![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=aws-powertools_powertools-lambda-java&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=aws-powertools_powertools-lambda-java) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=aws-powertools_powertools-lambda-java&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=aws-powertools_powertools-lambda-java) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-java/badge)](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-java) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) [![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-java/branch/main/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-java) Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. > Also available in [Python](https://github.com/aws-powertools/powertools-lambda-python), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet). -**[📜Documentation](https://docs.powertools.aws.dev/lambda-java/)** | **[Feature request](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=feature-request%2C+triage&template=feature_request.md&title=)** | **[🐛Bug Report](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=bug%2C+triage&template=bug_report.md&title=)** | **[Detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-aws-lambda-powertools-java/)** +**[📜Documentation](https://docs.powertools.aws.dev/lambda-java/latest)** | **[Feature request](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=feature_request.yml)** | **[🐛Bug Report](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=bug_report.yml)** | **[Detailed blog post](https://aws.amazon.com/blogs/compute/introducing-v2-of-powertools-for-aws-lambda-java/)** -### Installation +## Installation Powertools for AWS Lambda (Java) is available in Maven Central. You can use your favourite dependency management tool to install it -#### Maven: +### Maven: ```xml <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.16.1</version> + <version>2.9.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>1.16.1</version> + <artifactId>powertools-logging-log4j</artifactId> + <version>2.9.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.16.1</version> + <version>2.9.0</version> </dependency> ... </dependencies> ``` -Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project. A different configuration is needed for projects on Java 8. +Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project. <details> - <summary><b>Maven - Java 11 and newer</b></summary> + <summary><b>Maven</b></summary> ```xml <build> @@ -48,11 +50,11 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> + <source>11</source> + <target>11</target> + <complianceLevel>11</complianceLevel> <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> @@ -68,6 +70,14 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -83,58 +93,21 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam </details> <details> -<summary><b>Maven - Java 8</b></summary> - -```xml -<build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> -</build> -``` -</details> - -<details> -<summary><b>Gradle - Java 11+</b></summary> +<summary><b>Gradle</b></summary> ```groovy + plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.2.2' } + // the freefair aspect plugins targets gradle 8.2.1 + // https://docs.freefair.io/gradle-plugins/8.2.2/reference/ + wrapper { + gradleVersion = "8.2.1" + } + repositories { mavenCentral() } @@ -143,6 +116,8 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' + implementation 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' + implementation "org.aspectj:aspectjrt:1.9.22" } sourceCompatibility = 11 @@ -150,33 +125,37 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ``` </details> -<details> -<summary><b>Gradle - Java 8</b></summary> -```groovy - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' - aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' - aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 +### Java Compatibility +Powertools for AWS Lambda (Java) supports all Java versions from 11 to 25 in line with the [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + +For the modules that provide annotations, Powertools for AWS Lambda (Java) leverages the **aspectj** library. +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: + +```xml +<dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>1.9.??</version> +</dependency> ``` + +<details> + <summary><b>JDK - aspectj dependency matrix</b></summary> + +Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/release/JavaVersionCompatibility.adoc) to understand which AspectJ version to use based on your JDK version: + +| JDK version | aspectj version | +|-------------|------------------------| +| `11-17` | `1.9.20.1` (or higher) | +| `21` | `1.9.21` (or higher) | +| `25` | `1.9.25` (or higher) | + </details> ## Examples -See the **[examples](examples)** directory for example projects showcasing usage of different utilities. +See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/main/examples)** for example projects showcasing usage of different utilities. Have a demo project to contribute which showcase usage of different utilities from powertools? We are happy to accept it [here](CONTRIBUTING.md#security-issue-notifications). @@ -184,23 +163,19 @@ Have a demo project to contribute which showcase usage of different utilities fr ### 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 (Java), you can request to have your name and logo added to the README file by raising a [Support Powertools for AWS Lambda (Java) (become a reference)](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=customer-reference&template=support_powertools.yml&title=%5BSupport+Lambda+Powertools%5D%3A+%3Cyour+organization+name%3E) issue. +Knowing which companies are using this library is important to help prioritize the project internally. If your company is using Powertools for AWS Lambda (Java), you can request to have your name and logo added to the README file by raising a [Support Powertools for AWS Lambda (Java) (become a reference)](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=support_powertools.yml) issue. The following companies, among others, use Powertools: * [Capital One](https://www.capitalone.com/) * [CPQi (Exadel Financial Services)](https://cpqi.com/) * [Europace AG](https://europace.de/) - -## Credits - -* [MkDocs](https://www.mkdocs.org/) -* [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) +* [Vertex Pharmaceuticals](https://www.vrtx.com/) ## Connect -* **Powertools for AWS Lambda on Discord**: `#java` - **[Invite link](https://discord.gg/B8zZKbbyET)** -* **Email**: <aws-lambda-powertools-feedback@amazon.com> +- **Powertools for AWS Lambda on Discord**: `#java` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: <aws-powertools-maintainers@amazon.com> ## Security disclosures @@ -208,4 +183,4 @@ If you think you’ve found a potential security issue, please do not post it in ## License -This library is licensed under the Apache License, Version 2.0. See the LICENSE file. +This library is licensed under the MIT-0 License. See the [LICENSE](https://github.com/aws-powertools/powertools-lambda-java/blob/main/LICENSE) file. diff --git a/checkstyle.xml b/checkstyle.xml index 34ef98ef2..680d8808c 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -51,7 +51,7 @@ <module name="FileLength"/> <module name="Header"> - <property name="headerFile" value="license-header"/> + <property name="headerFile" value="${basedir}/license-header"/> <property name="severity" value="error"/> </module> diff --git a/docs/Dockerfile b/docs/Dockerfile index 1524933ab..56c4c19ea 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,2 +1,4 @@ -FROM squidfunk/mkdocs-material -RUN pip install mkdocs-git-revision-date-plugin mkdocs-macros-plugin \ No newline at end of file +FROM squidfunk/mkdocs-material@sha256:980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 + +COPY requirements.txt /tmp/ +RUN pip install --require-hashes -r /tmp/requirements.txt diff --git a/docs/FAQs.md b/docs/FAQs.md index 99ef40905..cea4b774f 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -3,16 +3,17 @@ title: FAQs description: Frequently Asked Questions --- - ## How can I use Powertools for AWS Lambda (Java) with Lombok? -Poweretools uses `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. In case you want to use `Lombok` or other compile-time preprocessor for your project, it is required to change `aspectj-maven-plugin` configuration to enable in-place weaving feature. Otherwise the plugin will ignore changes introduced by `Lombok` and will use `.java` files as a source. +Many utilities in this library use `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. In case you want to use `Lombok` or other compile-time preprocessor for your project, it is required to change `aspectj-maven-plugin` configuration to enable in-place weaving feature. Otherwise the plugin will ignore changes introduced by `Lombok` and will use `.java` files as a source. + +Alternatively, you can use the [functional approach](./usage-patterns.md#functional-approach) which does not require AspectJ configuration. To enable in-place weaving feature you need to use following `aspectj-maven-plugin` configuration: ```xml hl_lines="2-6" <configuration> - <forceAjcCompile>true</forceAjcCompile> + <forceAjcCompile>true</forceAjcCompile> <sources/> <weaveDirectories> <weaveDirectory>${project.build.directory}/classes</weaveDirectory> @@ -29,14 +30,16 @@ To enable in-place weaving feature you need to use following `aspectj-maven-plug ## How can I use Powertools for AWS Lambda (Java) with Kotlin projects? -Poweretools uses `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. When using it with Kotlin projects, it is required to `forceAjcCompile`. -No explicit configuration should be required for gradle projects. +Many utilities use `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. When using it with Kotlin projects, it is required to `forceAjcCompile`. +No explicit configuration should be required for gradle projects. + +Alternatively, you can use the [functional approach](./usage-patterns.md#functional-approach) which does not require AspectJ configuration. To enable `forceAjcCompile` you need to use following `aspectj-maven-plugin` configuration: ```xml hl_lines="2" <configuration> - <forceAjcCompile>true</forceAjcCompile> + <forceAjcCompile>true</forceAjcCompile> ... <aspectLibraries> <aspectLibrary> @@ -47,3 +50,212 @@ To enable `forceAjcCompile` you need to use following `aspectj-maven-plugin` con </configuration> ``` +## How can I use Powertools for AWS Lambda (Java) with the AWS CRT HTTP Client? + +Utilities relying on AWS SDK clients use the `url-connection-client` as the default HTTP client. The `url-connection-client` is a lightweight HTTP client, which keeps the impact on Lambda cold starts to a minimum. +With the [announcement](https://aws.amazon.com/blogs/developer/announcing-availability-of-the-aws-crt-http-client-in-the-aws-sdk-for-java-2-x/) of the `aws-crt-client` a new HTTP client has been released, which offers faster SDK startup time and smaller memory footprint. + +Unfortunately, replacing the `url-connection-client` dependency with the `aws-crt-client` will not immediately improve the lambda cold start performance and memory footprint, +as the default version of the dependency contains native system libraries for all supported runtimes and architectures (Linux, MacOS, Windows, AMD64, ARM64, etc). This makes the CRT client portable, without the user having to consider _where_ their code will run, but comes at the cost of JAR size. + +### Configuring dependencies + +Using the `aws-crt-client` in your project requires the exclusion of the `url-connection-client` transitive dependency from the `powertools-*` dependency. + +```xml +<dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + <version>2.0.0</version> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>url-connection-client</artifactId> + </exclusion> + </exclusions> +</dependency> +``` + +Next, add the `aws-crt-client` and exclude the "generic" `aws-crt` dependency (contains all runtime libraries). +Instead, set a specific classifier of the `aws-crt` to use the one for your target runtime: either `linux-x86_64` for a Lambda configured for x86 or `linux-aarch_64` for Lambda using arm64. + +!!! note "You will need to add a separate maven profile to build and debug locally when your development environment does not share the target architecture you are using in Lambda." +By specifying the specific target runtime, we prevent other target runtimes from being included in the jar file, resulting in a smaller Lambda package and improved cold start times. + +```xml +<dependencies> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>aws-crt-client</artifactId> + <version>2.23.21</version> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk.crt</groupId> + <artifactId>aws-crt</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>software.amazon.awssdk.crt</groupId> + <artifactId>aws-crt</artifactId> + <version>0.29.9</version> + <classifier>linux-x86_64</classifier> + </dependency> +</dependencies> +``` + +### Explicitly set the AWS CRT HTTP Client + +After configuring the dependencies, it's required to explicitly specify the AWS SDK HTTP client. +Depending on the utility you are using, there is a different way to configure the SDK client. + +The following example shows how to use the Parameters module while leveraging the AWS CRT Client. + +```java hl_lines="16 23-24" +import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.ssm.SsmClient; +import software.amazon.awssdk.http.crt.AwsCrtHttpClient; +import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; + +public class RequestHandlerWithParams implements RequestHandler<String, String> { + + // Get an instance of the SSMProvider with a custom HTTP client (aws crt). + SSMProvider ssmProvider = SSMProvider + .builder() + .withClient( + SsmClient.builder() + .httpClient(AwsCrtHttpClient.builder().build()) + .build() + ) + .build(); + + public String handleRequest(String input, Context context) { + // Retrieve a single param + String value = ssmProvider + .get("/my/secret"); + // We might instead want to retrieve multiple parameters at once, returning a Map of key/value pairs + // .getMultiple("/my/secret/path"); + + // Return the result + return value; + } +} +``` + +The `aws-crt-client` was considered for adoption as the default HTTP client in Powertools for AWS Lambda (Java) as mentioned in [Move SDK http client to CRT](https://github.com/aws-powertools/powertools-lambda-java/issues/1092), +but due to the impact on the developer experience it was decided to stick with the `url-connection-client`. + +## How can I use Powertools for AWS Lambda (Java) with GraalVM? + +Core utilities, i.e. [logging](./core/logging.md), [metrics](./core/metrics.md) and [tracing](./core/tracing.md), include the [GraalVM Reachability Metadata (GRM)](https://www.graalvm.org/latest/reference-manual/native-image/metadata/) in the `META-INF` directories of the respective JARs. You can find a working example of Serverless Application Model (SAM) based application in the [examples](../examples/powertools-examples-core-utilities/sam-graalvm/README.md) directory. + +Below, you find typical steps you need to follow in a Maven based Java project: + +### Set the environment to use GraalVM + +```shell +export JAVA_HOME=<path to GraalVM> +``` + +### Use log4j `>2.24.0` + +Log4j version `2.24.0` adds [support for GraalVM](https://github.com/apache/logging-log4j2/issues/1539#issuecomment-2106766878). Depending on your project's dependency hierarchy, older version of log4j might be included in the final dependency graph. Make sure version `>2.24.0` of these dependencies are used by your Maven project: + +```xml +<dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-layout-template-json</artifactId> + <version>${log4j.version}</version> + </dependency> +</dependencies> + +``` + +### Add the AWS Lambda Java Runtime Interface Client dependency + +The Runtime Interface Client allows your function to receive invocation events from Lambda, send the response back to Lambda, and report errors to the Lambda service. Add the below dependency to your Maven project: + +```xml +<dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.1.1</version> +</dependency> +``` + +Also include the AWS Lambda GRM files by copying the `com.amazonaws` [directory](../examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/) in your project's `META-INF/native-image` directory + +### Build the native image + +Use the `native-maven-plugin` to build the native image. You can do this by adding the plugin to your `pom.xml` and creating a build profile called `native-image` that can build the native image of your Lambda function: + +```xml +<profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.1</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>your-project-name</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <!-- required for AWS Lambda Runtime Interface Client --> + <arg>--enable-url-protocols=http</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> +</profiles> +``` + +Create a Docker image using a `Dockerfile` like [this](../examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile) to create an x86 based build image. + +```shell +docker build --platform linux/amd64 . -t your-org/your-app-graalvm-builder +``` + +Create the native image of you Lambda function using the Docker command below. + +```shell +docker run --platform linux/amd64 -it -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 your-org/your-app-graalvm-builder mvn clean -Pnative-image package + +``` + +The native image is created in the `target/` directory. diff --git a/docs/core/logging.md b/docs/core/logging.md index 09714a512..8358087d2 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -5,21 +5,37 @@ description: Core utility Logging provides an opinionated logger with output structured as JSON. -**Key features** +## Key features -* Capture key fields from Lambda context, cold start and structures logging output as JSON -* Log Lambda event when instructed, disabled by default, can be enabled explicitly via annotation param -* Append additional keys to structured log at any point in time +* Leverages standard logging libraries: [_SLF4J_](https://www.slf4j.org/){target="_blank"} as the API, and [_log4j2_](https://logging.apache.org/log4j/2.x/){target="_blank"} or [_logback_](https://logback.qos.ch/){target="_blank"} for the implementation +* Captures key fields from Lambda context, cold start and structures logging output as JSON +* Optionally logs Lambda request +* Optionally logs Lambda response +* Optionally supports log sampling by including a configurable percentage of DEBUG logs in logging output +* Optionally supports buffering lower level logs and flushing them on error or manually +* Allows additional keys to be appended to the structured log at any point in time +* GraalVM support -## Install -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. +## Getting started -=== "Maven Java 11+" +???+ tip + You can find complete examples in the [project repository](https://github.com/aws-powertools/powertools-lambda-java/tree/v2/examples/powertools-examples-core-utilities){target="_blank"}. - ```xml hl_lines="3-7 16 18 24-27" +### Installation +Depending on preference, you must choose to use either _log4j2_ or _logback_ as your log provider. If you use the AspectJ annotation approach, you must configure _aspectj_ to weave the code and make sure the annotation is processed. If you prefer the [functional approach](../usage-patterns.md#functional-approach), AspectJ configuration is not required. + +#### Maven +=== "log4j2" + + ```xml hl_lines="3-12 30-33" <dependencies> ... + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>{{ powertools.version }}</version> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> @@ -29,13 +45,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> <build> <plugins> ... <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>11</source> <!-- or higher --> <target>11</target> <!-- or higher --> @@ -47,6 +64,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -60,11 +85,16 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </build> ``` -=== "Maven Java 1.8" +=== "logback" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-12 30-33" <dependencies> ... + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-logback</artifactId> + <version>{{ powertools.version }}</version> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> @@ -74,17 +104,18 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> <build> <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.14</version> <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> + <source>11</source> <!-- or higher --> + <target>11</target> <!-- or higher --> + <complianceLevel>11</complianceLevel> <!-- or higher --> <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> @@ -92,6 +123,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -105,12 +144,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </build> ``` -=== "Gradle Java 11+" +#### Gradle + +=== "log4j2" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11-12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -118,19 +159,20 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl } dependencies { - aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' } sourceCompatibility = 11 targetCompatibility = 11 ``` -=== "Gradle Java 1.8" +=== "logback" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11-12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -138,27 +180,62 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl } dependencies { - aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-logging-logback:{{ powertools.version }}' } - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 11 + targetCompatibility = 11 ``` -## Initialization +### Configuration -Powertools for AWS Lambda (Java) extends the functionality of Log4J. Below is an example `#!xml log4j2.xml` file, with the `JsonTemplateLayout` using `#!json LambdaJsonLayout.json` configured. +#### Main environment variables -!!! info "LambdaJsonLayout is now deprecated" +The logging module requires two settings: - Configuring utiltiy using `<LambdaJsonLayout/>` plugin is deprecated now. While utility still supports the old configuration, we strongly recommend upgrading the - `log4j2.xml` configuration to `JsonTemplateLayout` instead. [JsonTemplateLayout](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html) is recommended way of doing structured logging. - - Please follow [this guide](#upgrade-to-jsontemplatelayout-from-deprecated-lambdajsonlayout-configuration-in-log4j2xml) for upgrade steps. +| Environment variable | Setting | Description | +|---------------------------|-------------------|-------------------------------------------------------------------------------------------------------------| +| `POWERTOOLS_LOG_LEVEL` | **Logging level** | Sets how verbose Logger should be. If not set, will use the [Logging configuration](#logging-configuration) | +| `POWERTOOLS_SERVICE_NAME` | **Service** | Sets service key that will be included in all log statements (Default is `service_undefined`) | + +Here is an example using AWS Serverless Application Model (SAM): + +=== "template.yaml" +``` yaml hl_lines="10 11" +Resources: + PaymentFunction: + Type: AWS::Serverless::Function + Properties: + MemorySize: 512 + Timeout: 20 + Runtime: java17 + Environment: + Variables: + POWERTOOLS_LOG_LEVEL: WARN + POWERTOOLS_SERVICE_NAME: payment +``` + +There are some other environment variables which can be set to modify Logging's settings at a global scope: + +| Environment variable | Type | Description | +|---------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------| +| `POWERTOOLS_LOGGER_SAMPLE_RATE` | float | Configure the sampling rate at which `DEBUG` logs should be included. See [sampling rate](#sampling-debug-logs) | +| `POWERTOOLS_LOGGER_LOG_EVENT` | boolean | Specify if the incoming Lambda event should be logged. See [Logging event](#logging-incoming-event) | +| `POWERTOOLS_LOGGER_LOG_RESPONSE` | boolean | Specify if the Lambda response should be logged. See [logging response](#logging-handler-response) | +| `POWERTOOLS_LOGGER_LOG_ERROR` | boolean | Specify if a Lambda uncaught exception should be logged. See [logging exception](#logging-handler-uncaught-exception ) | + +#### Logging configuration + +Powertools for AWS Lambda (Java) simply extends the functionality of the underlying library you choose (_log4j2_ or _logback_). +You can leverage the standard configuration files (_log4j2.xml_ or _logback.xml_): === "log4j2.xml" + With log4j2, we leverage the [`JsonTemplateLayout`](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html){target="_blank"} + to provide structured logging. A default template is provided in powertools ([_LambdaJsonLayout.json_](https://github.com/aws-powertools/powertools-lambda-java/blob/4444b4bce8eb1cc19880d1c1ef07188d97de9126/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json){target="_blank"}): + ```xml hl_lines="5" <?xml version="1.0" encoding="UTF-8"?> <Configuration> @@ -168,7 +245,7 @@ Powertools for AWS Lambda (Java) extends the functionality of Log4J. Below is an </Console> </Appenders> <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> + <Logger name="com.example" level="debug" additivity="false"> <AppenderRef ref="JsonAppender"/> </Logger> <Root level="info"> @@ -178,154 +255,201 @@ Powertools for AWS Lambda (Java) extends the functionality of Log4J. Below is an </Configuration> ``` -You can also override log level by setting **`POWERTOOLS_LOG_LEVEL`** env var. Here is an example using AWS Serverless Application Model (SAM) +=== "logback.xml" -=== "template.yaml" - ``` yaml hl_lines="9 10" - Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java8 - Environment: - Variables: - POWERTOOLS_LOG_LEVEL: DEBUG - POWERTOOLS_SERVICE_NAME: example + With logback, we leverage a custom [Encoder](https://logback.qos.ch/manual/encoders.html){target="_blank"} + to provide structured logging: + + ```xml hl_lines="4 5" + <?xml version="1.0" encoding="UTF-8"?> + <configuration> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + </encoder> + </appender> + <logger name="com.example" level="DEBUG" additivity="false"> + <appender-ref ref="console" /> + </logger> + <root level="INFO"> + <appender-ref ref="console" /> + </root> + </configuration> ``` -You can also explicitly set a service name via **`POWERTOOLS_SERVICE_NAME`** env var. This sets **service** key that will be present across all log statements. +## Log level +Log level is generally configured in the `log4j2.xml` or `logback.xml`. But this level is static and needs a redeployment of the function to be changed. +Powertools for AWS Lambda permits to change this level dynamically thanks to an environment variable `POWERTOOLS_LOG_LEVEL`. -## Standard structured keys +We support the following log levels (SLF4J levels): `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`. +If the level is set to `CRITICAL` (supported in log4j but not logback), we revert it back to `ERROR`. +If the level is set to any other value, we set it to the default value (`INFO`). -Your logs will always include the following keys to your structured logging: +### AWS Lambda Advanced Logging Controls (ALC) -Key | Type | Example | Description -------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------- -**timestamp** | String | "2020-05-24 18:17:33,774" | Timestamp of actual log statement -**level** | String | "INFO" | Logging level -**coldStart** | Boolean | true| ColdStart value. -**service** | String | "payment" | Service name defined. "service_undefined" will be used if unknown -**samplingRate** | int | 0.1 | Debug logging sampling rate in percentage e.g. 10% in this case -**message** | String | "Collecting payment" | Log statement value. Unserializable JSON values will be casted to string -**functionName**| String | "example-powertools-HelloWorldFunction-1P1Z6B39FLU73" -**functionVersion**| String | "12" -**functionMemorySize**| String | "128" -**functionArn**| String | "arn:aws:lambda:eu-west-1:012345678910:function:example-powertools-HelloWorldFunction-1P1Z6B39FLU73" -**xray_trace_id**| String | "1-5759e988-bd862e3fe1be46a994272793" | X-Ray Trace ID when Lambda function has enabled Tracing -**function_request_id**| String | "899856cb-83d1-40d7-8611-9e78f15f32f4"" | AWS Request ID from lambda context +!!!question "When is it useful?" + When you want to set a logging policy to drop informational or verbose logs for one or all AWS Lambda functions, regardless of runtime and logger used. -## Capturing context Lambda info +<!-- markdownlint-disable MD013 --> +With [AWS Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced){target="_blank"}, you can enforce a minimum log level that Lambda will accept from your application code. -You can enrich your structured logs with key Lambda context information via `logEvent` annotation parameter. -You can also explicitly log any incoming event using `logEvent` param. Refer [Override default object mapper](#override-default-object-mapper) -to customise what is logged. +When enabled, you should keep your own log level and ALC log level in sync to avoid data loss. -!!! warning - Log event is disabled by default to prevent sensitive info being logged. +Here's a sequence diagram to demonstrate how ALC will drop both `INFO` and `DEBUG` logs emitted from `Logger`, when ALC log level is stricter than `Logger`. +<!-- markdownlint-enable MD013 --> +```mermaid +sequenceDiagram + participant Lambda service + participant Lambda function + participant Application Logger -=== "App.java" + Note over Lambda service: AWS_LAMBDA_LOG_LEVEL="WARN" + Note over Application Logger: POWERTOOLS_LOG_LEVEL="DEBUG" - ```java hl_lines="14" - import org.apache.logging.log4j.LogManager; - import org.apache.logging.log4j.Logger; - import software.amazon.lambda.powertools.logging.LoggingUtils; + Lambda service->>Lambda function: Invoke (event) + Lambda function->>Lambda function: Calls handler + Lambda function->>Application Logger: logger.error("Something happened") + Lambda function-->>Application Logger: logger.debug("Something happened") + Lambda function-->>Application Logger: logger.info("Something happened") + Lambda service--xLambda service: DROP INFO and DEBUG logs + Lambda service->>CloudWatch Logs: Ingest error logs +``` + +### Priority of log level settings in Powertools for AWS Lambda + +We prioritise log level settings in this order: + +1. `AWS_LAMBDA_LOG_LEVEL` environment variable +2. `POWERTOOLS_LOG_LEVEL` environment variable +3. level defined in the `log4j2.xml` or `logback.xml` files + +If you set `POWERTOOLS_LOG_LEVEL` lower than ALC, we will emit a warning informing you that your messages will be discarded by Lambda. + +!!! note + With ALC enabled, we are unable to increase the minimum log level below the `AWS_LAMBDA_LOG_LEVEL` environment variable value, see [AWS Lambda service documentation](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-log-level){target="_blank"} for more details. + +## Basic Usage + +You can use Powertools for AWS Lambda Logging with either the `@Logging` annotation or the functional API: + +=== "@Logging annotation" + + ```java hl_lines="8 10 12 14" + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; - ... - - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - Logger log = LogManager.getLogger(App.class); + // ... other imports + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + @Logging public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... + LOGGER.info("Collecting payment"); + // ... + LOGGER.debug("order={}, amount={}", order.getId(), order.getAmount()); + // ... } } ``` -=== "AppLogEvent.java" - - ```java hl_lines="8" - /** - * Handler for requests to Lambda function. - */ - public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +=== "Functional API" + + ```java hl_lines="8 11 12 14 17" + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + // ... other imports + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(AppLogEvent.class); + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); - @Logging(logEvent = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... + return PowertoolsLogging.withLogging(context, () -> { + LOGGER.info("Collecting payment"); + // ... + LOGGER.debug("order={}, amount={}", order.getId(), order.getAmount()); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); } } ``` -### Customising fields in logs +## Standard structured keys -- Utility by default emits `timestamp` field in the logs in format `yyyy-MM-dd'T'HH:mm:ss.SSSZz` and in system default timezone. -If you need to customize format and timezone, you can do so by configuring `log4j2.component.properties` and configuring properties as shown in example below: +Your logs will always include the following keys in your structured logging: -=== "log4j2.component.properties" +| Key | Type | Example | Description | +|-------------------|--------|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| **timestamp** | String | "2023-12-01T14:49:19.293Z" | Timestamp of actual log statement, by default uses default AWS Lambda timezone (UTC) | +| **level** | String | "INFO" | Logging level (any level supported by _SLF4J_ (i.e. `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`) | +| **service** | String | "payment" | Service name defined, by default `service_undefined` | +| **sampling_rate** | float | 0.1 | Debug logging sampling rate in percentage e.g. 10% in this case (logged if not 0) | +| **message** | String | "Collecting payment" | Log statement value. Unserializable JSON values will be casted to string | +| **xray_trace_id** | String | "1-5759e988-bd862e3fe1be46a994272793" | X-Ray Trace ID when [Tracing is enabled](https://docs.aws.amazon.com/lambda/latest/dg/services-xray.html){target="_blank"} | +| **error** | Map | `{ "name": "InvalidAmountException", "message": "Amount must be superior to 0", "stack": "at..." }` | Eventual exception (e.g. when doing `logger.error("Error", new InvalidAmountException("Amount must be superior to 0"));`) | - ```properties hl_lines="1 2" - log4j.layout.jsonTemplate.timestampFormatPattern=yyyy-MM-dd'T'HH:mm:ss.SSSZz - log4j.layout.jsonTemplate.timeZone=Europe/Oslo - ``` +???+ note + If you emit a log message with a key that matches one of the [standard structured keys](#standard-structured-keys) or one of the [additional structured keys](#additional-structured-keys), the Logger will log a warning message and ignore the key. -- Utility also provides sample template for [Elastic Common Schema(ECS)](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html) layout. -The field emitted in logs will follow specs from [ECS](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html) together with field captured by utility as mentioned [above](#standard-structured-keys). +## Additional structured keys - Use `LambdaEcsLayout.json` as `eventTemplateUri` when configuring `JsonTemplateLayout`. +### Logging Lambda context information +The following keys will also be added to all your structured logs (unless [configured otherwise](#more-customization_1)): -=== "log4j2.xml" +| Key | Type | Example | Description | +|--------------------------|---------|----------------------------------------------------------------------------------------|------------------------------------| +| **cold_start** | Boolean | false | ColdStart value | +| **function_name** | String | "example-PaymentFunction-1P1Z6B39FLU73" | Name of the function | +| **function_version** | String | "12" | Version of the function | +| **function_memory_size** | String | "512" | Memory configure for the function | +| **function_arn** | String | "arn:aws:lambda:eu-west-1:012345678910:function:example-PaymentFunction-1P1Z6B39FLU73" | ARN of the function | +| **function_request_id** | String | "899856cb-83d1-40d7-8611-9e78f15f32f4"" | AWS Request ID from lambda context | - ```xml hl_lines="5" - <?xml version="1.0" encoding="UTF-8"?> - <Configuration> - <Appenders> - <Console name="JsonAppender" target="SYSTEM_OUT"> - <JsonTemplateLayout eventTemplateUri="classpath:LambdaEcsLayout.json" /> - </Console> - </Appenders> - <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> - <AppenderRef ref="JsonAppender"/> - </Logger> - <Root level="info"> - <AppenderRef ref="JsonAppender"/> - </Root> - </Loggers> - </Configuration> - ``` +### Logging additional keys -## Setting a Correlation ID +#### Logging a correlation ID -You can set a Correlation ID using `correlationIdPath` attribute by passing a [JSON Pointer expression](https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-03){target="_blank"}. +You can set a correlation ID using the `correlationIdPath` parameter by passing a [JMESPath expression](https://jmespath.org/tutorial.html){target="_blank"}, +including our custom [JMESPath Functions](../utilities/serialization.md#built-in-functions). -=== "App.java" +=== "@Logging annotation" - ```java hl_lines="8" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + ```java hl_lines="5" + public class AppCorrelationIdPath implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationIdPath.class); - @Logging(correlationIdPath = "/headers/my_request_id_header") + @Logging(correlationIdPath = "headers.my_request_id_header") public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - log.info("Collecting payment") - ... + // ... + LOGGER.info("Collecting payment") + // ... } } ``` -=== "Example Event" + +=== "Functional API" + + ```java hl_lines="6" + public class AppCorrelationIdPath implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationIdPath.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, "headers.my_request_id_header", input, () -> { + // ... + LOGGER.info("Collecting payment"); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` +=== "Example HTTP Event" ```json hl_lines="3" { @@ -335,42 +459,57 @@ You can set a Correlation ID using `correlationIdPath` attribute by passing a [J } ``` -=== "Example CloudWatch Logs excerpt" +=== "CloudWatch Logs" - ```json hl_lines="11" + ```json hl_lines="6" { "level": "INFO", "message": "Collecting payment", - "timestamp": "2021-05-03 11:47:12,494+0200", + "timestamp": "2023-12-01T14:49:19.293Z", "service": "payment", - "coldStart": true, - "functionName": "test", - "functionMemorySize": 128, - "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", "correlation_id": "correlation_id_value" } ``` -We provide [built-in JSON Pointer expression](https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-03){target="_blank"} -for known event sources, where either a request ID or X-Ray Trace ID are present. + +**Known correlation IDs** -=== "App.java" +To ease routine tasks like extracting correlation ID from popular event sources, +we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions). - ```java hl_lines="10" - import software.amazon.lambda.powertools.logging.CorrelationIdPathConstants; +=== "@Logging annotation" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + ```java hl_lines="1 7" + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + + public class AppCorrelationId implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationId.class); - @Logging(correlationIdPath = CorrelationIdPathConstants.API_GATEWAY_REST) + @Logging(correlationIdPath = CorrelationIdPaths.API_GATEWAY_REST) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - log.info("Collecting payment") - ... + // ... + LOGGER.info("Collecting payment") + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="1 8" + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + + public class AppCorrelationId implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationId.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, CorrelationIdPaths.API_GATEWAY_REST, input, () -> { + // ... + LOGGER.info("Collecting payment"); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); } } ``` @@ -379,268 +518,1097 @@ for known event sources, where either a request ID or X-Ray Trace ID are present ```json hl_lines="3" { - "requestContext": { - "requestId": "correlation_id_value" - } + "requestContext": { + "requestId": "correlation_id_value" + } } ``` -=== "Example CloudWatch Logs excerpt" +=== "Example CloudWatch Logs" - ```json hl_lines="11" + ```json hl_lines="6" { "level": "INFO", "message": "Collecting payment", - "timestamp": "2021-05-03 11:47:12,494+0200", + "timestamp": "2023-12-01T14:49:19.293Z", "service": "payment", - "coldStart": true, - "functionName": "test", - "functionMemorySize": 128, - "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", "correlation_id": "correlation_id_value" } ``` - -## Appending additional keys -!!! info "Custom keys are persisted across warm invocations" - Always set additional keys as part of your handler to ensure they have the latest value, or explicitly clear them with [`clearState=true`](#clearing-all-state). +#### Custom keys -You can append your own keys to your existing logs via `appendKey`. +**Using StructuredArguments** -=== "App.java" +To append additional keys in your logs, you can use the `StructuredArguments` class: - ```java hl_lines="11 19" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +=== "PaymentFunction.java" + + ```java hl_lines="1 2 11 17" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entries; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); - @Logging(logEvent = true) + @Logging public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - LoggingUtils.appendKey("test", "willBeLogged"); - ... + // ... + LOGGER.info("Collecting payment", entry("orderId", order.getId())); - ... - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test", "value"); - customKeys.put("test1", "value1"); - - LoggingUtils.appendKeys(customKeys); - ... + // ... + Map<String, String> customKeys = new HashMap<>(); + customKeys.put("paymentId", payment.getId()); + customKeys.put("amount", payment.getAmount); + LOGGER.info("Payment successful", entries(customKeys)); } } ``` +=== "CloudWatch Logs for PaymentFunction" -### Removing additional keys + ```json hl_lines="7 16-18" + { + "level": "INFO", + "message": "Collecting payment", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "orderId": "41376" + } + ... + { + "level": "INFO", + "message": "Payment successful", + "service": "payment", + "timestamp": "2023-12-01T14:49:20.118Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "orderId": "41376", + "paymentId": "3245", + "amount": 345.99 + } + ``` -You can remove any additional key from entry using `LoggingUtils.removeKeys()`. +`StructuredArguments` provides several options: -=== "App.java" + - `entry` to add one key and value into the log structure. Note that value can be any object type. + - `entries` to add multiple keys and values (from a Map) into the log structure. Note that values can be any object type. + - `json` to add a key and raw json (string) as value into the log structure. + - `array` to add one key and multiple values into the log structure. Note that values can be any object type. - ```java hl_lines="19 20" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +=== "OrderFunction.java" + + ```java hl_lines="1 2 11 17" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.array; + + public class OrderFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); - @Logging(logEvent = true) + @Logging public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - LoggingUtils.appendKey("test", "willBeLogged"); - ... - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test1", "value"); - customKeys.put("test2", "value1"); - - LoggingUtils.appendKeys(customKeys); - ... - LoggingUtils.removeKey("test"); - LoggingUtils.removeKeys("test1", "test2"); - ... + // ... + LOGGER.info("Processing order", entry("order", order), array("products", productList)); + // ... } } ``` -### Clearing all state +=== "CloudWatch Logs for OrderFunction" + + ```json hl_lines="7 13" + { + "level": "INFO", + "message": "Processing order", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "order": { + "orderId": 23542, + "amount": 459.99, + "date": "2023-12-01T14:49:19.018Z", + "customerId": 328496 + }, + "products": [ + { + "productId": 764330, + "name": "product1", + "quantity": 1, + "price": 300 + }, + { + "productId": 798034, + "name": "product42", + "quantity": 1, + "price": 159.99 + } + ] + } + ``` + +???+ tip "Use arguments without log placeholders" + As shown in the example above, you can use arguments (with `StructuredArguments`) without placeholders (`{}`) in the message. + If you add the placeholders, the arguments will be logged both as an additional field and also as a string in the log message, using the `toString()` method. -Logger is commonly initialized in the global scope. Due to [Lambda Execution Context reuse](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html), -this means that custom keys can be persisted across invocations. If you want all custom keys to be deleted, you can use -`clearState=true` attribute on `@Logging` annotation. + === "Function1.java" + + ```java + LOGGER.info("Processing {}", entry("order", order)); + ``` + + === "Order.java" + + ```java hl_lines="5" + public class Order { + // ... + + @Override + public String toString() { + return "Order{" + + "orderId=" + id + + ", amount=" + amount + + ", date='" + date + '\'' + + ", customerId=" + customerId + + '}'; + } + } + ``` + + === "CloudWatch Logs Function1" + + ```json hl_lines="3 7" + { + "level": "INFO", + "message": "Processing order=Order{orderId=23542, amount=459.99, date='2023-12-01T14:49:19.018Z', customerId=328496}", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "order": { + "orderId": 23542, + "amount": 459.99, + "date": "2023-12-01T14:49:19.018Z", + "customerId": 328496 + } + } + ``` + + You can also combine structured arguments with non structured ones. For example: + + === "Function2.java" + ```java + LOGGER.info("Processing order {}", order.getOrderId(), entry("order", order)); + ``` + + === "CloudWatch Logs Function2" + ```json + { + "level": "INFO", + "message": "Processing order 23542", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "order": { + "orderId": 23542, + "amount": 459.99, + "date": "2023-12-01T14:49:19.018Z", + "customerId": 328496 + } + } + ``` +???+ warning "Do not use reserved keys in `StructuredArguments`" + If the key name of your structured argument matches any of the [standard structured keys](#standard-structured-keys) or any of the [additional structured keys](#additional-structured-keys), the Logger will log a warning message and ignore the key. This is to protect you from accidentally overwriting reserved keys such as the log level or Lambda context information. + +**Using MDC** -=== "App.java" +Mapped Diagnostic Context (MDC) is essentially a Key-Value store. It is supported by the [SLF4J API](https://www.slf4j.org/manual.html#mdc){target="_blank"}, +[logback](https://logback.qos.ch/manual/mdc.html){target="_blank"} and log4j (known as [ThreadContext](https://logging.apache.org/log4j/2.x/manual/thread-context.html){target="_blank"}). You can use the following standard: - ```java hl_lines="8 12" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +`MDC.put("key", "value");` + +???+ warning "Custom keys stored in the MDC are persisted across warm invocations" + Always set additional keys as part of your handler method to ensure they have the latest value, or explicitly clear them with [`clearState=true`](#clearing-state). + +???+ warning "Do not add reserved keys to MDC" + Avoid adding any of the keys listed in [standard structured keys](#standard-structured-keys) and [additional structured keys](#additional-structured-keys) to your MDC. This may cause unindented behavior and will overwrite the context set by the Logger. Unlike with `StructuredArguments`, the Logger will **not** ignore reserved keys set via MDC. + + +### Removing additional keys + +You can remove additional keys added with the MDC using `MDC.remove("key")`. + +#### Clearing state + +Logger is commonly initialized in the global scope. Due to [Lambda Execution Context reuse](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html){target="_blank"}, +this means that custom keys, added with the MDC can be persisted across invocations. You can clear state using `clearState=true` on the `@Logging` annotation, or use the functional API which handles cleanup automatically. + +=== "@Logging annotation" + + ```java hl_lines="5 8" + public class CreditCardFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CreditCardFunction.class); @Logging(clearState = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - if(input.getHeaders().get("someSpecialHeader")) { - LoggingUtils.appendKey("specialKey", "value"); - } - - log.info("Collecting payment"); - ... + // ... + MDC.put("cardNumber", card.getId()); + LOGGER.info("Updating card information"); + // ... } } ``` + === "#1 Request" - ```json hl_lines="11" - { - "level": "INFO", - "message": "Collecting payment", - "timestamp": "2021-05-03 11:47:12,494+0200", - "service": "payment", - "coldStart": true, - "functionName": "test", - "functionMemorySize": 128, - "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", - "specialKey": "value" - } + ```json hl_lines="7" + { + "level": "INFO", + "message": "Updating card information", + "service": "card", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "cardNumber": "6818 8419 9395 5322" + } ``` === "#2 Request" - ```json - { - "level": "INFO", - "message": "Collecting payment", - "timestamp": "2021-05-03 11:47:12,494+0200", - "service": "payment", - "coldStart": true, - "functionName": "test", - "functionMemorySize": 128, - "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72" - } + ```json hl_lines="7" + { + "level": "INFO", + "message": "Updating card information", + "service": "card", + "timestamp": "2023-12-01T14:49:20.213Z", + "xray_trace_id": "2-7a518f43-5e9d2b1f6cfd5e8b3a4e1f9c", + "cardNumber": "7201 6897 6685 3285" + } ``` -## Override default object mapper +`clearState` is based on `MDC.clear()`. State clearing is automatically done at the end of the execution of the handler if set to `true`. -You can optionally choose to override default object mapper which is used to serialize lambda function events. You might -want to supply custom object mapper in order to control how serialisation is done, for example, when you want to log only -specific fields from received event due to security. +???+ tip + When using the functional API with `PowertoolsLogging.withLogging()`, state is automatically cleared at the end of execution, so you don't need to manage it manually. -=== "App.java" - ```java hl_lines="9 10" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - Logger log = LogManager.getLogger(App.class); +## Logging incoming event - static { - ObjectMapper objectMapper = new ObjectMapper(); - LoggingUtils.defaultObjectMapper(objectMapper); - } +When debugging in non-production environments, you can log the incoming event using the `@Logging` annotation with the `logEvent` parameter, via the `POWERTOOLS_LOGGER_LOG_EVENT` environment variable, or manually with the functional API. + +???+ warning + This is disabled by default to prevent sensitive info being logged. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogEvent.class); + @Logging(logEvent = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... + // ... } } ``` -## Sampling debug logs +=== "Functional API" -You can dynamically set a percentage of your logs to **DEBUG** level via env var `POWERTOOLS_LOGGER_SAMPLE_RATE` or -via `samplingRate` attribute on annotation. + ```java hl_lines="1 9" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; -!!! info - Configuration on environment variable is given precedence over sampling rate configuration on annotation, provided it's in valid value range. + public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogEvent.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + LOGGER.info("Handler Event", entry("event", input)); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` -=== "Sampling via annotation attribute" +???+ note + If you use this on a RequestStreamHandler, the SDK must duplicate input streams in order to log them when used together with the `@Logging` annotation. - ```java hl_lines="8" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +## Logging handler response + +When debugging in non-production environments, you can log the response using the `@Logging` annotation with the `logResponse` parameter, via the `POWERTOOLS_LOGGER_LOG_RESPONSE` environment variable, or manually with the functional API. + +???+ warning + This is disabled by default to prevent sensitive info being logged. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class AppLogResponse implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); + + @Logging(logResponse = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="1 11" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + + public class AppLogResponse implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + // ... + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent().withStatusCode(200); + LOGGER.info("Handler Response", entry("response", response)); + return response; + }); + } + } + ``` + +???+ note + If you use this on a RequestStreamHandler, Powertools must duplicate output streams in order to log them when used together with the `@Logging` annotation. + +## Logging handler uncaught exception +By default, AWS Lambda logs any uncaught exception that might happen in the handler. However, this log is not structured +and does not contain any additional context. When using the `@Logging` annotation, you can enable structured exception logging +with `logError` param or via `POWERTOOLS_LOGGER_LOG_ERROR` env var. + +???+ warning + This is disabled by default to prevent double logging. + +???+ note + This feature is only available when using the `@Logging` annotation. When using the functional API, you must catch and log exceptions manually using try-catch blocks. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class AppLogError implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - @Logging(samplingRate = 0.5) + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogError.class); + + @Logging(logError = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... + // ... } } ``` -=== "Sampling via environment variable" +=== "Functional API" - ```yaml hl_lines="9" - Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java8 - Environment: - Variables: - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 + ```java hl_lines="1 9 12-13" + import org.slf4j.MarkerFactory; + + public class AppLogError implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogError.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + try { + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } catch (Exception e) { + LOGGER.error(MarkerFactory.getMarker("FATAL"), "Exception in Lambda Handler", e); + throw e; + } + }); + } + } ``` +## Advanced -## Upgrade to JsonTemplateLayout from deprecated LambdaJsonLayout configuration in log4j2.xml +### Buffering logs -Prior to version [1.10.0](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v1.10.0), only supported way of configuring `log4j2.xml` was via `<LambdaJsonLayout/>`. This plugin is -deprecated now and will be removed in future version. Switching to `JsonTemplateLayout` is straight forward. +Log buffering enables you to buffer logs for a specific request or invocation. Enable log buffering by configuring the `BufferingAppender` in your logging configuration. You can buffer logs at the `WARNING`, `INFO` or `DEBUG` level, and flush them automatically on error or manually as needed. -Below examples shows deprecated and new configuration of `log4j2.xml`. +!!! tip "This is useful when you want to reduce the number of log messages emitted while still having detailed logs when needed, such as when troubleshooting issues." -=== "Deprecated configuration of log4j2.xml" +=== "log4j2.xml" - ```xml hl_lines="5" + ```xml hl_lines="7-12 16 19" <?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console name="JsonAppender" target="SYSTEM_OUT"> - <LambdaJsonLayout compact="true" eventEol="true"/> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> </Console> + <BufferingAppender name="BufferedJsonAppender" + maxBytes="20480" + bufferAtVerbosity="DEBUG" + flushOnErrorLog="true"> + <AppenderRef ref="JsonAppender"/> + </BufferingAppender> </Appenders> <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> - <AppenderRef ref="JsonAppender"/> + <Logger name="com.example" level="debug" additivity="false"> + <AppenderRef ref="BufferedJsonAppender"/> </Logger> - <Root level="info"> - <AppenderRef ref="JsonAppender"/> + <Root level="debug"> + <AppenderRef ref="BufferedJsonAppender"/> </Root> </Loggers> </Configuration> ``` -=== "New configuration of log4j2.xml" +=== "logback.xml" - ```xml hl_lines="5" + ```xml hl_lines="6-11 13 16" + <?xml version="1.0" encoding="UTF-8"?> + <configuration> + <appender name="JsonAppender" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder" /> + </appender> + <appender name="BufferedJsonAppender" class="software.amazon.lambda.powertools.logging.logback.BufferingAppender"> + <maxBytes>20480</maxBytes> + <bufferAtVerbosity>DEBUG</bufferAtVerbosity> + <flushOnErrorLog>true</flushOnErrorLog> + <appender-ref ref="JsonAppender" /> + </appender> + <logger name="com.example" level="DEBUG" additivity="false"> + <appender-ref ref="BufferedJsonAppender" /> + </logger> + <root level="DEBUG"> + <appender-ref ref="BufferedJsonAppender" /> + </root> + </configuration> + ``` + +=== "PaymentFunction.java" + + ```java hl_lines="8 12" + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.Logging; + // ... other imports + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("a debug log"); // this is buffered + LOGGER.info("an info log"); // this is not buffered + + // do stuff + + // Buffer is automatically cleared at the end of the method by @Logging annotation + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +#### Configuring the buffer + +When configuring log buffering, you have options to fine-tune how logs are captured, stored, and emitted. You can configure the following parameters in the `BufferingAppender` configuration: + +| Parameter | Description | Configuration | +| --------------------- | ----------------------------------------------- | ---------------------------- | +| `maxBytes` | Maximum size of the log buffer in bytes | `int` (default: 20480 bytes) | +| `bufferAtVerbosity` | Minimum log level to buffer | `DEBUG` (default), `INFO`, `WARNING` | +| `flushOnErrorLog` | Automatically flush buffer when `ERROR` or `FATAL` level logs are emitted | `true` (default), `false` | + +!!! warning "Logger Level Configuration" + To use log buffering effectively, you must set your logger levels to the same level as `bufferAtVerbosity` or more verbose for the logging framework to capture and forward logs to the `BufferingAppender`. For example, if you want to buffer `DEBUG` level logs and emit `INFO`+ level logs directly, you must: + + - Set your logger levels to `DEBUG` in your log4j2.xml or logback.xml configuration + - Set `POWERTOOLS_LOG_LEVEL=DEBUG` if using the environment variable (see [Log level](#log-level) section for more details) + + If you want to sample `INFO` and `WARNING` logs but not `DEBUG` logs, set your log level to `INFO` and `bufferAtVerbosity` to `WARNING`. This allows you to define the lower and upper bounds for buffering. All logs with a more severe level than `bufferAtVerbosity` will be emitted directly. + +=== "log4j2.xml - Buffer at WARNING level" + + ```xml hl_lines="9 14-15 18" <?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console name="JsonAppender" target="SYSTEM_OUT"> <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> </Console> + <BufferingAppender name="BufferedJsonAppender" + maxBytes="20480" + bufferAtVerbosity="WARNING"> + <AppenderRef ref="JsonAppender"/> + </BufferingAppender> </Appenders> <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> + <!-- Intentionally set to DEBUG to forward all logs to BufferingAppender --> + <Logger name="com.example" level="debug" additivity="false"> + <AppenderRef ref="BufferedJsonAppender"/> + </Logger> + <Root level="debug"> + <AppenderRef ref="BufferedJsonAppender"/> + </Root> + </Loggers> + </Configuration> + ``` + +=== "PaymentFunction.java - Buffer at WARNING level" + + ```java hl_lines="7-9 13" + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.warn("a warning log"); // this is buffered + LOGGER.info("an info log"); // this is buffered + LOGGER.debug("a debug log"); // this is buffered + + // do stuff + + // Buffer is automatically cleared at the end of the method by @Logging annotation + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +=== "log4j2.xml - Disable flush on error" + + ```xml hl_lines="9" + <?xml version="1.0" encoding="UTF-8"?> + <Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <BufferingAppender name="BufferedJsonAppender" + maxBytes="20480" + flushOnErrorLog="false"> <AppenderRef ref="JsonAppender"/> + </BufferingAppender> + </Appenders> + <Loggers> + <Logger name="com.example" level="debug" additivity="false"> + <AppenderRef ref="BufferedJsonAppender"/> </Logger> + <Root level="debug"> + <AppenderRef ref="BufferedJsonAppender"/> + </Root> + </Loggers> + </Configuration> + ``` + +=== "PaymentFunction.java - Manual flush required" + + ```java hl_lines="1 16 19-20" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("a debug log"); // this is buffered + + // do stuff + + try { + throw new RuntimeException("Something went wrong"); + } catch (RuntimeException error) { + LOGGER.error("An error occurred", error); // Logs won't be flushed here + } + + // Manually flush buffered logs + PowertoolsLogging.flushBuffer(); + + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +!!! note "Disabling `flushOnErrorLog` will not flush the buffer when logging an error. This is useful when you want to control when the buffer is flushed by calling the flush method manually." + +#### Manual buffer control + +You can manually control the log buffer using the `PowertoolsLogging` utility class, which provides a backend-independent API that works with both Log4j2 and Logback: + +=== "Manual flush" + + ```java hl_lines="1 12-13" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("Processing payment"); // this is buffered + LOGGER.info("Payment validation complete"); // this is buffered + + // Manually flush all buffered logs + PowertoolsLogging.flushBuffer(); + + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +=== "Manual clear" + + ```java hl_lines="1 12-13" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("Processing payment"); // this is buffered + LOGGER.info("Payment validation complete"); // this is buffered + + // Manually clear buffered logs without outputting them + PowertoolsLogging.clearBuffer(); + + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +**Available methods:** + +- `#!java PowertoolsLogging.flushBuffer()` - Outputs all buffered logs and clears the buffer +- `#!java PowertoolsLogging.clearBuffer()` - Discards all buffered logs without outputting them + +#### Flushing on exceptions + +Use the `@Logging` annotation to automatically flush buffered logs when an uncaught exception is raised in your Lambda function. This is enabled by default (`flushBufferOnUncaughtError = true`), but you can explicitly configure it if needed. + +???+ warning + This feature is only available when using the `@Logging` annotation. When using the functional API, you must manually flush the buffer in exception handlers. + +=== "@Logging annotation" + + ```java hl_lines="5 11" + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging(flushBufferOnUncaughtError = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("a debug log"); // this is buffered + + // do stuff + + throw new RuntimeException("Something went wrong"); // Logs will be flushed here + } + } + ``` + +=== "Functional API" + + ```java hl_lines="14" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + try { + LOGGER.debug("a debug log"); // this is buffered + // do stuff + throw new RuntimeException("Something went wrong"); + } catch (Exception e) { + PowertoolsLogging.flushBuffer(); // Manually flush buffered logs + throw e; + } + }); + } + } + ``` + +#### Buffering workflows + +##### Manual flush + +<center> +```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Logger + participant CloudWatch + Client->>Lambda: Invoke Lambda + Lambda->>Logger: Initialize with DEBUG level buffering + Logger-->>Lambda: Logger buffer ready + Lambda->>Logger: logger.debug("First debug log") + Logger-->>Logger: Buffer first debug log + Lambda->>Logger: logger.info("Info log") + Logger->>CloudWatch: Directly log info message + Lambda->>Logger: logger.debug("Second debug log") + Logger-->>Logger: Buffer second debug log + Lambda->>Logger: Manual flush call + Logger->>CloudWatch: Emit buffered logs to stdout + Lambda->>Client: Return execution result +``` +<i>Flushing buffer manually</i> +</center> + +##### Flushing when logging an error + +<center> +```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Logger + participant CloudWatch + Client->>Lambda: Invoke Lambda + Lambda->>Logger: Initialize with DEBUG level buffering + Logger-->>Lambda: Logger buffer ready + Lambda->>Logger: logger.debug("First log") + Logger-->>Logger: Buffer first debug log + Lambda->>Logger: logger.debug("Second log") + Logger-->>Logger: Buffer second debug log + Lambda->>Logger: logger.debug("Third log") + Logger-->>Logger: Buffer third debug log + Lambda->>Lambda: Exception occurs + Lambda->>Logger: logger.error("Error details") + Logger->>CloudWatch: Emit error log + Logger->>CloudWatch: Emit buffered debug logs + Lambda->>Client: Raise exception +``` +<i>Flushing buffer when an error happens</i> +</center> + +##### Flushing on exception + +This works when using the `@Logging` annotation which automatically clears the buffer at the end of method execution. + +<center> +```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Logger + participant CloudWatch + Client->>Lambda: Invoke Lambda + Lambda->>Logger: Using @Logging annotation + Logger-->>Lambda: Logger context injected + Lambda->>Logger: logger.debug("First log") + Logger-->>Logger: Buffer first debug log + Lambda->>Logger: logger.debug("Second log") + Logger-->>Logger: Buffer second debug log + Lambda->>Lambda: Uncaught Exception + Lambda->>CloudWatch: Automatically emit buffered debug logs + Lambda->>Client: Raise uncaught exception +``` +<i>Flushing buffer when an uncaught exception happens</i> +</center> + +#### Buffering FAQs + +1. **Does the buffer persist across Lambda invocations?** No, each Lambda invocation has its own buffer. The buffer is initialized when the Lambda function is invoked and is cleared after the function execution completes or when flushed manually. +2. **Are my logs buffered during cold starts (INIT phase)?** No, we never buffer logs during cold starts. This is because we want to ensure that logs emitted during this phase are always available for debugging and monitoring purposes. The buffer is only used during the execution of the Lambda function. +3. **How can I prevent log buffering from consuming excessive memory?** You can limit the size of the buffer by setting the `maxBytes` option in the `BufferingAppender` configuration. This will ensure that the buffer does not grow indefinitely. +4. **What happens if the log buffer reaches its maximum size?** Older logs are removed from the buffer to make room for new logs. This means that if the buffer is full, you may lose some logs if they are not flushed before the buffer reaches its maximum size. When this happens, we emit a warning when flushing the buffer to indicate that some logs have been dropped. +5. **How is the log size of a log line calculated?** The log size is calculated based on the size of the log line in bytes. This includes the size of the log message, any exception (if present), the log line location, additional keys, and the timestamp. +6. **What timestamp is used when I flush the logs?** The timestamp is the original time when the log record was created. If you create a log record at 11:00:10 and flush it at 11:00:25, the log line will retain its original timestamp of 11:00:10. +7. **What happens if I try to add a log line that is bigger than max buffer size?** The log will be emitted directly to standard output and not buffered. When this happens, we emit a warning to indicate that the log line was too big to be buffered. +8. **What happens if Lambda times out without flushing the buffer?** Logs that are still in the buffer will be lost. +9. **How does the `BufferingAppender` work with different appenders?** The `BufferingAppender` is designed to wrap arbitrary appenders, providing maximum flexibility. You can wrap console appenders, file appenders, or any custom appenders with buffering functionality. + +## Sampling debug logs + +You can dynamically set a percentage of your logs to`DEBUG` level to be included in the logger output, regardless of configured log level, using the`POWERTOOLS_LOGGER_SAMPLE_RATE` environment variable, +via the `samplingRate` attribute on the `@Logging` annotation, or as a parameter in the functional API. + +!!! info + Configuration via environment variable is given precedence over sampling rate configuration, provided it's in valid value range. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + @Logging(samplingRate = 0.5) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // will eventually be logged based on the sampling rate + LOGGER.debug("Handle payment"); + } + } + ``` + +=== "Functional API" + + ```java hl_lines="6" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, 0.5, () -> { + // will eventually be logged based on the sampling rate + LOGGER.debug("Handle payment"); + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + +=== "Sampling via environment variable" + + ```yaml hl_lines="8" + Resources: + PaymentFunction: + Type: AWS::Serverless::Function + Properties: + ... + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 + + ``` + +## Built-in Correlation ID expressions + +You can use any of the following built-in JMESPath expressions with the `@Logging` annotation or the functional API: + +???+ note "Note: Any object key named with `-` must be escaped" + For example, **`request.headers."x-amzn-trace-id"`**. + +| Name | Expression | Description | +|-------------------------------|-------------------------------------|---------------------------------| +| **API_GATEWAY_REST** | `"requestContext.requestId"` | API Gateway REST API request ID | +| **API_GATEWAY_HTTP** | `"requestContext.requestId"` | API Gateway HTTP API request ID | +| **APPSYNC_RESOLVER** | `request.headers."x-amzn-trace-id"` | AppSync X-Ray Trace ID | +| **APPLICATION_LOAD_BALANCER** | `headers."x-amzn-trace-id"` | ALB X-Ray Trace ID | +| **EVENT_BRIDGE** | `"id"` | EventBridge Event ID | + + +## Customising fields in logs + +Powertools for AWS Lambda comes with default json structure ([standard fields](#standard-structured-keys) & [lambda context fields](#logging-lambda-context-information)). + +You can go further and customize which fields you want to keep in your logs or not. The configuration varies according to the underlying logging library. + +### Log4j2 configuration +Log4j2 configuration is done in _log4j2.xml_ and leverages `JsonTemplateLayout`: + +```xml + <Console name="console" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> +``` + +The `JsonTemplateLayout` is automatically configured with the provided template: + +??? example "LambdaJsonLayout.json" + ```json + { + "level": { + "$resolver": "level", + "field": "name" + }, + "message": { + "$resolver": "message" + }, + "error": { + "message": { + "$resolver": "exception", + "field": "message" + }, + "name": { + "$resolver": "exception", + "field": "className" + }, + "stack": { + "$resolver": "exception", + "field": "stackTrace", + "stackTrace": { + "stringified": true + } + } + }, + "cold_start": { + "$resolver": "powertools", + "field": "cold_start" + }, + "function_arn": { + "$resolver": "powertools", + "field": "function_arn" + }, + "function_memory_size": { + "$resolver": "powertools", + "field": "function_memory_size" + }, + "function_name": { + "$resolver": "powertools", + "field": "function_name" + }, + "function_request_id": { + "$resolver": "powertools", + "field": "function_request_id" + }, + "function_version": { + "$resolver": "powertools", + "field": "function_version" + }, + "sampling_rate": { + "$resolver": "powertools", + "field": "sampling_rate" + }, + "service": { + "$resolver": "powertools", + "field": "service" + }, + "timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + } + }, + "xray_trace_id": { + "$resolver": "powertools", + "field": "xray_trace_id" + }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, + "": { + "$resolver": "powertools" + } + } + ``` + +You can create your own template and leverage the [PowertoolsResolver](https://github.com/aws-powertools/powertools-lambda-java/tree/v2/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java){target="_blank"} +and any other resolver to log the desired fields with the desired format. Some examples of customization are given below: + +#### Customising date format + +Utility by default emits `timestamp` field in the logs in format `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` and in system default timezone. +If you need to customize format and timezone, you can update your template.json or by configuring `log4j2.component.properties` as shown in examples below: + +=== "my-custom-template.json" + + ```json + { + "timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd HH:mm:ss", + "timeZone": "Europe/Paris", + } + }, + } + ``` + +=== "log4j2.component.properties" + + ```properties hl_lines="1 2" + log4j.layout.jsonTemplate.timestampFormatPattern=yyyy-MM-dd'T'HH:mm:ss.SSSZz + log4j.layout.jsonTemplate.timeZone=Europe/Oslo + ``` + +See [`TimestampResolver` documentation](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#event-template-resolver-timestamp){target="_blank"} for more details. + +???+ warning "Lambda Advanced Logging Controls date format" + When using the Lambda ALC, you must have a date format compatible with the [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) + +#### More customization +You can also customize how [exceptions are logged](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#event-template-resolver-exception){target="_blank"}, and much more. +See the [JSON Layout template documentation](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html){target="_blank"} for more details. + +### Logback configuration +Logback configuration is done in _logback.xml_ and the `LambdaJsonEncoder`: + +```xml + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + </encoder> + </appender> +``` + +The `LambdaJsonEncoder` can be customized in different ways: + +#### Customising date format +Utility by default emits `timestamp` field in the logs in format `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` and in system default timezone. +If you need to customize format and timezone, you can change use the following: + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <timestampFormat>yyyy-MM-dd HH:mm:ss</timestampFormat> + <timestampFormatTimezoneId>Europe/Paris</timestampFormatTimezoneId> + </encoder> +``` + +#### More customization + +- You can use a standard `ThrowableHandlingConverter` to customize the exception format (default is no converter). Example: + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"> + <maxDepthPerThrowable>30</maxDepthPerThrowable> + <maxLength>2048</maxLength> + <shortenedClassNameLength>20</shortenedClassNameLength> + <exclude>sun\.reflect\..*\.invoke.*</exclude> + <exclude>net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude> + <evaluator class="myorg.MyCustomEvaluator"/> + <rootCauseFirst>true</rootCauseFirst> + <inlineHash>true</inlineHash> + </throwableConverter> + </encoder> +``` + +- You can choose to add information about threads (default is `false`): + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <includeThreadInfo>true</includeThreadInfo> + </encoder> +``` + +- You can even choose to remove Powertools information from the logs like function name, arn: + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <includePowertoolsInfo>false</includePowertoolsInfo> + </encoder> +``` + +## Elastic Common Schema (ECS) Support + +Utility also supports [Elastic Common Schema(ECS)](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html){target="_blank"} format. +The field emitted in logs will follow specs from [ECS](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html){target="_blank"} together with field captured by utility as mentioned [above](#standard-structured-keys). + +### Log4j2 configuration + +Use `LambdaEcsLayout.json` as `eventTemplateUri` when configuring `JsonTemplateLayout`. + +=== "log4j2.xml" + + ```xml hl_lines="5" + <?xml version="1.0" encoding="UTF-8"?> + <Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaEcsLayout.json" /> + </Console> + </Appenders> + <Loggers> <Root level="info"> <AppenderRef ref="JsonAppender"/> </Root> @@ -648,3 +1616,20 @@ Below examples shows deprecated and new configuration of `log4j2.xml`. </Configuration> ``` +### Logback configuration + +Use the `LambdaEcsEncoder` rather than the `LambdaJsonEncoder` when configuring the appender: + +=== "logback.xml" + + ```xml hl_lines="3" + <configuration> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"> + </encoder> + </appender> + <root level="INFO"> + <appender-ref ref="console" /> + </root> + </configuration> + ``` diff --git a/docs/core/metrics.md b/docs/core/metrics.md index e06ab6d10..e7f7bd87f 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -3,23 +3,29 @@ title: Metrics description: Core utility --- -Metrics creates custom metrics asynchronously by logging metrics to standard output following Amazon CloudWatch Embedded Metric Format (EMF). +Metrics creates custom metrics asynchronously by logging metrics to standard output following [Amazon CloudWatch Embedded Metric Format (EMF)](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html). -These metrics can be visualized through [Amazon CloudWatch Console](https://console.aws.amazon.com/cloudwatch/). +These metrics can be visualized through [Amazon CloudWatch Console](https://aws.amazon.com/cloudwatch/). -**Key features** +## Key features -* Aggregate up to 100 metrics using a single CloudWatch EMF object (large JSON blob). -* Validate against common metric definitions mistakes (metric unit, values, max dimensions, max metrics, etc). -* Metrics are created asynchronously by the CloudWatch service, no custom stacks needed. -* Context manager to create a one off metric with a different dimension. +- Aggregate up to 100 metrics using a single [CloudWatch EMF](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html){target="\_blank"} object (large JSON blob) +- Validating your metrics against common metric definitions mistakes (for example, metric unit, values, max dimensions, max metrics) +- Metrics are created asynchronously by the CloudWatch service. You do not need any custom stacks, and there is no impact to Lambda function latency +- Support for creating one off metrics with different dimensions +- GraalVM support ## Terminologies -If you're new to Amazon CloudWatch, there are two terminologies you must be aware of before using this utility: +If you're new to Amazon CloudWatch, there are some terminologies you must be aware of before using this utility: -* **Namespace**. It's the highest level container that will group multiple metrics from multiple services for a given application, for example `ServerlessEcommerce`. -* **Dimensions**. Metrics metadata in key-value format. They help you slice and dice metrics visualization, for example `ColdStart` metric by Payment `service`. +- **Namespace**. It's the highest level container that will group multiple metrics from multiple services for a given application, for example `ServerlessAirline`. +- **Dimensions**. Metrics metadata in key-value format. They help you slice and dice metrics visualization, for example `ColdStart` metric by `service`. +- **Metric**. It's the name of the metric, for example: `SuccessfulBooking` or `UpdatedBooking`. +- **Unit**. It's a value representing the unit of measure for the corresponding metric, for example: `Count` or `Seconds`. +- **Resolution**. It's a value representing the storage resolution for the corresponding metric. Metrics can be either `Standard` or `High` resolution. Read more about CloudWatch Periods [here](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Resolution_definition). + +Visit the AWS documentation for a complete explanation for [Amazon CloudWatch concepts](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html). <figure> <img src="../../media/metrics_terminology.png" /> @@ -28,9 +34,7 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar ## Install - Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. - -=== "Maven Java 11+" +=== "Maven" ```xml hl_lines="3-7 16 18 24-27" <dependencies> @@ -44,13 +48,14 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> <build> <plugins> ... <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>11</source> <!-- or higher --> <target>11</target> <!-- or higher --> @@ -62,6 +67,14 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -75,103 +88,92 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar </build> ``` -=== "Maven Java 1.8" - - ```xml hl_lines="3-7 16 18 24-27" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" +=== "Gradle" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } - + repositories { mavenCentral() } - + dependencies { - aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach } - + sourceCompatibility = 11 targetCompatibility = 11 ``` -=== "Gradle Java 1.8" +## Getting started - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' +Metrics has three global settings that will be used across all metrics emitted. Use your application or main service as the metric namespace to easily group all metrics: + +| Setting | Description | Environment variable | Decorator parameter | +| -------------------- | ------------------------------------------------------------------------------- | ---------------------------------- | ------------------- | +| **Metric namespace** | Logical container where all metrics will be placed e.g. `ServerlessAirline` | `POWERTOOLS_METRICS_NAMESPACE` | `namespace` | +| **Service** | Optionally, sets **service** metric dimension across all metrics e.g. `payment` | `POWERTOOLS_SERVICE_NAME` | `service` | +| **Function name** | Function name used as dimension for the cold start metric | `POWERTOOLS_METRICS_FUNCTION_NAME` | `functionName` | +| **Disable Metrics** | Optionally, disables all metrics flushing | `POWERTOOLS_METRICS_DISABLED` | N/A | + +!!! tip "Use your application or main service as the metric namespace to easily group all metrics" + +!!! info "`POWERTOOLS_METRICS_DISABLED` will not disable default metrics created by AWS services." + +### Order of Precedence of `Metrics` configuration + +The `Metrics` Singleton can be configured by three different interfaces. The following order of precedence applies: + +1. `@FlushMetrics` annotation +2. `MetricsBuilder` using Builder pattern (see [Advanced section](#usage-without-flushmetrics-annotation)) +3. Environment variables (recommended) + +For most use-cases, we recommend using Environment variables and only overwrite settings in code where needed using either the `@FlushMetrics` annotation or `MetricsBuilder` if the annotation cannot be used. + +=== "@FlushMetrics annotation" + + ```java hl_lines="9" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + // ... } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + } ``` -## Getting started +=== "MetricsBuilder" + + ```java hl_lines="7-8" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsBuilder; -Metric has two global settings that will be used across all metrics emitted: + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { -Setting | Description | Environment variable | Constructor parameter -------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- -**Metric namespace** | Logical container where all metrics will be placed e.g. `ServerlessAirline` | `POWERTOOLS_METRICS_NAMESPACE` | `namespace` -**Service** | Optionally, sets **service** metric dimension across all metrics e.g. `payment` | `POWERTOOLS_SERVICE_NAME` | `service` + private static final Metrics metrics = MetricsBuilder.builder() + .withNamespace("ServerlessAirline") + .withService("payment") + .build(); -!!! tip "Use your application or main service as the metric namespace to easily group all metrics" + @Override + public Object handleRequest(Object input, Context context) { + // ... + metrics.flush(); + } + } + ``` -=== "template.yaml" +=== "Environment variables" ```yaml hl_lines="9 10" Resources: @@ -179,81 +181,150 @@ Setting | Description | Environment variable | Constructor parameter Type: AWS::Serverless::Function Properties: ... - Runtime: java8 + Runtime: java11 Environment: Variables: POWERTOOLS_SERVICE_NAME: payment POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline ``` +`Metrics` is implemented as a Singleton to keep track of your aggregate metrics in memory and make them accessible anywhere in your code. The `@FlushMetrics` annotation automatically flushes metrics at the end of the Lambda handler execution. Alternatively, you can use the functional approach and manually flush metrics using `metrics.flush()`. + +!!!info "Read more about the functional approach in the [advanced section below](#usage-without-flushmetrics-annotation)." + +## Creating metrics + +You can create metrics using `addMetric`, and manually create dimensions for all your aggregate metrics using `addDimension`. Anywhere in your code, you can access the current `Metrics` Singleton using the `MetricsFactory`. + === "MetricsEnabledHandler.java" - ```java hl_lines="8" + ```java hl_lines="13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; - + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { - - MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); - + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + @Override - @Metrics(namespace = "ExampleApplication", service = "booking") + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") public Object handleRequest(Object input, Context context) { - ... + metrics.addDimension("environment", "prod"); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + // ... } } ``` -You can initialize Metrics anywhere in your code as many times as you need - It'll keep track of your aggregate metrics in memory. +!!! tip "The `MetricUnit` enum facilitates finding a supported metric unit by CloudWatch." -## Creating metrics +<!-- prettier-ignore-start --> +!!! note "Metrics dimensions" + CloudWatch EMF supports a max of 9 dimensions per metric. The Metrics utility will validate this limit when adding dimensions. +<!-- prettier-ignore-end --> -You can create metrics using `putMetric`, and manually create dimensions for all your aggregate metrics using `putDimensions`. +### Adding high-resolution metrics -=== "MetricsEnabledHandler.java" +You can create [high-resolution metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html#high-resolution-metrics) +passing a `#!java MetricResolution.HIGH` to the `addMetric` method. If nothing is passed `#!java MetricResolution.STANDARD` will be used. - ```java hl_lines="11 12" +=== "HigResMetricsHandler.java" + + ```java hl_lines="3 13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; - import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; + import software.amazon.lambda.powertools.metrics.model.MetricResolution; public class MetricsEnabledHandler implements RequestHandler<Object, Object> { - - MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); - + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + @Override - @Metrics(namespace = "ExampleApplication", service = "booking") + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") public Object handleRequest(Object input, Context context) { - metricsLogger.putDimensions(DimensionSet.of("environment", "prod")); - metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT); - ... + // ... + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT, MetricResolution.HIGH); } } ``` -!!! tip "The `Unit` enum facilitate finding a supported metric unit by CloudWatch." +<!-- prettier-ignore-start --> +!!! info "When is it useful?" + High-resolution metrics are data with a granularity of one second and are very useful in several situations such as telemetry, time series, real-time incident management, and others. +<!-- prettier-ignore-end --> + +### Adding dimensions + +You can add dimensions to your metrics using the `addDimension` method. You can either pass key-value pairs or you can create higher cardinality dimensions using `DimensionSet`. + +=== "KeyValueDimensionHandler.java" + + ```java hl_lines="3 13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.model.MetricResolution; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { -!!! note "Metrics overflow" - CloudWatch EMF supports a max of 100 metrics. Metrics utility will flush all metrics when adding the 100th metric while subsequent metrics will be aggregated into a new EMF object, for your convenience. + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + metrics.addDimension("Dimension", "Value"); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + } + } + ``` + +=== "HighCardinalityDimensionHandler.java" + + ```java hl_lines="4 13-14" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.model.MetricResolution; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + // You can add up to 30 dimensions in a single DimensionSet + metrics.addDimension(DimensionSet.of("Dimension1", "Value1", "Dimension2", "Value2")); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + } + } + ``` ### Flushing metrics -The `@Metrics` annotation **validates**, **serializes**, and **flushes** all your metrics. During metrics validation, -if no metrics are provided no exception will be raised. If metrics are provided, and any of the following criteria are -not met, `ValidationException` exception will be raised. +The `@FlushMetrics` annotation **validates**, **serializes**, and **flushes** all your metrics. During metrics validation, +if no metrics are provided no exception will be raised. If metrics are provided, and any of the following criteria are +not met, `IllegalStateException` or `IllegalArgumentException` exceptions will be raised. +<!-- prettier-ignore-start --> !!! tip "Metric validation" - * Maximum of 9 dimensions + - Maximum of 30 dimensions (`Service` default dimension counts as a regular dimension) + - Dimension keys and values cannot be null or empty + - Metric values must be valid numbers +<!-- prettier-ignore-end --> -If you want to ensure that at least one metric is emitted, you can pass `raiseOnEmptyMetrics = true` to the **@Metrics** annotation: +If you want to ensure that at least one metric is emitted, you can pass `raiseOnEmptyMetrics = true` to the `@FlushMetrics` annotation: === "MetricsRaiseOnEmpty.java" ```java hl_lines="6" - import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.FlushMetrics; public class MetricsRaiseOnEmpty implements RequestHandler<Object, Object> { @Override - @Metrics(raiseOnEmptyMetrics = true) + @FlushMetrics(raiseOnEmptyMetrics = true) public Object handleRequest(Object input, Context context) { ... } @@ -262,17 +333,17 @@ If you want to ensure that at least one metric is emitted, you can pass `raiseOn ## Capturing cold start metric -You can capture cold start metrics automatically with `@Metrics` via the `captureColdStart` variable. +You can capture cold start metrics automatically with `@FlushMetrics` via the `captureColdStart` variable. === "MetricsColdStart.java" ```java hl_lines="6" - import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.FlushMetrics; public class MetricsColdStart implements RequestHandler<Object, Object> { @Override - @Metrics(captureColdStart = true) + @FlushMetrics(captureColdStart = true) public Object handleRequest(Object input, Context context) { ... } @@ -281,33 +352,77 @@ You can capture cold start metrics automatically with `@Metrics` via the `captur If it's a cold start invocation, this feature will: -* Create a separate EMF blob solely containing a metric named `ColdStart` -* Add `FunctionName` and `Service` dimensions +- Create a separate EMF blob solely containing a metric named `ColdStart` +- Add `FunctionName` and `Service` dimensions This has the advantage of keeping cold start metric separate from your application metrics. +You can also specify a custom function name to be used in the cold start metric: + +=== "MetricsColdStartCustomFunction.java" + + ```java hl_lines="6" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + + public class MetricsColdStartCustomFunction implements RequestHandler<Object, Object> { + + @Override + @FlushMetrics(captureColdStart = true, functionName = "CustomFunction") + public Object handleRequest(Object input, Context context) { + ... + } + } + ``` + +<!-- prettier-ignore-start --> +!!!tip "You can overwrite the default `Service` and `FunctionName` dimensions of the cold start metric" + Set `#!java @FlushMetrics(captureColdStart = false)` and use the `captureColdStartMetric` method manually: + + ```java hl_lines="6 8" + public class MetricsColdStartCustomFunction implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(captureColdStart = false) + public Object handleRequest(Object input, Context context) { + metrics.captureColdStartMetric(context, DimensionSet.of("CustomDimension", "CustomValue")); + ... + } + } + ``` +<!-- prettier-ignore-end --> + ## Advanced -## Adding metadata +### Adding metadata + +You can use `addMetadata` for advanced use cases, where you want to add metadata as part of the serialized metrics object. -You can use `putMetadata` for advanced use cases, where you want to metadata as part of the serialized metrics object. +<!-- prettier-ignore-start --> +!!! info + This will not be available during metrics visualization, use Dimensions for this purpose. !!! info - **This will not be available during metrics visualization, use `dimensions` for this purpose.** + Adding metadata with a key that is the same as an existing metric will be ignored. +<!-- prettier-ignore-end --> === "App.java" - ```java hl_lines="8 9" + ```java hl_lines="13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; - import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; + import software.amazon.lambda.powertools.metrics.MetricsFactory; public class App implements RequestHandler<Object, Object> { + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + @Override - @Metrics(namespace = "ServerlessAirline", service = "payment") + @FlushMetrics(namespace = "ServerlessAirline", service = "booking-service") public Object handleRequest(Object input, Context context) { - metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); - metricsLogger().putMetadata("booking_id", "1234567890"); + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + metrics.addMetadata("booking_id", "1234567890"); // Needs to be added BEFORE flushing ... } } @@ -315,79 +430,262 @@ You can use `putMetadata` for advanced use cases, where you want to metadata as This will be available in CloudWatch Logs to ease operations on high cardinal data. -## Overriding default dimension set +### Setting default dimensions + +By default, all metrics emitted via module captures `Service` as one of the default dimensions. This is either specified via `POWERTOOLS_SERVICE_NAME` environment variable or via `service` attribute on `Metrics` annotation. -By default, all metrics emitted via module captures `Service` as one of the default dimension. This is either specified via -`POWERTOOLS_SERVICE_NAME` environment variable or via `service` attribute on `Metrics` annotation. If you wish to override the default -Dimension, it can be done via `#!java MetricsUtils.defaultDimensions()`. +If you wish to set custom default dimensions, it can be done via `#!java metrics.setDefaultDimensions()`. You can also use the `MetricsBuilder` instead of the `MetricsFactory` to configure **and** retrieve the `Metrics` Singleton at the same time (see `MetricsBuilder.java` tab). === "App.java" - ```java hl_lines="8 9 10" + ```java hl_lines="13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; - import static software.amazon.lambda.powertools.metrics.MetricsUtils; - + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + public class App implements RequestHandler<Object, Object> { - - MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); - - static { - MetricsUtils.defaultDimensions(DimensionSet.of("CustomDimension", "booking")); + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + metrics.setDefaultDimensions(DimensionSet.of("CustomDimension", "booking", "Environment", "prod")); + ... } - + } + ``` + +=== "MetricsBuilder.java" + + ```java hl_lines="8-10" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + + public class App implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsBuilder.builder() + .withDefaultDimensions(DimensionSet.of("CustomDimension", "booking", "Environment", "prod")) + .build(); + @Override - @Metrics(namespace = "ExampleApplication", service = "booking") + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") public Object handleRequest(Object input, Context context) { + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); ... - MetricsUtils.withSingleMetric("Metric2", 1, Unit.COUNT, log -> {}); } } ``` -## Creating a metric with a different dimension +<!-- prettier-ignore-start --> +!!!note + Overwriting the default dimensions will also overwrite the default `Service` dimension. If you wish to keep `Service` in your default dimensions, you need to add it manually. +<!-- prettier-ignore-end --> -CloudWatch EMF uses the same dimensions across all your metrics. Use `withSingleMetric` if you have a metric that should have different dimensions. +### Creating metrics with different configuration -!!! info - Generally, this would be an edge case since you [pay for unique metric](https://aws.amazon.com/cloudwatch/pricing/). Keep the following formula in mind: - **unique metric = (metric_name + dimension_name + dimension_value)** +You can create metrics with different configurations e.g. different namespace and/or dimensions using `flushMetrics()`: === "App.java" - ```java hl_lines="7 8 9" - import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; + ```java hl_lines="12-22" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; public class App implements RequestHandler<Object, Object> { + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") public Object handleRequest(Object input, Context context) { - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metrics.flushMetrics((customMetrics) -> { + customMetrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + // To optionally set a different namespace + customMetrics.setNamespace("CustomNamespace"); + // To optionally set different default dimensions + customMetrics.setDefaultDimensions(DimensionSet.of("CustomDefaultDimension", "value")); + // To optionally append additional dimensions to default dimensions + customMetrics.addDimension(DimensionSet.of("CustomDimension", "value")); + // To optionally add metadata + customMetrics.addMetadata("CustomMetadata", "value")); }); } } ``` -## Creating metrics with different configurations +<!-- prettier-ignore-start --> +!!! info + Generally, this would be an edge case since you [pay for unique metric](https://aws.amazon.com/cloudwatch/pricing). Keep the following formula in mind: -Use `withMetricsLogger` if you have one or more metrics that should have different configurations e.g. dimensions or namespace. + **unique metric = (metric_name + dimension_name + dimension_value)** +<!-- prettier-ignore-end --> + +### Usage without `@FlushMetrics` annotation + +You can use the **functional API** approach (see [usage patterns](../usage-patterns.md#functional-approach)) to work with Metrics without the `@FlushMetrics` annotation. The `Metrics` Singleton provides all configuration options via `MetricsBuilder`. This approach eliminates the AspectJ runtime dependency and is useful if you work in an environment or with a framework that does not leverage the vanilla Lambda `handleRequest` method. + +!!!info "The environment variables for Service and Namespace configuration still apply but can be overwritten with `MetricsBuilder` if needed." + +The following example shows how to configure a custom `Metrics` Singleton using the Builder pattern. With the functional approach, you must manually flush metrics using `metrics.flush()`. === "App.java" - ```java hl_lines="7 8 9 10 11 12 13" - import static software.amazon.lambda.powertools.metrics.MetricsUtils.withMetricsLogger; + ```java hl_lines="7-12 19 24" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsBuilder; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; public class App implements RequestHandler<Object, Object> { + // Create and configure a Metrics singleton using the functional approach + private static final Metrics metrics = MetricsBuilder.builder() + .withNamespace("ServerlessAirline") + .withRaiseOnEmptyMetrics(true) + .withService("payment") + .build(); @Override public Object handleRequest(Object input, Context context) { - withMetricsLogger(logger -> { - // override default dimensions - logger.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - // add metrics - logger.putMetric("CustomMetrics1", 1, Unit.COUNT); - logger.putMetric("CustomMetrics2", 5, Unit.COUNT); - }); + // You can manually capture the cold start metric + // Lambda context is an optional argument if not available in your environment + // Dimensions are also optional. + metrics.captureColdStartMetric(context, DimensionSet.of("FunctionName", "MyFunction", "Service", "payment")); + + // Add metrics + metrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + // Manually flush metrics + metrics.flush(); } } ``` + +## Testing your code + +### Suppressing metrics output + +If you would like to suppress metrics output during your unit tests, you can use the `POWERTOOLS_METRICS_DISABLED` environment variable. For example, using Maven you can set in your build plugins: + +```xml +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <POWERTOOLS_METRICS_DISABLED>true</POWERTOOLS_METRICS_DISABLED> + </environmentVariables> + </configuration> +</plugin> +``` + +### Asserting EMF output + +When unit testing your code, you can run assertions against the output generated by the `Metrics` Singleton. For the `EmfMetricsLogger`, you can assert the generated JSON blob following the [CloudWatch EMF specification](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html) against your expected output. + +Make sure to set a test metrics namespace and service name to run assertions against metrics. For example, by setting the following environment variables in your tests: + +```xml +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <POWERTOOLS_SERVICE_NAME>TestService</POWERTOOLS_SERVICE_NAME> + <POWERTOOLS_METRICS_NAMESPACE>TestNamespace</POWERTOOLS_METRICS_NAMESPACE> + </environmentVariables> + </configuration> +</plugin> +``` + +Consider the following example where we redirect the standard output to a custom `PrintStream`. We use the Jackson library to parse the EMF output into a `JsonNode` and run assertions against that. + +```java hl_lines="35 40 56-72" +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +@ExtendWith(MockitoExtension.class) +class MetricsTestExample { + + @Mock + Context lambdaContext; + + private final PrintStream standardOut = System.out; + private ByteArrayOutputStream outputStreamCaptor; + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() { + outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(standardOut); + } + + @Test + void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, lambdaContext); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + String[] jsonLines = emfOutput.split("\n"); + + // First JSON object should be the cold start metric + JsonNode coldStartNode = objectMapper.readTree(jsonLines[0]); + assertThat(coldStartNode.has("ColdStart")).isTrue(); + assertThat(coldStartNode.get("ColdStart").asDouble()).isEqualTo(1.0); + assertThat(coldStartNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("TestNamespace"); + assertThat(coldStartNode.has("Service")).isTrue(); + assertThat(coldStartNode.get("Service").asText()).isEqualTo("TestService"); + + // Second JSON object should be the regular metric + JsonNode regularNode = objectMapper.readTree(jsonLines[1]); + assertThat(regularNode.has("test-metric")).isTrue(); + assertThat(regularNode.get("test-metric").asDouble()).isEqualTo(100.0); + assertThat(regularNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("TestNamespace"); + assertThat(regularNode.has("Service")).isTrue(); + assertThat(regularNode.get("Service").asText()).isEqualTo("TestService"); + } + + static class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(captureColdStart = true) + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; + } + } +} +``` diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 17e81b867..95fbe6d06 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -3,7 +3,7 @@ title: Tracing description: Core utility --- -Powertools tracing is an opinionated thin wrapper for [AWS X-Ray Java SDK](https://github.com/aws/aws-xray-sdk-java/) +The Tracing utility is an opinionated thin wrapper for [AWS X-Ray Java SDK](https://github.com/aws/aws-xray-sdk-java/) a provides functionality to reduce the overhead of performing common tracing tasks. ![Tracing showcase](../media/tracing_utility_showcase.png) @@ -14,14 +14,13 @@ a provides functionality to reduce the overhead of performing common tracing tas * Helper methods to improve the developer experience of creating new X-Ray subsegments. * Better developer experience when developing with multiple threads. * Auto patch supported modules by AWS X-Ray + * GraalVM support ## Install -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. +=== "Maven" -=== "Maven Java 11+" - - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 25-28" <dependencies> ... <dependency> @@ -33,13 +32,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> <build> <plugins> ... <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>11</source> <!-- or higher --> <target>11</target> <!-- or higher --> @@ -51,6 +51,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -64,77 +72,12 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </build> ``` -=== "Maven Java 1.8" - - ```xml hl_lines="3-7 16 18 24-27" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' - } - - sourceCompatibility = 11 - targetCompatibility = 11 - ``` - -=== "Gradle Java 1.8" +=== "Gradle" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -142,14 +85,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl } dependencies { - aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach } - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 11 // or higher + targetCompatibility = 11 // or higher ``` - ## Initialization Before your use this utility, your AWS Lambda function [must have permissions](https://docs.aws.amazon.com/lambda/latest/dg/services-xray.html#services-xray-permissions) to send traces to AWS X-Ray. @@ -164,7 +107,7 @@ Before your use this utility, your AWS Lambda function [must have permissions](h Type: AWS::Serverless::Function Properties: ... - Runtime: java8 + Runtime: java11 Tracing: Active Environment: @@ -177,11 +120,13 @@ The Powertools for AWS Lambda (Java) service name is used as the X-Ray namespace ### Lambda handler -To enable Powertools for AWS Lambda (Java) tracing to your function add the `@Tracing` annotation to your `handleRequest` method or on -any method will capture the method as a separate subsegment automatically. You can optionally choose to customize -segment name that appears in traces. +You can enable tracing using either the `@Tracing` annotation or the functional API. + +**With the `@Tracing` annotation**, add it to your `handleRequest` method or any method to capture it as a separate subsegment automatically. You can optionally customize the segment name that appears in traces. + +**With the functional API**, use `TracingUtils.withSubsegment()` to manually create subsegments without AspectJ configuration. -=== "Tracing annotation" +=== "@Tracing annotation" ```java hl_lines="3 10 15" public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -205,6 +150,25 @@ segment name that appears in traces. } ``` +=== "Functional API" + + ```java hl_lines="1 6 7 8 10 11 12" + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("businessLogic1", subsegment -> { + // Business logic 1 + }); + + TracingUtils.withSubsegment("businessLogic2", subsegment -> { + // Business logic 2 + }); + } + } + ``` + === "Custom Segment names" ```java hl_lines="3" @@ -216,22 +180,25 @@ segment name that appears in traces. } ``` -When using this `@Tracing` annotation, Utility performs these additional tasks to ease operations: +When using the `@Tracing` annotation, the utility performs these additional tasks to ease operations: * Creates a `ColdStart` annotation to easily filter traces that have had an initialization overhead. * Creates a `Service` annotation if service parameter or `POWERTOOLS_SERVICE_NAME` is set. * Captures any response, or full exceptions generated by the handler, and include as tracing metadata. +By default, the `@Tracing` annotation uses `captureMode=ENVIRONMENT_VAR`, which means it will only record method responses and exceptions if you set +the environment variables `POWERTOOLS_TRACER_CAPTURE_RESPONSE` and `POWERTOOLS_TRACER_CAPTURE_ERROR` to `true`. You can override this behavior by +specifying a different `captureMode` to always record response, exception, both, or neither. -By default, this annotation will automatically record method responses and exceptions. You can change the default behavior by setting -the environment variables `POWERTOOLS_TRACER_CAPTURE_RESPONSE` and `POWERTOOLS_TRACER_CAPTURE_ERROR` as needed. Optionally, you can override behavior by -different supported `captureMode` to record response, exception or both. +!!! note + When using the functional API with `TracingUtils.withSubsegment()`, response and exception capture is not automatic. You can manually add metadata using `TracingUtils.putMetadata()` as needed. -!!! warning "Returning sensitive information from your Lambda handler or functions, where `Tracing` is used?" - You can disable annotation from capturing their responses and exception as tracing metadata with **`captureMode=DISABLED`** - or globally by setting environment variables **`POWERTOOLS_TRACER_CAPTURE_RESPONSE`** and **`POWERTOOLS_TRACER_CAPTURE_ERROR`** to **`false`** +!!! warning "Returning sensitive information from your Lambda handler or functions?" + When using the `@Tracing` annotation, you can disable it from capturing responses and exceptions as tracing metadata with **`captureMode=DISABLED`** + or globally by setting the environment variables **`POWERTOOLS_TRACER_CAPTURE_RESPONSE`** and **`POWERTOOLS_TRACER_CAPTURE_ERROR`** to **`false`**. + When using the functional API, you have full control over what metadata is captured. -=== "Disable on annotation" +=== "@Tracing annotation - Disable on method" ```java hl_lines="3" public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -242,7 +209,7 @@ different supported `captureMode` to record response, exception or both. } ``` -=== "Disable Globally" +=== "@Tracing annotation - Disable Globally" ```yaml hl_lines="11 12" Resources: @@ -250,7 +217,7 @@ different supported `captureMode` to record response, exception or both. Type: AWS::Serverless::Function Properties: ... - Runtime: java8 + Runtime: java11 Tracing: Active Environment: @@ -259,6 +226,20 @@ different supported `captureMode` to record response, exception or both. POWERTOOLS_TRACER_CAPTURE_ERROR: false ``` +=== "Functional API" + + ```java hl_lines="6 7 8" + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("businessLogic", subsegment -> { + // With functional API, you control what metadata is captured + }); + } + ``` + ### Annotations & Metadata **Annotations** are key-values associated with traces and indexed by AWS X-Ray. You can use them to filter traces and to @@ -331,32 +312,13 @@ specific fields from received event due to security. } ``` -## Utilities - -Tracing modules comes with certain utility method when you don't want to use annotation for capturing a code block -under a subsegment, or you are doing multithreaded programming. Refer examples below. +## Advanced usage -=== "Functional Api" +### Multi-threaded programming - ```java hl_lines="7 8 9 11 12 13" - import software.amazon.lambda.powertools.tracing.Tracing; - import software.amazon.lambda.powertools.tracing.TracingUtils; - - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - TracingUtils.withSubsegment("loggingResponse", subsegment -> { - // Some business logic - }); - - TracingUtils.withSubsegment("localNamespace", "loggingResponse", subsegment -> { - // Some business logic - }); - } - } - ``` +When working with multiple threads, you need to pass the trace entity to ensure proper trace context propagation. -=== "Multi Threaded Programming" +=== "Multi-threaded example" ```java hl_lines="7 9 10 11" import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; @@ -376,8 +338,32 @@ under a subsegment, or you are doing multithreaded programming. Refer examples b ## Instrumenting SDK clients and HTTP calls -User should make sure to instrument the SDK clients explicitly based on the function dependency. Refer details on -[how to instrument SDK client with Xray](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-awssdkclients.html) and [outgoing http calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-httpclients.html). +### AWS SDK for Java 2.x + +Powertools for AWS Lambda (Java) includes the `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` library, which **automatically instruments all AWS SDK v2 clients** when you add the `powertools-tracing` dependency to your project. This means downstream calls to AWS services are traced without any additional configuration. + +If you need more control over which clients are instrumented, you can manually add the `TracingInterceptor` to specific clients: + +=== "Manual instrumentation (optional)" + + ```java hl_lines="1 2 3 8 9 10 11" + import com.amazonaws.xray.interceptors.TracingInterceptor; + import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + + public class LambdaHandler { + private DynamoDbClient client = DynamoDbClient.builder() + .region(Region.US_WEST_2) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .addExecutionInterceptor(new TracingInterceptor()) + .build() + ) + .build(); + // ... + } + ``` + +For more details, refer to the [AWS X-Ray documentation on tracing AWS SDK calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-awssdkclients.html) and [outgoing HTTP calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-httpclients.html). ## Testing your code @@ -394,7 +380,7 @@ used internally via AWS X-Ray SDK to configure itself properly for lambda runtim === "Maven (pom.xml)" - ```xml hl_lines="4-13" + ```xml <build> ... <plugins> @@ -413,9 +399,9 @@ used internally via AWS X-Ray SDK to configure itself properly for lambda runtim ``` -=== "Gradle (build.gradle) " +=== "Gradle (build.gradle)" - ```json hl_lines="2-4" + ```json // Configures environment variable to avoid initialization of AWS X-Ray segments for each tests test { environment "LAMBDA_TASK_ROOT", "handler" @@ -461,6 +447,3 @@ Below is an example configuration needed for each test case. // test logic } ``` - - - diff --git a/docs/index.md b/docs/index.md index d3e487174..655c16e03 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,16 +3,14 @@ title: Homepage description: Powertools for AWS Lambda (Java) --- -![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/aws-powertools/powertools-lambda-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) - -Powertools for AWS Lambda (Java) is a suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. +Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. ???+ tip Powertools for AWS Lambda is also available for [Python](https://docs.powertools.aws.dev/lambda/python/latest/){target="_blank"}, [TypeScript](https://docs.powertools.aws.dev/lambda/typescript/latest/){target="_blank"}, and [.NET](https://docs.powertools.aws.dev/lambda/dotnet/){target="_blank"} -!!! tip "Looking for a quick run through of the core utilities?" - Check out [this detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-aws-lambda-powertools-java/) with a practical example. To dive deeper, +???+ tip "Looking for a quick run through of the core utilities?" + Check out [this detailed blog post](https://aws.amazon.com/blogs/compute/introducing-v2-of-powertools-for-aws-lambda-java/) with a practical example. To dive deeper, the [Powertools for AWS Lambda (Java) workshop](https://catalog.us-east-1.prod.workshops.aws/workshops/a7011c82-e4af-4a52-80fa-fcd61f1dacd9/en-US/introduction) is a great next step. ## Tenets @@ -28,6 +26,7 @@ This project separates core utilities that will be available in other runtimes v ## Install +<!-- TODO: Uncomment when SAM template is updated - https://github.com/aws-powertools/powertools-lambda-java/issues/1889 **Quick hello world example using SAM CLI** You can use [SAM](https://aws.amazon.com/serverless/sam/) to quickly setup a serverless project including Powertools for AWS Lambda (Java). @@ -73,16 +72,14 @@ Which runtime would you like to use? 12 - python3.10 Runtime: 2, 3, 4 or 5 ``` +--> -**Manual installation** Powertools for AWS Lambda (Java) dependencies are available in Maven Central. You can use your favourite dependency management tool to install it * [Maven](https://maven.apache.org/) * [Gradle](https://gradle.org) -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. - -=== "Maven Java 11+" +=== "Maven" ```xml <dependencies> @@ -94,7 +91,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>{{ powertools.version }}</version> </dependency> <dependency> @@ -102,17 +99,24 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl <artifactId>powertools-metrics</artifactId> <version>{{ powertools.version }}</version> </dependency> + <!-- Make sure to include AspectJ runtime compatible with plugin version --> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>1.9.22</version> + </dependency> ... </dependencies> ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> <build> <plugins> ... <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>11</source> <!-- or higher --> <target>11</target> <!-- or higher --> @@ -132,6 +136,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -145,83 +157,28 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </build> ``` -=== "Maven Java 1.8" - - ```xml - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" +=== "Gradle" ```groovy + plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.2.2' } + + // the freefair aspect plugins targets gradle 8.2.1 + // https://docs.freefair.io/gradle-plugins/8.2.2/reference/ + wrapper { + gradleVersion = "8.2.1" + } repositories { mavenCentral() } dependencies { - aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' + // Note: This AspectJ configuration is not needed when using the functional approach + aspect 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' } @@ -230,45 +187,45 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl targetCompatibility = 11 ``` -=== "Gradle Java 1.8" +???+ tip "Don't want to use AspectJ?" + Powertools for AWS Lambda (Java) now provides a functional API that doesn't require AspectJ configuration. Learn more about the [functional approach](./usage-patterns.md#functional-approach). - ```groovy - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' - aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' - aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` +### Java Compatibility +Powertools for AWS Lambda (Java) supports all Java versions from 11 to 25 in line with the [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + +In addition to the functional approach, [Logging](./core/logging.md), [Metrics](./core/metrics.md), [Tracing](./core/tracing.md), [Parameters](./utilities/parameters.md), [Idempotency](./utilities/idempotency.md), [Validation](./utilities/validation.md), and [Large Messages](./utilities/large_messages.md) utilities support annotations using AspectJ, which require configuration of the `aspectjrt` runtime library. -???+ tip "Why a different configuration?" - Powertools for AWS Lambda (Java) is using [AspectJ](https://eclipse.dev/aspectj/doc/released/progguide/starting.html) internally - to handle annotations. Recently, in order to support Java 17 we had to move to `dev.aspectj:aspectj-maven-plugin` because - `org.codehaus.mojo:aspectj-maven-plugin` does not support Java 17. - Under the hood, `org.codehaus.mojo:aspectj-maven-plugin` is based on AspectJ 1.9.7, - while `dev.aspectj:aspectj-maven-plugin` is based on AspectJ 1.9.8, compiled for Java 11+. +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: + +```xml +<dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>1.9.??</version> +</dependency> +``` + +Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/release/JavaVersionCompatibility.adoc) to understand which AspectJ version to use based on your JDK version: + +| JDK version | aspectj version | +|-------------|------------------------| +| `11-17` | `1.9.20.1` (or higher) | +| `21` | `1.9.21` (or higher) | +| `25` | `1.9.25` (or higher) | ## Environment variables !!! info - **Explicit parameters take precedence over environment variables.** - -| Environment variable | Description | Utility | -| ------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | -| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | -| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | -| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logging](./core/logging) | -| **POWERTOOLS_LOG_LEVEL** | Sets logging level | [Logging](./core/logging) | -| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Enables/Disables tracing mode to capture method response | [Tracing](./core/tracing) | -| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Enables/Disables tracing mode to capture method error | [Tracing](./core/tracing) | + Explicit parameters take precedence over environment variables. + +| Environment variable | Description | Utility | +| -------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------- | +| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | +| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | +| **POWERTOOLS_METRICS_FUNCTION_NAME** | Function name used as dimension for the cold start metric | [Metrics](./core/metrics) | +| **POWERTOOLS_METRICS_DISABLED** | Disables all flushing of metrics | [Metrics](./core/metrics) | +| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logging](./core/logging) | +| **POWERTOOLS_LOG_LEVEL** | Sets logging level | [Logging](./core/logging) | +| **POWERTOOLS_LOGGER_LOG_EVENT** | Enables/Disables whether to log the incoming event when using the aspect | [Logging](./core/logging) | +| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Enables/Disables tracing mode to capture method response | [Tracing](./core/tracing) | +| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Enables/Disables tracing mode to capture method error | [Tracing](./core/tracing) | diff --git a/docs/javascript/aws-amplify.min.js b/docs/javascript/aws-amplify.min.js deleted file mode 100644 index f19b36a50..000000000 --- a/docs/javascript/aws-amplify.min.js +++ /dev/null @@ -1,108 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("aws_amplify",[],t):"object"==typeof exports?exports.aws_amplify=t():e.aws_amplify=t()}(this,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=157)}([function(e,t,n){"use strict";n.d(t,"a",(function(){return a})),n.d(t,"b",(function(){return u})),n.d(t,"f",(function(){return c})),n.d(t,"g",(function(){return f})),n.d(t,"h",(function(){return l})),n.d(t,"c",(function(){return h})),n.d(t,"e",(function(){return g})),n.d(t,"d",(function(){return m}));var r=n(1),i=function(){var e=[],t=[],n=new Set,a=function(n){return e.forEach((function(e){n.add(e.middleware,Object(r.__assign)({},e))})),t.forEach((function(e){n.addRelativeTo(e.middleware,Object(r.__assign)({},e))})),n},u=function(e){var t=[];return e.before.forEach((function(e){0===e.before.length&&0===e.after.length?t.push(e):t.push.apply(t,Object(r.__spread)(u(e)))})),t.push(e),e.after.reverse().forEach((function(e){0===e.before.length&&0===e.after.length?t.push(e):t.push.apply(t,Object(r.__spread)(u(e)))})),t},c=function(){var n,i=[],a=[],c={};return e.forEach((function(e){var t=Object(r.__assign)(Object(r.__assign)({},e),{before:[],after:[]});t.name&&(c[t.name]=t),i.push(t)})),t.forEach((function(e){var t=Object(r.__assign)(Object(r.__assign)({},e),{before:[],after:[]});t.name&&(c[t.name]=t),a.push(t)})),a.forEach((function(e){if(e.toMiddleware){var t=c[e.toMiddleware];if(void 0===t)throw new Error(e.toMiddleware+" is not found when adding "+(e.name||"anonymous")+" middleware "+e.relation+" "+e.toMiddleware);"after"===e.relation&&t.after.push(e),"before"===e.relation&&t.before.push(e)}})),(n=i,n.sort((function(e,t){return o[t.step]-o[e.step]||s[t.priority||"normal"]-s[e.priority||"normal"]}))).map(u).reduce((function(e,t){return e.push.apply(e,Object(r.__spread)(t)),e}),[]).map((function(e){return e.middleware}))},f={add:function(t,i){void 0===i&&(i={});var o=i.name,s=Object(r.__assign)({step:"initialize",priority:"normal",middleware:t},i);if(o){if(n.has(o))throw new Error("Duplicate middleware name '"+o+"'");n.add(o)}e.push(s)},addRelativeTo:function(e,i){var o=i.name,s=Object(r.__assign)({middleware:e},i);if(o){if(n.has(o))throw new Error("Duplicated middleware name '"+o+"'");n.add(o)}t.push(s)},clone:function(){return a(i())},use:function(e){e.applyToStack(f)},remove:function(r){return"string"==typeof r?function(r){var i=!1,o=function(e){return!e.name||e.name!==r||(i=!0,n.delete(r),!1)};return e=e.filter(o),t=t.filter(o),i}(r):function(r){var i=!1,o=function(e){return e.middleware!==r||(i=!0,e.name&&n.delete(e.name),!1)};return e=e.filter(o),t=t.filter(o),i}(r)},removeByTag:function(r){var i=!1,o=function(e){var t=e.tags,o=e.name;return!t||!t.includes(r)||(o&&n.delete(o),i=!0,!1)};return e=e.filter(o),t=t.filter(o),i},concat:function(e){var t=a(i());return t.use(e),t},applyToStack:a,resolve:function(e,t){var n,i;try{for(var o=Object(r.__values)(c().reverse()),s=o.next();!s.done;s=o.next()){e=(0,s.value)(e,t)}}catch(e){n={error:e}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}return e}};return f},o={initialize:5,serialize:4,build:3,finalizeRequest:2,deserialize:1},s={high:3,normal:2,low:1},a=function(){function e(e){this.middlewareStack=i(),this.config=e}return e.prototype.send=function(e,t,n){var r="function"!=typeof t?t:void 0,i="function"==typeof t?t:n,o=e.resolveMiddleware(this.middlewareStack,this.config,r);if(!i)return o(e).then((function(e){return e.output}));o(e).then((function(e){return i(null,e.output)}),(function(e){return i(e)})).catch((function(){}))},e.prototype.destroy=function(){this.config.requestHandler.destroy&&this.config.requestHandler.destroy()},e}(),u=function(){this.middlewareStack=i()};function c(e){return encodeURIComponent(e).replace(/[!'()*]/g,(function(e){return"%"+e.charCodeAt(0).toString(16)}))}var f=function(e){return Array.isArray(e)?e:[e]},l=function(e){for(var t in e)e.hasOwnProperty(t)&&void 0!==e[t]["#text"]?e[t]=e[t]["#text"]:"object"==typeof e[t]&&null!==e[t]&&(e[t]=l(e[t]));return e},d=function(){var e=Object.getPrototypeOf(this).constructor,t=Function.bind.apply(String,Object(r.__spread)([null],arguments)),n=new t;return Object.setPrototypeOf(n,e.prototype),n};d.prototype=Object.create(String.prototype,{constructor:{value:d,enumerable:!1,writable:!0,configurable:!0}}),Object.setPrototypeOf(d,String);var h=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Object(r.__extends)(t,e),t.prototype.deserializeJSON=function(){return JSON.parse(e.prototype.toString.call(this))},t.prototype.toJSON=function(){return e.prototype.toString.call(this)},t.fromObject=function(e){return e instanceof t?e:new t(e instanceof String||"string"==typeof e?e:JSON.stringify(e))},t}(d),p=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],v=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function g(e){var t=e.getUTCFullYear(),n=e.getUTCMonth(),r=e.getUTCDay(),i=e.getUTCDate(),o=e.getUTCHours(),s=e.getUTCMinutes(),a=e.getUTCSeconds();return p[r]+", "+(i<10?"0"+i:""+i)+" "+v[n]+" "+t+" "+(o<10?"0"+o:""+o)+":"+(s<10?"0"+s:""+s)+":"+(a<10?"0"+a:""+a)+" GMT"}var m="***SensitiveInformation***"},function(e,t,n){"use strict";n.r(t),n.d(t,"__extends",(function(){return i})),n.d(t,"__assign",(function(){return o})),n.d(t,"__rest",(function(){return s})),n.d(t,"__decorate",(function(){return a})),n.d(t,"__param",(function(){return u})),n.d(t,"__metadata",(function(){return c})),n.d(t,"__awaiter",(function(){return f})),n.d(t,"__generator",(function(){return l})),n.d(t,"__createBinding",(function(){return d})),n.d(t,"__exportStar",(function(){return h})),n.d(t,"__values",(function(){return p})),n.d(t,"__read",(function(){return v})),n.d(t,"__spread",(function(){return g})),n.d(t,"__spreadArrays",(function(){return m})),n.d(t,"__await",(function(){return b})),n.d(t,"__asyncGenerator",(function(){return y})),n.d(t,"__asyncDelegator",(function(){return w})),n.d(t,"__asyncValues",(function(){return _})),n.d(t,"__makeTemplateObject",(function(){return S})),n.d(t,"__importStar",(function(){return E})),n.d(t,"__importDefault",(function(){return M})),n.d(t,"__classPrivateFieldGet",(function(){return A})),n.d(t,"__classPrivateFieldSet",(function(){return I})); -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ -var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function i(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var o=function(){return(o=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function s(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n}function a(e,t,n,r){var i,o=arguments.length,s=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s}function u(e,t){return function(n,r){t(n,r,e)}}function c(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function f(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function l(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}function d(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}function h(e,t){for(var n in e)"default"===n||t.hasOwnProperty(n)||(t[n]=e[n])}function p(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function v(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s}function g(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(v(arguments[t]));return e}function m(){for(var e=0,t=0,n=arguments.length;t<n;t++)e+=arguments[t].length;var r=Array(e),i=0;for(t=0;t<n;t++)for(var o=arguments[t],s=0,a=o.length;s<a;s++,i++)r[i]=o[s];return r}function b(e){return this instanceof b?(this.v=e,this):new b(e)}function y(e,t,n){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var r,i=n.apply(e,t||[]),o=[];return r={},s("next"),s("throw"),s("return"),r[Symbol.asyncIterator]=function(){return this},r;function s(e){i[e]&&(r[e]=function(t){return new Promise((function(n,r){o.push([e,t,n,r])>1||a(e,t)}))})}function a(e,t){try{(n=i[e](t)).value instanceof b?Promise.resolve(n.value.v).then(u,c):f(o[0][2],n)}catch(e){f(o[0][3],e)}var n}function u(e){a("next",e)}function c(e){a("throw",e)}function f(e,t){e(t),o.shift(),o.length&&a(o[0][0],o[0][1])}}function w(e){var t,n;return t={},r("next"),r("throw",(function(e){throw e})),r("return"),t[Symbol.iterator]=function(){return this},t;function r(r,i){t[r]=e[r]?function(t){return(n=!n)?{value:b(e[r](t)),done:"return"===r}:i?i(t):t}:i}}function _(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=p(e),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,i){(function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)})(r,i,(t=e[n](t)).done,t.value)}))}}}function S(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}function E(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function M(e){return e&&e.__esModule?e:{default:e}}function A(e,t){if(!t.has(e))throw new TypeError("attempted to get private field on non-instance");return t.get(e)}function I(e,t,n){if(!t.has(e))throw new TypeError("attempted to set private field on non-instance");return t.set(e,n),n}},function(e,t,n){"use strict";n.d(t,"b",(function(){return r})),n.d(t,"a",(function(){return o}));var r=function(){function e(e){this.statusCode=e.statusCode,this.headers=e.headers||{},this.body=e.body}return e.isInstance=function(e){if(!e)return!1;var t=e;return"number"==typeof t.statusCode&&"object"==typeof t.headers},e}(),i=n(1),o=function(){function e(e){this.method=e.method||"GET",this.hostname=e.hostname||"localhost",this.port=e.port,this.query=e.query||{},this.headers=e.headers||{},this.body=e.body,this.protocol=e.protocol?":"!==e.protocol.substr(-1)?e.protocol+":":e.protocol:"https:",this.path=e.path?"/"!==e.path.charAt(0)?"/"+e.path:e.path:"/"}return e.isInstance=function(e){if(!e)return!1;var t=e;return"method"in t&&"protocol"in t&&"hostname"in t&&"path"in t&&"object"==typeof t.query&&"object"==typeof t.headers},e.prototype.clone=function(){var t,n=new e(Object(i.__assign)(Object(i.__assign)({},this),{headers:Object(i.__assign)({},this.headers)}));return n.query&&(n.query=(t=n.query,Object.keys(t).reduce((function(e,n){var r,o=t[n];return Object(i.__assign)(Object(i.__assign)({},e),((r={})[n]=Array.isArray(o)?Object(i.__spread)(o):o,r))}),{}))),n},e}()},function(e,t,n){"use strict";n.d(t,"f",(function(){return A})),n.d(t,"t",(function(){return I})),n.d(t,"y",(function(){return k})),n.d(t,"s",(function(){return x})),n.d(t,"e",(function(){return C})),n.d(t,"x",(function(){return P})),n.d(t,"g",(function(){return N})),n.d(t,"h",(function(){return R})),n.d(t,"d",(function(){return D})),n.d(t,"c",(function(){return U})),n.d(t,"b",(function(){return B})),n.d(t,"a",(function(){return j})),n.d(t,"u",(function(){return F})),n.d(t,"v",(function(){return q})),n.d(t,"i",(function(){return K})),n.d(t,"w",(function(){return H})),n.d(t,"j",(function(){return V})),n.d(t,"p",(function(){return G})),n.d(t,"k",(function(){return W})),n.d(t,"q",(function(){return $})),n.d(t,"l",(function(){return Y})),n.d(t,"n",(function(){return J})),n.d(t,"r",(function(){return Z})),n.d(t,"o",(function(){return X})),n.d(t,"m",(function(){return Q}));var r=n(6),i=n(32),o=n.n(i);function s(e){var t=new Error(e);return t.source="ulid",t}var a="0123456789ABCDEFGHJKMNPQRSTVWXYZ",u=a.length,c=Math.pow(2,48)-1;function f(e,t,n){return t>e.length-1?e:e.substr(0,t)+n+e.substr(t+1)}function l(e){var t=Math.floor(e()*u);return t===u&&(t=u-1),a.charAt(t)}function d(e,t){if(isNaN(e))throw new Error(e+" must be a number");if(e>c)throw s("cannot encode time greater than "+c);if(e<0)throw s("time must be positive");if(!1===Number.isInteger(e))throw s("time must be an integer");for(var n=void 0,r="";t>0;t--)r=a.charAt(n=e%u)+r,e=(e-n)/u;return r}function h(e,t){for(var n="";e>0;e--)n=l(t)+n;return n}function p(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments[1];t||(t="undefined"!=typeof window?window:null);var r=t&&(t.crypto||t.msCrypto);if(r)return function(){var e=new Uint8Array(1);return r.getRandomValues(e),e[0]/255};try{var i=n(485);return function(){return i.randomBytes(1).readUInt8()/255}}catch(e){}if(e){try{console.error("secure crypto unusable, falling back to insecure Math.random()!")}catch(e){}return function(){return Math.random()}}throw s("secure crypto unusable, insecure Math.random not allowed")}function v(e){e||(e=p());var t=0,n=void 0;return function(r){if(isNaN(r)&&(r=Date.now()),r<=t){var i=n=function(e){for(var t=void 0,n=e.length,r=void 0,i=void 0,o=u-1;!t&&n-- >=0;){if(r=e[n],-1===(i=a.indexOf(r)))throw s("incorrectly encoded string");i!==o?t=f(e,n,a[i+1]):e=f(e,n,a[0])}if("string"==typeof t)return t;throw s("cannot increment this string")}(n);return d(t,10)+i}t=r;var o=n=h(16,e);return d(r,10)+o}}g||(g=p());var g,m=n(109),b=n(4);function y(e){return(y="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)}var w,_=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},S=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},E=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},M=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},A=function(e,t){if(void 0===t&&(t=!0),t)throw new Error("Invalid "+e)},I=function(e){return void 0===e||null==e},k=function e(t,n,r){var i,o=!1;if(0===r.length)return!0;switch(n){case"not":i="every",o=!0;break;case"and":i="every";break;case"or":i="some";break;default:A(n)}var s=r[i]((function(n){if(Object(b.k)(n)){var r=n.field,i=n.operator,o=n.operand,s=t[r];return O(s,i,o)}if(Object(b.j)(n)){var a=n.type,u=n.predicates;return e(t,a,u)}throw new Error("Not a predicate or group")}));return o?!s:s},O=function(e,t,n){switch(t){case"ne":return e!==n;case"eq":return e===n;case"le":return e<=n;case"lt":return e<n;case"ge":return e>=n;case"gt":return e>n;case"between":var r=E(n,2),i=r[0],o=r[1];return e>=i&&e<=o;case"beginsWith":return e.startsWith(n);case"contains":return e.indexOf(n)>-1;case"notContains":return-1===e.indexOf(n);default:return A(t,!1),!1}},x=function(e){return e&&"function"==typeof e.copyOf},C=function(e){var t={};return Object.keys(e.models).forEach((function(n){t[n]={indexes:[],relationTypes:[]};var r=e.models[n];Object.keys(r.fields).forEach((function(e){var i=r.fields[e];if("object"===y(i.type)&&"model"in i.type){var o=i.association.connectionType;t[n].relationTypes.push({fieldName:i.name,modelName:i.type.model,relationType:o,targetName:i.association.targetName,associatedWith:i.association.associatedWith}),"BELONGS_TO"===o&&t[n].indexes.push(i.association.targetName)}})),r.attributes&&r.attributes.forEach((function(e){if("key"===e.type){var r=e.properties.fields;r&&r.forEach((function(e){t[n].indexes.includes(e)||t[n].indexes.push(e)}))}}))})),t},T=new WeakMap,P=function(e,t,n,r,i){var o=n.relationships,s=i(n.name,e),a=o[e],u=[],c=s.copyOf(t,(function(e){a.relationTypes.forEach((function(o){var s=i(n.name,o.modelName);switch(o.relationType){case"HAS_ONE":if(t[o.fieldName]){var a=void 0;try{a=r(s,t[o.fieldName])}catch(e){}u.push({modelName:o.modelName,item:t[o.fieldName],instance:a}),e[o.fieldName]=e[o.fieldName].id}break;case"BELONGS_TO":if(t[o.fieldName]){a=void 0;try{a=r(s,t[o.fieldName])}catch(e){}e[o.fieldName]._deleted||u.push({modelName:o.modelName,item:t[o.fieldName],instance:a})}e[o.targetName]=e[o.fieldName]?e[o.fieldName].id:null,delete e[o.fieldName];break;case"HAS_MANY":break;default:A(o.relationType)}}))}));u.unshift({modelName:e,item:c,instance:c}),T.has(n)||T.set(n,Array.from(n.modelTopologicalOrdering.keys()));var f=T.get(n);return u.sort((function(e,t){return f.indexOf(e.modelName)-f.indexOf(t.modelName)})),u},N=function(e,t){var n="";return e.some((function(e){e.modelName===t&&(n=e.targetName)})),n},R=function(e,t){return e.find((function(e){return e===t}))};!function(e){e.DATASTORE="datastore",e.USER="user",e.SYNC="sync",e.STORAGE="storage"}(w||(w={}));var L,j=w.DATASTORE,D=w.USER,U=w.SYNC,B=w.STORAGE,F=function(){return new Promise((function(e){var t,n=Object(m.v4)(),r=function(){L=!1,e(!0)},i=function(){return _(void 0,void 0,void 0,(function(){return S(this,(function(r){switch(r.label){case 0:return t&&t.result&&"function"==typeof t.result.close?[4,t.result.close()]:[3,2];case 1:r.sent(),r.label=2;case 2:return[4,indexedDB.deleteDatabase(n)];case 3:return r.sent(),L=!0,[2,e(!1)]}}))}))};return!0===L?i():!1===L||null===indexedDB?r():((t=indexedDB.open(n)).onerror=r,void(t.onsuccess=i))}))},z=function(){return(e=1,r.Buffer.from(o.a.lib.WordArray.random(e).toString(),"hex")).readUInt8(0)/255;var e};function q(e){var t=v(z);return function(){return t(e)}}function K(){return"undefined"!=typeof performance&&performance&&"function"==typeof performance.now?0|performance.now():Date.now()}function H(e){return function(t,n){var r,i;try{for(var o=M(e),s=o.next();!s.done;s=o.next()){var a=s.value,u=a.field,c=a.sortDirection===b.e.ASCENDING?1:-1;if(t[u]<n[u])return-1*c;if(t[u]>n[u])return 1*c}}catch(e){r={error:e}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(r)throw r.error}}return 0}}var V=function(e){return!!/^\d{4}-\d{2}-\d{2}(Z|[+-]\d{2}:\d{2}($|:\d{2}))?$/.exec(e)},G=function(e){return!!/^\d{2}:\d{2}(:\d{2}(.\d+)?)?(Z|[+-]\d{2}:\d{2}($|:\d{2}))?$/.exec(e)},W=function(e){return!!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(.\d+)?)?(Z|[+-]\d{2}:\d{2}($|:\d{2}))?$/.exec(e)},$=function(e){return!!/^\d+$/.exec(String(e))},Y=function(e){return!!/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.exec(e)},J=function(e){try{return JSON.parse(e),!0}catch(e){return!1}},Z=function(e){try{return!!new URL(e)}catch(e){return!1}},X=function(e){return!!/^\+?\d[\d\s-]+$/.exec(e)},Q=function(e){return!!/((^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$))$/.exec(e)}},function(e,t,n){"use strict";n.d(t,"l",(function(){return f})),n.d(t,"m",(function(){return l})),n.d(t,"b",(function(){return r})),n.d(t,"g",(function(){return d})),n.d(t,"h",(function(){return h})),n.d(t,"i",(function(){return p})),n.d(t,"f",(function(){return v})),n.d(t,"c",(function(){return i})),n.d(t,"k",(function(){return g})),n.d(t,"j",(function(){return m})),n.d(t,"d",(function(){return o})),n.d(t,"e",(function(){return s})),n.d(t,"n",(function(){return b})),n.d(t,"a",(function(){return y}));var r,i,o,s,a=n(3),u=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},c=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}};function f(e){return e&&void 0!==e.pluralName}function l(e){return e&&e.targetName}function d(e){return e&&void 0!==r[e]}function h(e){return!(!e||!e.model)}function p(e){return!(!e||!e.nonModel)}function v(e){return!(!e||!e.enum)}function g(e){return e&&void 0!==e.field}function m(e){return e&&void 0!==e.type}function b(e,t){return u(this,void 0,void 0,(function(){return c(this,(function(n){return[2,{modelConstructor:e,conditionProducer:t}]}))}))}!function(e){e[e.ID=0]="ID",e[e.String=1]="String",e[e.Int=2]="Int",e[e.Float=3]="Float",e[e.Boolean=4]="Boolean",e[e.AWSDate=5]="AWSDate",e[e.AWSTime=6]="AWSTime",e[e.AWSDateTime=7]="AWSDateTime",e[e.AWSTimestamp=8]="AWSTimestamp",e[e.AWSEmail=9]="AWSEmail",e[e.AWSJSON=10]="AWSJSON",e[e.AWSURL=11]="AWSURL",e[e.AWSPhone=12]="AWSPhone",e[e.AWSIPAddress=13]="AWSIPAddress"}(r||(r={})),function(e){e.getJSType=function(e){switch(e){case"Boolean":return"boolean";case"ID":case"String":case"AWSDate":case"AWSTime":case"AWSDateTime":case"AWSEmail":case"AWSJSON":case"AWSURL":case"AWSPhone":case"AWSIPAddress":return"string";case"Int":case"Float":case"AWSTimestamp":return"number";default:Object(a.f)(e)}},e.getValidationFunction=function(e){switch(e){case"AWSDate":return a.j;case"AWSTime":return a.p;case"AWSDateTime":return a.k;case"AWSTimestamp":return a.q;case"AWSEmail":return a.l;case"AWSJSON":return a.n;case"AWSURL":return a.r;case"AWSPhone":return a.o;case"AWSIPAddress":return a.m;default:return}}}(r||(r={})),function(e){e.INSERT="INSERT",e.UPDATE="UPDATE",e.DELETE="DELETE"}(i||(i={})),function(e){e[e.FIRST=0]="FIRST",e[e.LAST=1]="LAST"}(o||(o={})),function(e){e.ASCENDING="ASCENDING",e.DESCENDING="DESCENDING"}(s||(s={}));var y=Symbol("DISCARD")},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(19),i=n(50),o=(n(142),n(89),{userAgent:i.a.userAgent});r.a},function(e,t,n){"use strict";(function(e){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh <http://feross.org> - * @license MIT - */ -var r=n(269),i=n(270),o=n(160);function s(){return u.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function a(e,t){if(s()<t)throw new RangeError("Invalid typed array length");return u.TYPED_ARRAY_SUPPORT?(e=new Uint8Array(t)).__proto__=u.prototype:(null===e&&(e=new u(t)),e.length=t),e}function u(e,t,n){if(!(u.TYPED_ARRAY_SUPPORT||this instanceof u))return new u(e,t,n);if("number"==typeof e){if("string"==typeof t)throw new Error("If encoding is specified then the first argument must be a string");return l(this,e)}return c(this,e,t,n)}function c(e,t,n,r){if("number"==typeof t)throw new TypeError('"value" argument must not be a number');return"undefined"!=typeof ArrayBuffer&&t instanceof ArrayBuffer?function(e,t,n,r){if(t.byteLength,n<0||t.byteLength<n)throw new RangeError("'offset' is out of bounds");if(t.byteLength<n+(r||0))throw new RangeError("'length' is out of bounds");t=void 0===n&&void 0===r?new Uint8Array(t):void 0===r?new Uint8Array(t,n):new Uint8Array(t,n,r);u.TYPED_ARRAY_SUPPORT?(e=t).__proto__=u.prototype:e=d(e,t);return e}(e,t,n,r):"string"==typeof t?function(e,t,n){"string"==typeof n&&""!==n||(n="utf8");if(!u.isEncoding(n))throw new TypeError('"encoding" must be a valid string encoding');var r=0|p(t,n),i=(e=a(e,r)).write(t,n);i!==r&&(e=e.slice(0,i));return e}(e,t,n):function(e,t){if(u.isBuffer(t)){var n=0|h(t.length);return 0===(e=a(e,n)).length||t.copy(e,0,0,n),e}if(t){if("undefined"!=typeof ArrayBuffer&&t.buffer instanceof ArrayBuffer||"length"in t)return"number"!=typeof t.length||(r=t.length)!=r?a(e,0):d(e,t);if("Buffer"===t.type&&o(t.data))return d(e,t.data)}var r;throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")}(e,t)}function f(e){if("number"!=typeof e)throw new TypeError('"size" argument must be a number');if(e<0)throw new RangeError('"size" argument must not be negative')}function l(e,t){if(f(t),e=a(e,t<0?0:0|h(t)),!u.TYPED_ARRAY_SUPPORT)for(var n=0;n<t;++n)e[n]=0;return e}function d(e,t){var n=t.length<0?0:0|h(t.length);e=a(e,n);for(var r=0;r<n;r+=1)e[r]=255&t[r];return e}function h(e){if(e>=s())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s().toString(16)+" bytes");return 0|e}function p(e,t){if(u.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return F(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return z(e).length;default:if(r)return F(e).length;t=(""+t).toLowerCase(),r=!0}}function v(e,t,n){var r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return x(this,t,n);case"utf8":case"utf-8":return I(this,t,n);case"ascii":return k(this,t,n);case"latin1":case"binary":return O(this,t,n);case"base64":return A(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function g(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function m(e,t,n,r,i){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=i?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(i)return-1;n=e.length-1}else if(n<0){if(!i)return-1;n=0}if("string"==typeof t&&(t=u.from(t,r)),u.isBuffer(t))return 0===t.length?-1:b(e,t,n,r,i);if("number"==typeof t)return t&=255,u.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):b(e,[t],n,r,i);throw new TypeError("val must be string, number or Buffer")}function b(e,t,n,r,i){var o,s=1,a=e.length,u=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;s=2,a/=2,u/=2,n/=2}function c(e,t){return 1===s?e[t]:e.readUInt16BE(t*s)}if(i){var f=-1;for(o=n;o<a;o++)if(c(e,o)===c(t,-1===f?0:o-f)){if(-1===f&&(f=o),o-f+1===u)return f*s}else-1!==f&&(o-=o-f),f=-1}else for(n+u>a&&(n=a-u),o=n;o>=0;o--){for(var l=!0,d=0;d<u;d++)if(c(e,o+d)!==c(t,d)){l=!1;break}if(l)return o}return-1}function y(e,t,n,r){n=Number(n)||0;var i=e.length-n;r?(r=Number(r))>i&&(r=i):r=i;var o=t.length;if(o%2!=0)throw new TypeError("Invalid hex string");r>o/2&&(r=o/2);for(var s=0;s<r;++s){var a=parseInt(t.substr(2*s,2),16);if(isNaN(a))return s;e[n+s]=a}return s}function w(e,t,n,r){return q(F(t,e.length-n),e,n,r)}function _(e,t,n,r){return q(function(e){for(var t=[],n=0;n<e.length;++n)t.push(255&e.charCodeAt(n));return t}(t),e,n,r)}function S(e,t,n,r){return _(e,t,n,r)}function E(e,t,n,r){return q(z(t),e,n,r)}function M(e,t,n,r){return q(function(e,t){for(var n,r,i,o=[],s=0;s<e.length&&!((t-=2)<0);++s)n=e.charCodeAt(s),r=n>>8,i=n%256,o.push(i),o.push(r);return o}(t,e.length-n),e,n,r)}function A(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function I(e,t,n){n=Math.min(e.length,n);for(var r=[],i=t;i<n;){var o,s,a,u,c=e[i],f=null,l=c>239?4:c>223?3:c>191?2:1;if(i+l<=n)switch(l){case 1:c<128&&(f=c);break;case 2:128==(192&(o=e[i+1]))&&(u=(31&c)<<6|63&o)>127&&(f=u);break;case 3:o=e[i+1],s=e[i+2],128==(192&o)&&128==(192&s)&&(u=(15&c)<<12|(63&o)<<6|63&s)>2047&&(u<55296||u>57343)&&(f=u);break;case 4:o=e[i+1],s=e[i+2],a=e[i+3],128==(192&o)&&128==(192&s)&&128==(192&a)&&(u=(15&c)<<18|(63&o)<<12|(63&s)<<6|63&a)>65535&&u<1114112&&(f=u)}null===f?(f=65533,l=1):f>65535&&(f-=65536,r.push(f>>>10&1023|55296),f=56320|1023&f),r.push(f),i+=l}return function(e){var t=e.length;if(t<=4096)return String.fromCharCode.apply(String,e);var n="",r=0;for(;r<t;)n+=String.fromCharCode.apply(String,e.slice(r,r+=4096));return n}(r)}t.Buffer=u,t.SlowBuffer=function(e){+e!=e&&(e=0);return u.alloc(+e)},t.INSPECT_MAX_BYTES=50,u.TYPED_ARRAY_SUPPORT=void 0!==e.TYPED_ARRAY_SUPPORT?e.TYPED_ARRAY_SUPPORT:function(){try{var e=new Uint8Array(1);return e.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===e.foo()&&"function"==typeof e.subarray&&0===e.subarray(1,1).byteLength}catch(e){return!1}}(),t.kMaxLength=s(),u.poolSize=8192,u._augment=function(e){return e.__proto__=u.prototype,e},u.from=function(e,t,n){return c(null,e,t,n)},u.TYPED_ARRAY_SUPPORT&&(u.prototype.__proto__=Uint8Array.prototype,u.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&u[Symbol.species]===u&&Object.defineProperty(u,Symbol.species,{value:null,configurable:!0})),u.alloc=function(e,t,n){return function(e,t,n,r){return f(t),t<=0?a(e,t):void 0!==n?"string"==typeof r?a(e,t).fill(n,r):a(e,t).fill(n):a(e,t)}(null,e,t,n)},u.allocUnsafe=function(e){return l(null,e)},u.allocUnsafeSlow=function(e){return l(null,e)},u.isBuffer=function(e){return!(null==e||!e._isBuffer)},u.compare=function(e,t){if(!u.isBuffer(e)||!u.isBuffer(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var n=e.length,r=t.length,i=0,o=Math.min(n,r);i<o;++i)if(e[i]!==t[i]){n=e[i],r=t[i];break}return n<r?-1:r<n?1:0},u.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"latin1":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},u.concat=function(e,t){if(!o(e))throw new TypeError('"list" argument must be an Array of Buffers');if(0===e.length)return u.alloc(0);var n;if(void 0===t)for(t=0,n=0;n<e.length;++n)t+=e[n].length;var r=u.allocUnsafe(t),i=0;for(n=0;n<e.length;++n){var s=e[n];if(!u.isBuffer(s))throw new TypeError('"list" argument must be an Array of Buffers');s.copy(r,i),i+=s.length}return r},u.byteLength=p,u.prototype._isBuffer=!0,u.prototype.swap16=function(){var e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var t=0;t<e;t+=2)g(this,t,t+1);return this},u.prototype.swap32=function(){var e=this.length;if(e%4!=0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(var t=0;t<e;t+=4)g(this,t,t+3),g(this,t+1,t+2);return this},u.prototype.swap64=function(){var e=this.length;if(e%8!=0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(var t=0;t<e;t+=8)g(this,t,t+7),g(this,t+1,t+6),g(this,t+2,t+5),g(this,t+3,t+4);return this},u.prototype.toString=function(){var e=0|this.length;return 0===e?"":0===arguments.length?I(this,0,e):v.apply(this,arguments)},u.prototype.equals=function(e){if(!u.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===u.compare(this,e)},u.prototype.inspect=function(){var e="",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),"<Buffer "+e+">"},u.prototype.compare=function(e,t,n,r,i){if(!u.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===i&&(i=this.length),t<0||n>e.length||r<0||i>this.length)throw new RangeError("out of range index");if(r>=i&&t>=n)return 0;if(r>=i)return-1;if(t>=n)return 1;if(this===e)return 0;for(var o=(i>>>=0)-(r>>>=0),s=(n>>>=0)-(t>>>=0),a=Math.min(o,s),c=this.slice(r,i),f=e.slice(t,n),l=0;l<a;++l)if(c[l]!==f[l]){o=c[l],s=f[l];break}return o<s?-1:s<o?1:0},u.prototype.includes=function(e,t,n){return-1!==this.indexOf(e,t,n)},u.prototype.indexOf=function(e,t,n){return m(this,e,t,n,!0)},u.prototype.lastIndexOf=function(e,t,n){return m(this,e,t,n,!1)},u.prototype.write=function(e,t,n,r){if(void 0===t)r="utf8",n=this.length,t=0;else if(void 0===n&&"string"==typeof t)r=t,n=this.length,t=0;else{if(!isFinite(t))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");t|=0,isFinite(n)?(n|=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}var i=this.length-t;if((void 0===n||n>i)&&(n=i),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var o=!1;;)switch(r){case"hex":return y(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":return _(this,e,t,n);case"latin1":case"binary":return S(this,e,t,n);case"base64":return E(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return M(this,e,t,n);default:if(o)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),o=!0}},u.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function k(e,t,n){var r="";n=Math.min(e.length,n);for(var i=t;i<n;++i)r+=String.fromCharCode(127&e[i]);return r}function O(e,t,n){var r="";n=Math.min(e.length,n);for(var i=t;i<n;++i)r+=String.fromCharCode(e[i]);return r}function x(e,t,n){var r=e.length;(!t||t<0)&&(t=0),(!n||n<0||n>r)&&(n=r);for(var i="",o=t;o<n;++o)i+=B(e[o]);return i}function C(e,t,n){for(var r=e.slice(t,n),i="",o=0;o<r.length;o+=2)i+=String.fromCharCode(r[o]+256*r[o+1]);return i}function T(e,t,n){if(e%1!=0||e<0)throw new RangeError("offset is not uint");if(e+t>n)throw new RangeError("Trying to access beyond buffer length")}function P(e,t,n,r,i,o){if(!u.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>i||t<o)throw new RangeError('"value" argument is out of bounds');if(n+r>e.length)throw new RangeError("Index out of range")}function N(e,t,n,r){t<0&&(t=65535+t+1);for(var i=0,o=Math.min(e.length-n,2);i<o;++i)e[n+i]=(t&255<<8*(r?i:1-i))>>>8*(r?i:1-i)}function R(e,t,n,r){t<0&&(t=4294967295+t+1);for(var i=0,o=Math.min(e.length-n,4);i<o;++i)e[n+i]=t>>>8*(r?i:3-i)&255}function L(e,t,n,r,i,o){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function j(e,t,n,r,o){return o||L(e,0,n,4),i.write(e,t,n,r,23,4),n+4}function D(e,t,n,r,o){return o||L(e,0,n,8),i.write(e,t,n,r,52,8),n+8}u.prototype.slice=function(e,t){var n,r=this.length;if((e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t<e&&(t=e),u.TYPED_ARRAY_SUPPORT)(n=this.subarray(e,t)).__proto__=u.prototype;else{var i=t-e;n=new u(i,void 0);for(var o=0;o<i;++o)n[o]=this[o+e]}return n},u.prototype.readUIntLE=function(e,t,n){e|=0,t|=0,n||T(e,t,this.length);for(var r=this[e],i=1,o=0;++o<t&&(i*=256);)r+=this[e+o]*i;return r},u.prototype.readUIntBE=function(e,t,n){e|=0,t|=0,n||T(e,t,this.length);for(var r=this[e+--t],i=1;t>0&&(i*=256);)r+=this[e+--t]*i;return r},u.prototype.readUInt8=function(e,t){return t||T(e,1,this.length),this[e]},u.prototype.readUInt16LE=function(e,t){return t||T(e,2,this.length),this[e]|this[e+1]<<8},u.prototype.readUInt16BE=function(e,t){return t||T(e,2,this.length),this[e]<<8|this[e+1]},u.prototype.readUInt32LE=function(e,t){return t||T(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},u.prototype.readUInt32BE=function(e,t){return t||T(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},u.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||T(e,t,this.length);for(var r=this[e],i=1,o=0;++o<t&&(i*=256);)r+=this[e+o]*i;return r>=(i*=128)&&(r-=Math.pow(2,8*t)),r},u.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||T(e,t,this.length);for(var r=t,i=1,o=this[e+--r];r>0&&(i*=256);)o+=this[e+--r]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*t)),o},u.prototype.readInt8=function(e,t){return t||T(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},u.prototype.readInt16LE=function(e,t){t||T(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},u.prototype.readInt16BE=function(e,t){t||T(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},u.prototype.readInt32LE=function(e,t){return t||T(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 t||T(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},u.prototype.readFloatLE=function(e,t){return t||T(e,4,this.length),i.read(this,e,!0,23,4)},u.prototype.readFloatBE=function(e,t){return t||T(e,4,this.length),i.read(this,e,!1,23,4)},u.prototype.readDoubleLE=function(e,t){return t||T(e,8,this.length),i.read(this,e,!0,52,8)},u.prototype.readDoubleBE=function(e,t){return t||T(e,8,this.length),i.read(this,e,!1,52,8)},u.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||P(this,e,t,n,Math.pow(2,8*n)-1,0);var i=1,o=0;for(this[t]=255&e;++o<n&&(i*=256);)this[t+o]=e/i&255;return t+n},u.prototype.writeUIntBE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||P(this,e,t,n,Math.pow(2,8*n)-1,0);var i=n-1,o=1;for(this[t+i]=255&e;--i>=0&&(o*=256);)this[t+i]=e/o&255;return t+n},u.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,1,255,0),u.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},u.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,65535,0),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):N(this,e,t,!0),t+2},u.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,65535,0),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):N(this,e,t,!1),t+2},u.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,4294967295,0),u.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):R(this,e,t,!0),t+4},u.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,4294967295,0),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):R(this,e,t,!1),t+4},u.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var i=Math.pow(2,8*n-1);P(this,e,t,n,i-1,-i)}var o=0,s=1,a=0;for(this[t]=255&e;++o<n&&(s*=256);)e<0&&0===a&&0!==this[t+o-1]&&(a=1),this[t+o]=(e/s>>0)-a&255;return t+n},u.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var i=Math.pow(2,8*n-1);P(this,e,t,n,i-1,-i)}var o=n-1,s=1,a=0;for(this[t+o]=255&e;--o>=0&&(s*=256);)e<0&&0===a&&0!==this[t+o+1]&&(a=1),this[t+o]=(e/s>>0)-a&255;return t+n},u.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,1,127,-128),u.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},u.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,32767,-32768),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):N(this,e,t,!0),t+2},u.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,32767,-32768),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):N(this,e,t,!1),t+2},u.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,2147483647,-2147483648),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):R(this,e,t,!0),t+4},u.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):R(this,e,t,!1),t+4},u.prototype.writeFloatLE=function(e,t,n){return j(this,e,t,!0,n)},u.prototype.writeFloatBE=function(e,t,n){return j(this,e,t,!1,n)},u.prototype.writeDoubleLE=function(e,t,n){return D(this,e,t,!0,n)},u.prototype.writeDoubleBE=function(e,t,n){return D(this,e,t,!1,n)},u.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r<n&&(r=n),r===n)return 0;if(0===e.length||0===this.length)return 0;if(t<0)throw new RangeError("targetStart out of bounds");if(n<0||n>=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t<r-n&&(r=e.length-t+n);var i,o=r-n;if(this===e&&n<t&&t<r)for(i=o-1;i>=0;--i)e[i+t]=this[i+n];else if(o<1e3||!u.TYPED_ARRAY_SUPPORT)for(i=0;i<o;++i)e[i+t]=this[i+n];else Uint8Array.prototype.set.call(e,this.subarray(n,n+o),t);return o},u.prototype.fill=function(e,t,n,r){if("string"==typeof e){if("string"==typeof t?(r=t,t=0,n=this.length):"string"==typeof n&&(r=n,n=this.length),1===e.length){var i=e.charCodeAt(0);i<256&&(e=i)}if(void 0!==r&&"string"!=typeof r)throw new TypeError("encoding must be a string");if("string"==typeof r&&!u.isEncoding(r))throw new TypeError("Unknown encoding: "+r)}else"number"==typeof e&&(e&=255);if(t<0||this.length<t||this.length<n)throw new RangeError("Out of range index");if(n<=t)return this;var o;if(t>>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(o=t;o<n;++o)this[o]=e;else{var s=u.isBuffer(e)?e:F(new u(e,r).toString()),a=s.length;for(o=0;o<n-t;++o)this[o+t]=s[o%a]}return this};var U=/[^+\/0-9A-Za-z-_]/g;function B(e){return e<16?"0"+e.toString(16):e.toString(16)}function F(e,t){var n;t=t||1/0;for(var r=e.length,i=null,o=[],s=0;s<r;++s){if((n=e.charCodeAt(s))>55295&&n<57344){if(!i){if(n>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(s+1===r){(t-=3)>-1&&o.push(239,191,189);continue}i=n;continue}if(n<56320){(t-=3)>-1&&o.push(239,191,189),i=n;continue}n=65536+(i-55296<<10|n-56320)}else i&&(t-=3)>-1&&o.push(239,191,189);if(i=null,n<128){if((t-=1)<0)break;o.push(n)}else if(n<2048){if((t-=2)<0)break;o.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;o.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return o}function z(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(U,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function q(e,t,n,r){for(var i=0;i<r&&!(i+n>=t.length||i>=e.length);++i)t[i+n]=e[i];return i}}).call(this,n(31))},function(e,t){"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){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},function(e,t,n){ -/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */ -var r=n(6),i=r.Buffer;function o(e,t){for(var n in e)t[n]=e[n]}function s(e,t,n){return i(e,t,n)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=r:(o(r,t),t.Buffer=s),s.prototype=Object.create(i.prototype),o(i,s),s.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return i(e,t,n)},s.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=i(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},s.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return i(e)},s.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t,n){"use strict";n.d(t,"c",(function(){return o})),n.d(t,"b",(function(){return s})),n.d(t,"a",(function(){return a}));var r=n(3),i=new WeakSet;function o(e){return i.has(e)}Symbol("A predicate that matches all records");var s=function(){function e(){}return Object.defineProperty(e,"ALL",{get:function(){var e=function(e){return e};return i.add(e),e},enumerable:!0,configurable:!0}),e}(),a=function(){function e(){}return e.createPredicateBuilder=function(t){var n,i=t.name,o=new Set(Object.keys(t.fields)),s=new Proxy({},n={get:function(t,s,a){var u=s;switch(u){case"and":case"or":case"not":return function(t){var r={type:u,predicates:[]},i=new Proxy({},n);return e.predicateGroupsMap.set(i,r),t(i),e.predicateGroupsMap.get(a).predicates.push(r),a};default:Object(r.f)(u,!1)}var c=s;if(!o.has(c))throw new Error("Invalid field for model. field: "+c+", model: "+i);return function(t,n){return e.predicateGroupsMap.get(a).predicates.push({field:c,operator:t,operand:n}),a}}});return e.predicateGroupsMap.set(s,{type:"and",predicates:[]}),s},e.isValidPredicate=function(t){return e.predicateGroupsMap.has(t)},e.getPredicates=function(t,n){if(void 0===n&&(n=!0),n&&!e.isValidPredicate(t))throw new Error("The predicate is not valid");return e.predicateGroupsMap.get(t)},e.createFromExisting=function(t,n){if(n&&t)return n(e.createPredicateBuilder(t))},e.createForId=function(t,n){return e.createPredicateBuilder(t).id("eq",n)},e.predicateGroupsMap=new WeakMap,e}()},function(e,t,n){"use strict";n.d(t,"a",(function(){return s}));var r=n(1),i={name:"deserializerMiddleware",step:"deserialize",tags:["DESERIALIZER"]},o={name:"serializerMiddleware",step:"serialize",tags:["SERIALIZER"]};function s(e,t,n){return{applyToStack:function(s){s.add(function(e,t){return function(n,i){return function(o){return Object(r.__awaiter)(void 0,void 0,void 0,(function(){var s,a,u,c,f;return Object(r.__generator)(this,(function(l){switch(l.label){case 0:return s=i.logger,a=i.outputFilterSensitiveLog,[4,n(o)];case 1:return u=l.sent().response,"function"==typeof(null==s?void 0:s.debug)&&s.debug({httpResponse:u}),[4,t(u,e)];case 2:return c=l.sent(),c.$metadata,f=Object(r.__rest)(c,["$metadata"]),"function"==typeof(null==s?void 0:s.info)&&s.info({output:a(f)}),[2,{response:u,output:c}]}}))}))}}}(e,n),i),s.add(function(e,t){return function(n,i){return function(o){return Object(r.__awaiter)(void 0,void 0,void 0,(function(){var s,a,u;return Object(r.__generator)(this,(function(c){switch(c.label){case 0:return s=i.logger,a=i.inputFilterSensitiveLog,"function"==typeof(null==s?void 0:s.info)&&s.info({input:a(o.input)}),[4,t(o.input,e)];case 1:return u=c.sent(),"function"==typeof(null==s?void 0:s.debug)&&s.debug({httpRequest:u}),[2,n(Object(r.__assign)(Object(r.__assign)({},o),{request:u}))]}}))}))}}}(e,t),o)}}}},function(e,t,n){"use strict";n.d(t,"b",(function(){return o})),n.d(t,"a",(function(){return v})),n.d(t,"c",(function(){return m}));var r=n(1),i={name:"retryMiddleware",tags:["RETRY"],step:"finalizeRequest",priority:"high"},o=function(e){return{applyToStack:function(t){t.add(function(e){return function(t){return function(n){return Object(r.__awaiter)(void 0,void 0,void 0,(function(){return Object(r.__generator)(this,(function(r){return[2,e.retryStrategy.retry(t,n)]}))}))}}}(e),i)}}},s=n(2),a=["AuthFailure","InvalidSignatureException","RequestExpired","RequestInTheFuture","RequestTimeTooSkewed","SignatureDoesNotMatch"],u=["Throttling","ThrottlingException","ThrottledException","RequestThrottledException","TooManyRequestsException","ProvisionedThroughputExceededException","TransactionInProgressException","RequestLimitExceeded","BandwidthLimitExceeded","LimitExceededException","RequestThrottled","SlowDown","PriorRequestNotComplete","EC2ThrottledException"],c=["AbortError","TimeoutError","RequestTimeout","RequestTimeoutException"],f=[500,502,503,504],l=function(e){var t;return u.includes(e.name)||1==(null===(t=e.$retryable)||void 0===t?void 0:t.throttling)},d=n(27),h=function(e,t){return Math.floor(Math.min(2e4,Math.random()*Math.pow(2,t)*e))},p=function(e){return!!e&&(function(e){return void 0!==e.$retryable}(e)||function(e){return a.includes(e.name)}(e)||l(e)||function(e){var t;return c.includes(e.name)||f.includes((null===(t=e.$metadata)||void 0===t?void 0:t.httpStatusCode)||0)}(e))},v=3,g=function(){function e(e,t){var n,r,i,o,s,a,u,c;this.maxAttemptsProvider=e,this.retryDecider=null!==(n=null==t?void 0:t.retryDecider)&&void 0!==n?n:p,this.delayDecider=null!==(r=null==t?void 0:t.delayDecider)&&void 0!==r?r:h,this.retryQuota=null!==(i=null==t?void 0:t.retryQuota)&&void 0!==i?i:(s=o=500,a=o,u=function(e){return"TimeoutError"===e.name?10:5},c=function(e){return u(e)<=a},Object.freeze({hasRetryTokens:c,retrieveRetryTokens:function(e){if(!c(e))throw new Error("No retry token available");var t=u(e);return a-=t,t},releaseRetryTokens:function(e){a+=null!=e?e:1,a=Math.min(a,s)}}))}return e.prototype.shouldRetry=function(e,t,n){return t<n&&this.retryDecider(e)&&this.retryQuota.hasRetryTokens(e)},e.prototype.getMaxAttempts=function(){return Object(r.__awaiter)(this,void 0,void 0,(function(){var e;return Object(r.__generator)(this,(function(t){switch(t.label){case 0:return t.trys.push([0,2,,3]),[4,this.maxAttemptsProvider()];case 1:return e=t.sent(),[3,3];case 2:return t.sent(),e=v,[3,3];case 3:return[2,e]}}))}))},e.prototype.retry=function(e,t){return Object(r.__awaiter)(this,void 0,void 0,(function(){var n,i,o,a,u,c,f,h;return Object(r.__generator)(this,(function(p){switch(p.label){case 0:return i=0,o=0,[4,this.getMaxAttempts()];case 1:a=p.sent(),u=t.request,s.a.isInstance(u)&&(u.headers["amz-sdk-invocation-id"]=Object(d.v4)()),c=function(){var c,d,h,p,v;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return r.trys.push([0,2,,5]),s.a.isInstance(u)&&(u.headers["amz-sdk-request"]="attempt="+(i+1)+"; max="+a),[4,e(t)];case 1:return c=r.sent(),d=c.response,h=c.output,f.retryQuota.releaseRetryTokens(n),h.$metadata.attempts=i+1,h.$metadata.totalRetryDelay=o,[2,{value:{response:d,output:h}}];case 2:return p=r.sent(),i++,f.shouldRetry(p,i,a)?(n=f.retryQuota.retrieveRetryTokens(p),v=f.delayDecider(l(p)?500:100,i),o+=v,[4,new Promise((function(e){return setTimeout(e,v)}))]):[3,4];case 3:return r.sent(),[2,"continue"];case 4:throw p.$metadata||(p.$metadata={}),p.$metadata.attempts=i,p.$metadata.totalRetryDelay=o,p;case 5:return[2]}}))},f=this,p.label=2;case 2:return[5,c()];case 3:return"object"==typeof(h=p.sent())?[2,h.value]:[3,2];case 4:return[2]}}))}))},e}(),m=function(e){var t=b(e.maxAttempts);return Object(r.__assign)(Object(r.__assign)({},e),{maxAttempts:t,retryStrategy:e.retryStrategy||new g(t)})},b=function(e){if(void 0===e&&(e=v),"number"==typeof e){var t=Promise.resolve(e);return function(){return t}}return e}},function(e,t,n){"use strict";var r,i;function o(e){return e&&!!["provider"].find((function(t){return e.hasOwnProperty(t)}))}function s(e){return e&&!!["customProvider"].find((function(t){return e.hasOwnProperty(t)}))}function a(e){return e&&!!["customState"].find((function(t){return e.hasOwnProperty(t)}))}function u(e){return void 0!==e.redirectSignIn}function c(e){return!!e.username}n.d(t,"b",(function(){return r})),n.d(t,"e",(function(){return o})),n.d(t,"f",(function(){return s})),n.d(t,"c",(function(){return a})),n.d(t,"d",(function(){return u})),n.d(t,"a",(function(){return i})),n.d(t,"g",(function(){return c})),function(e){e.Cognito="COGNITO",e.Google="Google",e.Facebook="Facebook",e.Amazon="LoginWithAmazon",e.Apple="SignInWithApple"}(r||(r={})),function(e){e.NoConfig="noConfig",e.MissingAuthConfig="missingAuthConfig",e.EmptyUsername="emptyUsername",e.InvalidUsername="invalidUsername",e.EmptyPassword="emptyPassword",e.EmptyCode="emptyCode",e.SignUpError="signUpError",e.NoMFA="noMFA",e.InvalidMFA="invalidMFA",e.EmptyChallengeResponse="emptyChallengeResponse",e.NoUserSession="noUserSession",e.Default="default"}(i||(i={}))},function(e,t,n){"use strict";n.d(t,"a",(function(){return i})),n.d(t,"e",(function(){return d})),n.d(t,"c",(function(){return h})),n.d(t,"b",(function(){return p})),n.d(t,"d",(function(){return v})),n.d(t,"g",(function(){return g})),n.d(t,"h",(function(){return m})),n.d(t,"f",(function(){return b}));var r,i,o=n(4),s=n(3),a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};!function(e){e.LIST="query",e.CREATE="mutation",e.UPDATE="mutation",e.DELETE="mutation",e.GET="query"}(r||(r={})),function(e){e.CREATE="Create",e.UPDATE="Update",e.DELETE="Delete",e.GET="Get"}(i||(i={}));var u={_version:void 0,_lastChangedAt:void 0,_deleted:void 0},c=Object.keys(u);function f(e,t){var n=l(t),r=function(e,t){var n=[];return Object.values(t.fields).forEach((function(t){var r=t.name,i=t.type;if(Object(o.i)(i)){var s=e.nonModels[i.nonModel],a=Object.values(l(s)).map((function(e){return e.name})),u=[];Object.values(s.fields).forEach((function(t){var n=t.type,r=t.name;if(Object(o.i)(n)){var i=e.nonModels[n.nonModel];u.push(r+" { "+f(e,i)+" }")}})),n.push(r+" { "+a.join(" ")+" "+u.join(" ")+" }")}})),n}(e,t),i=function(e,t){var n=function(e){var t=[];Object(o.l)(e)&&e.attributes&&e.attributes.forEach((function(e){if(e.properties&&e.properties.rules){var n=e.properties.rules.find((function(e){return"owner"===e.allow}));n&&n.ownerField&&t.push(n.ownerField)}}));return t}(e);if(!t.owner&&n.includes("owner"))return["owner"];return[]}(t,n),a=Object.values(n).map((function(e){return e.name})).concat(i).concat(r);return Object(o.l)(t)&&(a=a.concat(c).concat(function(e){var t=[];return Object.values(e.fields).filter((function(e){var t=e.association;return t&&Object.keys(t).length})).forEach((function(e){var n=e.name,r=e.association,i=r.connectionType;switch(i){case"HAS_ONE":case"HAS_MANY":break;case"BELONGS_TO":Object(o.m)(r)&&t.push(n+" { id _deleted }");break;default:Object(s.f)(i)}})),t}(t))),a.join("\n")}function l(e){var t=e.fields;return Object.values(t).filter((function(e){return!(!Object(o.g)(e.type)&&!Object(o.f)(e.type))})).reduce((function(e,t){return e[t.name]=t,e}),{})}function d(e){var t=([].concat(e.attributes).find((function(e){return e&&"auth"===e.type}))||{}).properties,n=(void 0===t?{}:t).rules,r=[];return(void 0===n?[]:n).forEach((function(t){var n=t.identityClaim,i=void 0===n?"cognito:username":n,o=t.ownerField,s=void 0===o?"owner":o,a=t.operations,u=void 0===a?["create","update","delete","read"]:a,c=t.provider,f=void 0===c?"userPools":c,l=t.groupClaim,d=void 0===l?"cognito:groups":l,h=t.allow,p=void 0===h?"iam":h,v=t.groups,g=void 0===v?[]:v,m="owner"===p;if(u.includes("read")||m){var b={identityClaim:i,ownerField:s,provider:f,groupClaim:d,authStrategy:p,groups:g,areSubscriptionsPublic:!1};if(m){var y=([].concat(e.attributes).find((function(e){return e&&"model"===e.type}))||{}).properties,w=(void 0===y?{}:y).subscriptions,_=(void 0===w?{}:w).level,S=void 0===_?"on":_;b.areSubscriptionsPublic=!u.includes("read")||"public"===S}m?r.push(b):r.unshift(b)}})),r}function h(e,t,n,r,i){var o=f(e,t),s=t.name,a=(t.pluralName,"on"+n+s),u="",c="";return r&&(u="($"+i+": String!)",c="("+i+": $"+i+")"),[n,a,"subscription operation"+u+"{\n\t\t\t"+a+c+"{\n\t\t\t\t"+o+"\n\t\t\t}\n\t\t}"]}function p(e,t,n){var o,a,u=f(e,t),c=t.name,l=t.pluralName,d=" ",h=" ";switch(n){case"LIST":o="sync"+l,d="($limit: Int, $nextToken: String, $lastSync: AWSTimestamp, $filter: Model"+c+"FilterInput)",h="(limit: $limit, nextToken: $nextToken, lastSync: $lastSync, filter: $filter)",u="items {\n\t\t\t\t\t\t\t"+u+"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnextToken\n\t\t\t\t\t\tstartedAt";break;case"CREATE":o="create"+c,d="($input: Create"+c+"Input!)",h="(input: $input)",a=i.CREATE;break;case"UPDATE":o="update"+c,d="($input: Update"+c+"Input!, $condition: Model"+c+"ConditionInput)",h="(input: $input, condition: $condition)",a=i.UPDATE;break;case"DELETE":o="delete"+c,d="($input: Delete"+c+"Input!, $condition: Model"+c+"ConditionInput)",h="(input: $input, condition: $condition)",a=i.DELETE;break;case"GET":o="get"+c,d="($id: ID!)",h="(id: $id)",a=i.GET;break;default:Object(s.f)(n)}return[[a,o,r[n]+" operation"+d+"{\n\t\t"+o+h+"{\n\t\t\t"+u+"\n\t\t}\n\t}"]]}function v(e,t,n,r,u,c,f,l,d){var h;switch(n){case o.c.INSERT:h=i.CREATE;break;case o.c.UPDATE:h=i.UPDATE;break;case o.c.DELETE:h=i.DELETE;break;default:Object(s.f)(n)}return l(f,a(a({},d?{id:d}:{}),{data:JSON.stringify(u),modelId:u.id,model:r.name,operation:h,condition:JSON.stringify(c)}))}function g(e){var t={};return e&&Array.isArray(e.predicates)?(e.predicates.forEach((function(e){var n;if(Object(o.k)(e)){var r=e.field,i=e.operator,s=e.operand;if("id"===r)return;t[r]=((n={})[i]=s,n)}else t[e.type]=g(e)})),t):t}function m(e){var t={};if(!e||!Array.isArray(e.predicates))return t;var n=e.type,r=e.predicates,i="and"===n||"or"===n;t[n]=i?[]:{};var s=function(e){return i?t[n].push(e):t[n]=e};return r.forEach((function(e){var t,n;if(Object(o.k)(e)){var r=e.field,i=e.operator,a=e.operand,u=((t={})[r]=((n={})[i]=a,n),t);s(u)}else s(m(e))})),t}function b(e,t){var n=e[t.groupClaim]||[];if("string"==typeof n){var r=void 0;try{r=JSON.parse(n)}catch(e){r=n}n=[].concat(r)}return n}},function(e,t,n){"use strict";var r=n(255),i=n.n(r).a;t.a=i},function(e,t,n){"use strict";n.d(t,"a",(function(){return r})),n.d(t,"b",(function(){return i}));var r=function(e){return"function"==typeof TextEncoder?function(e){return(new TextEncoder).encode(e)}(e):function(e){for(var t=[],n=0,r=e.length;n<r;n++){var i=e.charCodeAt(n);if(i<128)t.push(i);else if(i<2048)t.push(i>>6|192,63&i|128);else if(n+1<e.length&&55296==(64512&i)&&56320==(64512&e.charCodeAt(n+1))){var o=65536+((1023&i)<<10)+(1023&e.charCodeAt(++n));t.push(o>>18|240,o>>12&63|128,o>>6&63|128,63&o|128)}else t.push(i>>12|224,i>>6&63|128,63&i|128)}return Uint8Array.from(t)}(e)},i=function(e){return"function"==typeof TextDecoder?function(e){return new TextDecoder("utf-8").decode(e)}(e):function(e){for(var t="",n=0,r=e.length;n<r;n++){var i=e[n];if(i<128)t+=String.fromCharCode(i);else if(192<=i&&i<224){var o=e[++n];t+=String.fromCharCode((31&i)<<6|63&o)}else if(240<=i&&i<365){var s="%"+[i,e[++n],e[++n],e[++n]].map((function(e){return e.toString(16)})).join("%");t+=decodeURIComponent(s)}else t+=String.fromCharCode((15&i)<<12|(63&e[++n])<<6|63&e[++n])}return t}(e)}},function(e,t,n){"use strict";var r=n(366),i=n(367);function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}t.parse=y,t.resolve=function(e,t){return y(e,!1,!0).resolve(t)},t.resolveObject=function(e,t){return e?y(e,!1,!0).resolveObject(t):t},t.format=function(e){i.isString(e)&&(e=y(e));return e instanceof o?e.format():o.prototype.format.call(e)},t.Url=o;var s=/^([a-z0-9.+-]+:)/i,a=/:[0-9]*$/,u=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,c=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),f=["'"].concat(c),l=["%","/","?",";","#"].concat(f),d=["/","?","#"],h=/^[+a-z0-9A-Z_-]{0,63}$/,p=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,v={javascript:!0,"javascript:":!0},g={javascript:!0,"javascript:":!0},m={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},b=n(368);function y(e,t,n){if(e&&i.isObject(e)&&e instanceof o)return e;var r=new o;return r.parse(e,t,n),r}o.prototype.parse=function(e,t,n){if(!i.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),a=-1!==o&&o<e.indexOf("#")?"?":"#",c=e.split(a);c[0]=c[0].replace(/\\/g,"/");var y=e=c.join(a);if(y=y.trim(),!n&&1===e.split("#").length){var w=u.exec(y);if(w)return this.path=y,this.href=y,this.pathname=w[1],w[2]?(this.search=w[2],this.query=t?b.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var _=s.exec(y);if(_){var S=(_=_[0]).toLowerCase();this.protocol=S,y=y.substr(_.length)}if(n||_||y.match(/^\/\/[^@\/]+@[^@\/]+/)){var E="//"===y.substr(0,2);!E||_&&g[_]||(y=y.substr(2),this.slashes=!0)}if(!g[_]&&(E||_&&!m[_])){for(var M,A,I=-1,k=0;k<d.length;k++){-1!==(O=y.indexOf(d[k]))&&(-1===I||O<I)&&(I=O)}-1!==(A=-1===I?y.lastIndexOf("@"):y.lastIndexOf("@",I))&&(M=y.slice(0,A),y=y.slice(A+1),this.auth=decodeURIComponent(M)),I=-1;for(k=0;k<l.length;k++){var O;-1!==(O=y.indexOf(l[k]))&&(-1===I||O<I)&&(I=O)}-1===I&&(I=y.length),this.host=y.slice(0,I),y=y.slice(I),this.parseHost(),this.hostname=this.hostname||"";var x="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!x)for(var C=this.hostname.split(/\./),T=(k=0,C.length);k<T;k++){var P=C[k];if(P&&!P.match(h)){for(var N="",R=0,L=P.length;R<L;R++)P.charCodeAt(R)>127?N+="x":N+=P[R];if(!N.match(h)){var j=C.slice(0,k),D=C.slice(k+1),U=P.match(p);U&&(j.push(U[1]),D.unshift(U[2])),D.length&&(y="/"+D.join(".")+y),this.hostname=j.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),x||(this.hostname=r.toASCII(this.hostname));var B=this.port?":"+this.port:"",F=this.hostname||"";this.host=F+B,this.href+=this.host,x&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==y[0]&&(y="/"+y))}if(!v[S])for(k=0,T=f.length;k<T;k++){var z=f[k];if(-1!==y.indexOf(z)){var q=encodeURIComponent(z);q===z&&(q=escape(z)),y=y.split(z).join(q)}}var K=y.indexOf("#");-1!==K&&(this.hash=y.substr(K),y=y.slice(0,K));var H=y.indexOf("?");if(-1!==H?(this.search=y.substr(H),this.query=y.substr(H+1),t&&(this.query=b.parse(this.query)),y=y.slice(0,H)):t&&(this.search="",this.query={}),y&&(this.pathname=y),m[S]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){B=this.pathname||"";var V=this.search||"";this.path=B+V}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=(e=encodeURIComponent(e)).replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",r=this.hash||"",o=!1,s="";this.host?o=e+this.host:this.hostname&&(o=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(o+=":"+this.port)),this.query&&i.isObject(this.query)&&Object.keys(this.query).length&&(s=b.stringify(this.query));var a=this.search||s&&"?"+s||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||m[t])&&!1!==o?(o="//"+(o||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):o||(o=""),r&&"#"!==r.charAt(0)&&(r="#"+r),a&&"?"!==a.charAt(0)&&(a="?"+a),t+o+(n=n.replace(/[?#]/g,(function(e){return encodeURIComponent(e)})))+(a=a.replace("#","%23"))+r},o.prototype.resolve=function(e){return this.resolveObject(y(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if(i.isString(e)){var t=new o;t.parse(e,!1,!0),e=t}for(var n=new o,r=Object.keys(this),s=0;s<r.length;s++){var a=r[s];n[a]=this[a]}if(n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol){for(var u=Object.keys(e),c=0;c<u.length;c++){var f=u[c];"protocol"!==f&&(n[f]=e[f])}return m[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n}if(e.protocol&&e.protocol!==n.protocol){if(!m[e.protocol]){for(var l=Object.keys(e),d=0;d<l.length;d++){var h=l[d];n[h]=e[h]}return n.href=n.format(),n}if(n.protocol=e.protocol,e.host||g[e.protocol])n.pathname=e.pathname;else{for(var p=(e.pathname||"").split("/");p.length&&!(e.host=p.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==p[0]&&p.unshift(""),p.length<2&&p.unshift(""),n.pathname=p.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var v=n.pathname||"",b=n.search||"";n.path=v+b}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var y=n.pathname&&"/"===n.pathname.charAt(0),w=e.host||e.pathname&&"/"===e.pathname.charAt(0),_=w||y||n.host&&e.pathname,S=_,E=n.pathname&&n.pathname.split("/")||[],M=(p=e.pathname&&e.pathname.split("/")||[],n.protocol&&!m[n.protocol]);if(M&&(n.hostname="",n.port=null,n.host&&(""===E[0]?E[0]=n.host:E.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===p[0]?p[0]=e.host:p.unshift(e.host)),e.host=null),_=_&&(""===p[0]||""===E[0])),w)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,E=p;else if(p.length)E||(E=[]),E.pop(),E=E.concat(p),n.search=e.search,n.query=e.query;else if(!i.isNullOrUndefined(e.search)){if(M)n.hostname=n.host=E.shift(),(x=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@"))&&(n.auth=x.shift(),n.host=n.hostname=x.shift());return n.search=e.search,n.query=e.query,i.isNull(n.pathname)&&i.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!E.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var A=E.slice(-1)[0],I=(n.host||e.host||E.length>1)&&("."===A||".."===A)||""===A,k=0,O=E.length;O>=0;O--)"."===(A=E[O])?E.splice(O,1):".."===A?(E.splice(O,1),k++):k&&(E.splice(O,1),k--);if(!_&&!S)for(;k--;k)E.unshift("..");!_||""===E[0]||E[0]&&"/"===E[0].charAt(0)||E.unshift(""),I&&"/"!==E.join("/").substr(-1)&&E.push("");var x,C=""===E[0]||E[0]&&"/"===E[0].charAt(0);M&&(n.hostname=n.host=C?"":E.length?E.shift():"",(x=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@"))&&(n.auth=x.shift(),n.host=n.hostname=x.shift()));return(_=_||n.host&&E.length)&&!C&&E.unshift(""),E.length?n.pathname=E.join("/"):(n.pathname=null,n.path=null),i.isNull(n.pathname)&&i.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var e=this.host,t=a.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},function(e,t,n){"use strict";n.d(t,"a",(function(){return f})),n.d(t,"b",(function(){return l}));for(var r={},i=new Array(64),o=0,s="A".charCodeAt(0),a="Z".charCodeAt(0);o+s<=a;o++){var u=String.fromCharCode(o+s);r[u]=o,i[o]=u}for(o=0,s="a".charCodeAt(0),a="z".charCodeAt(0);o+s<=a;o++){u=String.fromCharCode(o+s);var c=o+26;r[u]=c,i[c]=u}for(o=0;o<10;o++){r[o.toString(10)]=o+52;u=o.toString(10),c=o+52;r[u]=c,i[c]=u}r["+"]=62,i[62]="+",r["/"]=63,i[63]="/";function f(e){var t=e.length/4*3;"=="===e.substr(-2)?t-=2:"="===e.substr(-1)&&t--;for(var n=new ArrayBuffer(t),i=new DataView(n),o=0;o<e.length;o+=4){for(var s=0,a=0,u=o,c=o+3;u<=c;u++)"="!==e[u]?(s|=r[e[u]]<<6*(c-u),a+=6):s>>=6;var f=o/4*3;s>>=a%8;for(var l=Math.floor(a/8),d=0;d<l;d++){var h=8*(l-d-1);i.setUint8(f+d,(s&255<<h)>>h)}}return new Uint8Array(n)}function l(e){for(var t="",n=0;n<e.length;n+=3){for(var r=0,o=0,s=n,a=Math.min(n+3,e.length);s<a;s++)r|=e[s]<<8*(a-s-1),o+=8;var u=Math.ceil(o/6);r<<=6*u-o;for(var c=1;c<=u;c++){var f=6*(u-c);t+=i[(r&63<<f)>>f]}t+="==".slice(0,4-u)}return t}},function(e,t,n){"use strict";n.d(t,"a",(function(){return s})),n.d(t,"b",(function(){return u}));var r=n(1),i=n(2),o=n(74);var s=function(){function e(e){void 0===e&&(e={}),this.httpOptions=e}return e.prototype.destroy=function(){},e.prototype.handle=function(e,t){var n=null==t?void 0:t.abortSignal,s=this.httpOptions.requestTimeout;if(null==n?void 0:n.aborted){var a=new Error("Request aborted");return a.name="AbortError",Promise.reject(a)}var u=e.path;if(e.query){var c=Object(o.a)(e.query);c&&(u+="?"+c)}var f=e.port,l=e.protocol+"//"+e.hostname+(f?":"+f:"")+u,d={body:e.body,headers:new Headers(e.headers),method:e.method};"undefined"!=typeof AbortController&&(d.signal=n);var h,p=new Request(l,d),v=[fetch(p).then((function(e){var t,n,o=e.headers,s={};try{for(var a=Object(r.__values)(o.entries()),u=a.next();!u.done;u=a.next()){var c=u.value;s[c[0]]=c[1]}}catch(e){t={error:e}}finally{try{u&&!u.done&&(n=a.return)&&n.call(a)}finally{if(t)throw t.error}}return void 0!==e.body?{response:new i.b({headers:s,statusCode:e.status,body:e.body})}:e.blob().then((function(t){return{response:new i.b({headers:s,statusCode:e.status,body:t})}}))})),(h=s,void 0===h&&(h=0),new Promise((function(e,t){h&&setTimeout((function(){var e=new Error("Request did not complete within "+h+" ms");e.name="TimeoutError",t(e)}),h)})))];return n&&v.push(new Promise((function(e,t){n.onabort=function(){var e=new Error("Request aborted");e.name="AbortError",t(e)}}))),Promise.race(v)},e}(),a=n(17),u=function(e){return"function"==typeof Blob&&e instanceof Blob?function(e){return Object(r.__awaiter)(this,void 0,void 0,(function(){var t,n;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return[4,c(e)];case 1:return t=r.sent(),n=Object(a.a)(t),[2,new Uint8Array(n)]}}))}))}(e):function(e){return Object(r.__awaiter)(this,void 0,void 0,(function(){var t,n,i,o,s,a,u;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:t=new Uint8Array(0),n=e.getReader(),i=!1,r.label=1;case 1:return i?[3,3]:[4,n.read()];case 2:return o=r.sent(),s=o.done,(a=o.value)&&(u=t,(t=new Uint8Array(u.length+a.length)).set(u),t.set(a,u.length)),i=s,[3,1];case 3:return[2,t]}}))}))}(e)};function c(e){return new Promise((function(t,n){var r=new FileReader;r.onloadend=function(){var e;if(2!==r.readyState)return n(new Error("Reader aborted too early"));var i=null!==(e=r.result)&&void 0!==e?e:"",o=i.indexOf(","),s=o>-1?o+1:i.length;t(i.substring(s))},r.onabort=function(){return n(new Error("Read aborted"))},r.onerror=function(){return n(r.error)},r.readAsDataURL(e)}))}},function(e,t,n){"use strict";n.d(t,"b",(function(){return s})),n.d(t,"a",(function(){return a}));var r=n(44),i=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},o=new r.a("Amplify"),s=function(){function e(){this._components=[],this._config={},this._modules={},this.Auth=null,this.Analytics=null,this.API=null,this.Credentials=null,this.Storage=null,this.I18n=null,this.Cache=null,this.PubSub=null,this.Interactions=null,this.Pushnotification=null,this.UI=null,this.XR=null,this.Predictions=null,this.DataStore=null,this.Logger=r.a,this.ServiceWorker=null}return e.prototype.register=function(e){o.debug("component registered in amplify",e),this._components.push(e),"function"==typeof e.getModuleName?(this._modules[e.getModuleName()]=e,this[e.getModuleName()]=e):o.debug("no getModuleName method for component",e),e.configure(this._config)},e.prototype.configure=function(e){var t=this;return e?(this._config=Object.assign(this._config,e),o.debug("amplify config",this._config),Object.entries(this._modules).forEach((function(e){var n=i(e,2),r=(n[0],n[1]);Object.keys(r).forEach((function(e){t._modules[e]&&(r[e]=t._modules[e])}))})),this._components.map((function(e){e.configure(t._config)})),this._config):this._config},e.prototype.addPluggable=function(e){e&&e.getCategory&&"function"==typeof e.getCategory&&this._components.map((function(t){t.addPluggable&&"function"==typeof t.addPluggable&&t.addPluggable(e)}))},e}(),a=new s},function(e,t){var n,r,i=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function a(e){if(n===setTimeout)return setTimeout(e,0);if((n===o||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:o}catch(e){n=o}try{r="function"==typeof clearTimeout?clearTimeout:s}catch(e){r=s}}();var u,c=[],f=!1,l=-1;function d(){f&&u&&(f=!1,u.length?c=u.concat(c):l=-1,c.length&&h())}function h(){if(!f){var e=a(d);f=!0;for(var t=c.length;t;){for(u=c,c=[];++l<t;)u&&u[l].run();l=-1,t=c.length}u=null,f=!1,function(e){if(r===clearTimeout)return clearTimeout(e);if((r===s||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(e);try{r(e)}catch(t){try{return r.call(null,e)}catch(t){return r.call(this,e)}}}(e)}}function p(e,t){this.fun=e,this.array=t}function v(){}i.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];c.push(new p(e,t)),1!==c.length||f||a(h)},p.prototype.run=function(){this.fun.apply(null,this.array)},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=v,i.addListener=v,i.once=v,i.off=v,i.removeListener=v,i.removeAllListeners=v,i.emit=v,i.prependListener=v,i.prependOnceListener=v,i.listeners=function(e){return[]},i.binding=function(e){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(e){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}},function(e,t,n){"use strict";n.d(t,"b",(function(){return o})),n.d(t,"a",(function(){return a}));var r=n(1),i=n(2);function o(e){return e}var s={name:"hostHeaderMiddleware",step:"build",priority:"low",tags:["HOST"]},a=function(e){return{applyToStack:function(t){t.add(function(e){return function(t){return function(n){return Object(r.__awaiter)(void 0,void 0,void 0,(function(){var o,s;return Object(r.__generator)(this,(function(r){return i.a.isInstance(n.request)?(o=n.request,s=(e.requestHandler.metadata||{}).handlerProtocol,(void 0===s?"":s).indexOf("h2")>=0&&!o.headers[":authority"]?(delete o.headers.host,o.headers[":authority"]=""):o.headers.host||(o.headers.host=o.hostname),[2,t(n)]):[2,t(n)]}))}))}}}(e),s)}}}},function(e,t,n){"use strict";n.d(t,"a",(function(){return i})),n.d(t,"b",(function(){return a}));var r=n(1),i=function(e){var t;return Object(r.__assign)(Object(r.__assign)({},e),{tls:null===(t=e.tls)||void 0===t||t,endpoint:e.endpoint?o(e):function(){return s(e)},isCustomEndpoint:!!e.endpoint})},o=function(e){var t=e.endpoint,n=e.urlParser;if("string"==typeof t){var r=Promise.resolve(n(t));return function(){return r}}if("object"==typeof t){var i=Promise.resolve(t);return function(){return i}}return t},s=function(e){return Object(r.__awaiter)(void 0,void 0,void 0,(function(){var t,n,i,o,s;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return t=e.tls,n=void 0===t||t,[4,e.region()];case 1:if(i=r.sent(),!new RegExp(/^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/).test(i))throw new Error("Invalid region in client config");return[4,e.regionInfoProvider(i)];case 2:if(!(o=(null!==(s=r.sent())&&void 0!==s?s:{}).hostname))throw new Error("Cannot resolve hostname from client config");return[2,e.urlParser((n?"https:":"http:")+"//"+o)]}}))}))},a=function(e){if(!e.region)throw new Error("Region is missing");return Object(r.__assign)(Object(r.__assign)({},e),{region:u(e.region)})},u=function(e){if("string"==typeof e){var t=Promise.resolve(e);return function(){return t}}return e}},function(e,t,n){"use strict";function r(e){return e}n.d(t,"b",(function(){return r})),n.d(t,"a",(function(){return a}));var i=n(1),o=n(2);var s={name:"getUserAgentMiddleware",step:"build",tags:["SET_USER_AGENT","USER_AGENT"]},a=function(e){return{applyToStack:function(t){var n;t.add((n=e,function(e){return function(t){var r=t.request;if(!o.a.isInstance(r))return e(t);var s=r.headers,a="node"===n.runtime?"user-agent":"x-amz-user-agent";return s[a]?s[a]+=" "+n.defaultUserAgent:s[a]=""+n.defaultUserAgent,n.customUserAgent&&(s[a]+=" "+n.customUserAgent),e(Object(i.__assign)(Object(i.__assign)({},t),{request:r}))}}),s)}}}},function(e,t,n){"use strict";n.d(t,"a",(function(){return r}));var r=function(e){return function(){throw new Error(e)}}},function(e,t,n){"use strict";n.d(t,"b",(function(){return o})),n.d(t,"a",(function(){return l}));var r=n(1),i=n(111);function o(e){var t,n=this,o=s(e.credentials||e.credentialDefaultProvider(e)),a=e.signingEscapePath,u=void 0===a||a,c=e.systemClockOffset,f=void 0===c?e.systemClockOffset||0:c,l=e.sha256;return t=e.signer?s(e.signer):function(){return s(e.region)().then((function(t){return Object(r.__awaiter)(n,void 0,void 0,(function(){return Object(r.__generator)(this,(function(n){switch(n.label){case 0:return[4,e.regionInfoProvider(t)];case 1:return[2,[n.sent()||{},t]]}}))}))})).then((function(t){var n=Object(r.__read)(t,2),s=n[0],a=n[1],c=s.signingRegion,f=void 0===c?e.signingRegion:c,d=s.signingService,h=void 0===d?e.signingName:d;return e.signingRegion=e.signingRegion||f||a,e.signingName=e.signingName||h,new i.a({credentials:o,region:e.signingRegion,service:e.signingName,sha256:l,uriEscapePath:u})}))},Object(r.__assign)(Object(r.__assign)({},e),{systemClockOffset:f,signingEscapePath:u,credentials:o,signer:t})}function s(e){if("object"==typeof e){var t=Promise.resolve(e);return function(){return t}}return e}var a=n(2),u=function(e){return new Date(Date.now()+e)};function c(e){return function(t,n){return function(i){return Object(r.__awaiter)(this,void 0,void 0,(function(){var o,s,c,f,l,d,h,p,v;return Object(r.__generator)(this,(function(g){switch(g.label){case 0:return a.a.isInstance(i.request)?"function"!=typeof e.signer?[3,2]:[4,e.signer()]:[2,t(i)];case 1:return s=g.sent(),[3,3];case 2:s=e.signer,g.label=3;case 3:return o=s,f=t,l=[Object(r.__assign)({},i)],v={},[4,o.sign(i.request,{signingDate:new Date(Date.now()+e.systemClockOffset),signingRegion:n.signing_region,signingService:n.signing_service})];case 4:return[4,f.apply(void 0,[r.__assign.apply(void 0,l.concat([(v.request=g.sent(),v)]))])];case 5:return c=g.sent(),d=c.response.headers,(h=d&&(d.date||d.Date))&&(p=Date.parse(h),m=p,b=e.systemClockOffset,Math.abs(u(b).getTime()-m)>=3e5&&(e.systemClockOffset=p-Date.now())),[2,c]}var m,b}))}))}}}var f={name:"awsAuthMiddleware",tags:["SIGNATURE","AWSAUTH"],relation:"after",toMiddleware:"retryMiddleware"},l=function(e){return{applyToStack:function(t){t.addRelativeTo(c(e),f)}}}},function(e,t,n){"use strict";var r=n(19),i={keyPrefix:"aws-amplify-cache",capacityInBytes:1048576,itemMaxSize:21e4,defaultTTL:2592e5,defaultPriority:5,warningThreshold:.8,storage:(new(n(86).a)).getStorage()};function o(e){var t=0;t=e.length;for(var n=e.length;n>=0;n-=1){var r=e.charCodeAt(n);r>127&&r<=2047?t+=1:r>2047&&r<=65535&&(t+=2),r>=56320&&r<=57343&&(n-=1)}return t}function s(){return(new Date).getTime()}function a(e){return Number.isInteger?Number.isInteger(e):function(e){return"number"==typeof e&&isFinite(e)&&Math.floor(e)===e}(e)}var u={},c=(function(){function e(){}e.clear=function(){u={}},e.getItem=function(e){return u[e]||null},e.setItem=function(e,t){u[e]=t},e.removeItem=function(e){delete u[e]}}(),n(44));function f(e){return(f="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)}var l,d=new c.a("StorageCache"),h=function(){function e(e){this.config=Object.assign({},e),this.cacheCurSizeKey=this.config.keyPrefix+"CurSize",this.checkConfig()}return e.prototype.getModuleName=function(){return"Cache"},e.prototype.checkConfig=function(){a(this.config.capacityInBytes)||(d.error("Invalid parameter: capacityInBytes. It should be an Integer. Setting back to default."),this.config.capacityInBytes=i.capacityInBytes),a(this.config.itemMaxSize)||(d.error("Invalid parameter: itemMaxSize. It should be an Integer. Setting back to default."),this.config.itemMaxSize=i.itemMaxSize),a(this.config.defaultTTL)||(d.error("Invalid parameter: defaultTTL. It should be an Integer. Setting back to default."),this.config.defaultTTL=i.defaultTTL),a(this.config.defaultPriority)||(d.error("Invalid parameter: defaultPriority. It should be an Integer. Setting back to default."),this.config.defaultPriority=i.defaultPriority),this.config.itemMaxSize>this.config.capacityInBytes&&(d.error("Invalid parameter: itemMaxSize. It should be smaller than capacityInBytes. Setting back to default."),this.config.itemMaxSize=i.itemMaxSize),(this.config.defaultPriority>5||this.config.defaultPriority<1)&&(d.error("Invalid parameter: defaultPriority. It should be between 1 and 5. Setting back to default."),this.config.defaultPriority=i.defaultPriority),(Number(this.config.warningThreshold)>1||Number(this.config.warningThreshold)<0)&&(d.error("Invalid parameter: warningThreshold. It should be between 0 and 1. Setting back to default."),this.config.warningThreshold=i.warningThreshold);this.config.capacityInBytes>5242880&&(d.error("Cache Capacity should be less than 5MB. Setting back to default. Setting back to default."),this.config.capacityInBytes=i.capacityInBytes)},e.prototype.fillCacheItem=function(e,t,n){var r={key:e,data:t,timestamp:s(),visitedTime:s(),priority:n.priority,expires:n.expires,type:f(t),byteSize:0};return r.byteSize=o(JSON.stringify(r)),r.byteSize=o(JSON.stringify(r)),r},e.prototype.configure=function(e){return e?(e.keyPrefix&&d.warn("Don't try to configure keyPrefix!"),this.config=Object.assign({},this.config,e,e.Cache),this.checkConfig(),this.config):this.config},e}(),p=(l=function(e,t){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}l(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),v=new c.a("Cache"),g=new(function(e){function t(t){var n=this,r=t?Object.assign({},i,t):i;return(n=e.call(this,r)||this).config.storage=r.storage,n.getItem=n.getItem.bind(n),n.setItem=n.setItem.bind(n),n.removeItem=n.removeItem.bind(n),n}return p(t,e),t.prototype._decreaseCurSizeInBytes=function(e){var t=this.getCacheCurSize();this.config.storage.setItem(this.cacheCurSizeKey,(t-e).toString())},t.prototype._increaseCurSizeInBytes=function(e){var t=this.getCacheCurSize();this.config.storage.setItem(this.cacheCurSizeKey,(t+e).toString())},t.prototype._refreshItem=function(e,t){return e.visitedTime=s(),this.config.storage.setItem(t,JSON.stringify(e)),e},t.prototype._isExpired=function(e){var t=this.config.storage.getItem(e),n=JSON.parse(t);return s()>=n.expires},t.prototype._removeItem=function(e,t){var n=t||JSON.parse(this.config.storage.getItem(e)).byteSize;this._decreaseCurSizeInBytes(n),this.config.storage.removeItem(e)},t.prototype._setItem=function(e,t){this._increaseCurSizeInBytes(t.byteSize);try{this.config.storage.setItem(e,JSON.stringify(t))}catch(e){this._decreaseCurSizeInBytes(t.byteSize),v.error("Failed to set item "+e)}},t.prototype._sizeToPop=function(e){var t=this.getCacheCurSize()+e-this.config.capacityInBytes,n=(1-this.config.warningThreshold)*this.config.capacityInBytes;return t>n?t:n},t.prototype._isCacheFull=function(e){return e+this.getCacheCurSize()>this.config.capacityInBytes},t.prototype._findValidKeys=function(){for(var e=[],t=[],n=0;n<this.config.storage.length;n+=1)t.push(this.config.storage.key(n));for(n=0;n<t.length;n+=1){var r=t[n];0===r.indexOf(this.config.keyPrefix)&&r!==this.cacheCurSizeKey&&(this._isExpired(r)?this._removeItem(r):e.push(r))}return e},t.prototype._popOutItems=function(e,t){for(var n=[],r=t,i=0;i<e.length;i+=1){var o=this.config.storage.getItem(e[i]);if(null!=o){var s=JSON.parse(o);n.push(s)}}n.sort((function(e,t){return e.priority>t.priority?-1:e.priority<t.priority?1:e.visitedTime<t.visitedTime?-1:1}));for(i=0;i<n.length;i+=1)if(this._removeItem(n[i].key,n[i].byteSize),(r-=n[i].byteSize)<=0)return},t.prototype.setItem=function(e,t,n){v.log("Set item: key is "+e+", value is "+t+" with options: "+n);var r=this.config.keyPrefix+e;if(r!==this.config.keyPrefix&&r!==this.cacheCurSizeKey)if(void 0!==t){var i={priority:n&&void 0!==n.priority?n.priority:this.config.defaultPriority,expires:n&&void 0!==n.expires?n.expires:this.config.defaultTTL+s()};if(i.priority<1||i.priority>5)v.warn("Invalid parameter: priority due to out or range. It should be within 1 and 5.");else{var o=this.fillCacheItem(r,t,i);if(o.byteSize>this.config.itemMaxSize)v.warn("Item with key: "+e+" you are trying to put into is too big!");else try{var a=this.config.storage.getItem(r);if(a&&this._removeItem(r,JSON.parse(a).byteSize),this._isCacheFull(o.byteSize)){var u=this._findValidKeys();if(this._isCacheFull(o.byteSize)){var c=this._sizeToPop(o.byteSize);this._popOutItems(u,c)}}this._setItem(r,o)}catch(e){v.warn("setItem failed! "+e)}}}else v.warn("The value of item should not be undefined!");else v.warn("Invalid key: should not be empty or 'CurSize'")},t.prototype.getItem=function(e,t){v.log("Get item: key is "+e+" with options "+t);var n=null,r=this.config.keyPrefix+e;if(r===this.config.keyPrefix||r===this.cacheCurSizeKey)return v.warn("Invalid key: should not be empty or 'CurSize'"),null;try{if(null!=(n=this.config.storage.getItem(r))){if(!this._isExpired(r)){var i=JSON.parse(n);return(i=this._refreshItem(i,r)).data}this._removeItem(r,JSON.parse(n).byteSize),n=null}if(t&&void 0!==t.callback){var o=t.callback();return null!==o&&this.setItem(e,o,t),o}return null}catch(e){return v.warn("getItem failed! "+e),null}},t.prototype.removeItem=function(e){v.log("Remove item: key is "+e);var t=this.config.keyPrefix+e;if(t!==this.config.keyPrefix&&t!==this.cacheCurSizeKey)try{var n=this.config.storage.getItem(t);n&&this._removeItem(t,JSON.parse(n).byteSize)}catch(e){v.warn("removeItem failed! "+e)}},t.prototype.clear=function(){v.log("Clear Cache");for(var e=[],t=0;t<this.config.storage.length;t+=1){var n=this.config.storage.key(t);0===n.indexOf(this.config.keyPrefix)&&e.push(n)}try{for(t=0;t<e.length;t+=1)this.config.storage.removeItem(e[t])}catch(e){v.warn("clear failed! "+e)}},t.prototype.getAllKeys=function(){for(var e=[],t=0;t<this.config.storage.length;t+=1){var n=this.config.storage.key(t);0===n.indexOf(this.config.keyPrefix)&&n!==this.cacheCurSizeKey&&e.push(n.substring(this.config.keyPrefix.length))}return e},t.prototype.getCacheCurSize=function(){var e=this.config.storage.getItem(this.cacheCurSizeKey);return e||(this.config.storage.setItem(this.cacheCurSizeKey,"0"),e="0"),Number(e)},t.prototype.createInstance=function(e){return e.keyPrefix&&e.keyPrefix!==i.keyPrefix||(v.error("invalid keyPrefix, setting keyPrefix with timeStamp"),e.keyPrefix=s.toString()),new t(e)},t}(h));t.a=g;r.a.register(g)},function(e,t,n){var r=n(371),i=n(372),o=i;o.v1=r,o.v4=i,e.exports=o},function(e,t,n){"use strict";n.d(t,"a",(function(){return a})),n.d(t,"b",(function(){return u}));for(var r={},i={},o=0;o<256;o++){var s=o.toString(16).toLowerCase();1===s.length&&(s="0"+s),r[o]=s,i[s]=o}function a(e){if(e.length%2!=0)throw new Error("Hex encoded strings must have an even number length");for(var t=new Uint8Array(e.length/2),n=0;n<e.length;n+=2){var r=e.substr(n,2).toLowerCase();if(!(r in i))throw new Error("Cannot decode unrecognized sequence "+r+" as hexadecimal");t[n/2]=i[r]}return t}function u(e){for(var t="",n=0;n<e.byteLength;n++)t+=r[e[n]];return t}},function(e,t,n){(function(e){!function(e,t){"use strict";function r(e,t){if(!e)throw new Error(t||"Assertion failed")}function i(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}function o(e,t,n){if(o.isBN(e))return e;this.negative=0,this.words=null,this.length=0,this.red=null,null!==e&&("le"!==t&&"be"!==t||(n=t,t=10),this._init(e||0,t||10,n||"be"))}var s;"object"==typeof e?e.exports=o:t.BN=o,o.BN=o,o.wordSize=26;try{s=n(315).Buffer}catch(e){}function a(e,t,n){for(var r=0,i=Math.min(e.length,n),o=t;o<i;o++){var s=e.charCodeAt(o)-48;r<<=4,r|=s>=49&&s<=54?s-49+10:s>=17&&s<=22?s-17+10:15&s}return r}function u(e,t,n,r){for(var i=0,o=Math.min(e.length,n),s=t;s<o;s++){var a=e.charCodeAt(s)-48;i*=r,i+=a>=49?a-49+10:a>=17?a-17+10:a}return i}o.isBN=function(e){return e instanceof o||null!==e&&"object"==typeof e&&e.constructor.wordSize===o.wordSize&&Array.isArray(e.words)},o.max=function(e,t){return e.cmp(t)>0?e:t},o.min=function(e,t){return e.cmp(t)<0?e:t},o.prototype._init=function(e,t,n){if("number"==typeof e)return this._initNumber(e,t,n);if("object"==typeof e)return this._initArray(e,t,n);"hex"===t&&(t=16),r(t===(0|t)&&t>=2&&t<=36);var i=0;"-"===(e=e.toString().replace(/\s+/g,""))[0]&&i++,16===t?this._parseHex(e,i):this._parseBase(e,t,i),"-"===e[0]&&(this.negative=1),this.strip(),"le"===n&&this._initArray(this.toArray(),t,n)},o.prototype._initNumber=function(e,t,n){e<0&&(this.negative=1,e=-e),e<67108864?(this.words=[67108863&e],this.length=1):e<4503599627370496?(this.words=[67108863&e,e/67108864&67108863],this.length=2):(r(e<9007199254740992),this.words=[67108863&e,e/67108864&67108863,1],this.length=3),"le"===n&&this._initArray(this.toArray(),t,n)},o.prototype._initArray=function(e,t,n){if(r("number"==typeof e.length),e.length<=0)return this.words=[0],this.length=1,this;this.length=Math.ceil(e.length/3),this.words=new Array(this.length);for(var i=0;i<this.length;i++)this.words[i]=0;var o,s,a=0;if("be"===n)for(i=e.length-1,o=0;i>=0;i-=3)s=e[i]|e[i-1]<<8|e[i-2]<<16,this.words[o]|=s<<a&67108863,this.words[o+1]=s>>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===n)for(i=0,o=0;i<e.length;i+=3)s=e[i]|e[i+1]<<8|e[i+2]<<16,this.words[o]|=s<<a&67108863,this.words[o+1]=s>>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this.strip()},o.prototype._parseHex=function(e,t){this.length=Math.ceil((e.length-t)/6),this.words=new Array(this.length);for(var n=0;n<this.length;n++)this.words[n]=0;var r,i,o=0;for(n=e.length-6,r=0;n>=t;n-=6)i=a(e,n,n+6),this.words[r]|=i<<o&67108863,this.words[r+1]|=i>>>26-o&4194303,(o+=24)>=26&&(o-=26,r++);n+6!==t&&(i=a(e,t,n+6),this.words[r]|=i<<o&67108863,this.words[r+1]|=i>>>26-o&4194303),this.strip()},o.prototype._parseBase=function(e,t,n){this.words=[0],this.length=1;for(var r=0,i=1;i<=67108863;i*=t)r++;r--,i=i/t|0;for(var o=e.length-n,s=o%r,a=Math.min(o,o-s)+n,c=0,f=n;f<a;f+=r)c=u(e,f,f+r,t),this.imuln(i),this.words[0]+c<67108864?this.words[0]+=c:this._iaddn(c);if(0!==s){var l=1;for(c=u(e,f,e.length,t),f=0;f<s;f++)l*=t;this.imuln(l),this.words[0]+c<67108864?this.words[0]+=c:this._iaddn(c)}},o.prototype.copy=function(e){e.words=new Array(this.length);for(var t=0;t<this.length;t++)e.words[t]=this.words[t];e.length=this.length,e.negative=this.negative,e.red=this.red},o.prototype.clone=function(){var e=new o(null);return this.copy(e),e},o.prototype._expand=function(e){for(;this.length<e;)this.words[this.length++]=0;return this},o.prototype.strip=function(){for(;this.length>1&&0===this.words[this.length-1];)this.length--;return this._normSign()},o.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},o.prototype.inspect=function(){return(this.red?"<BN-R: ":"<BN: ")+this.toString(16)+">"};var c=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],f=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],l=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function d(e,t,n){n.negative=t.negative^e.negative;var r=e.length+t.length|0;n.length=r,r=r-1|0;var i=0|e.words[0],o=0|t.words[0],s=i*o,a=67108863&s,u=s/67108864|0;n.words[0]=a;for(var c=1;c<r;c++){for(var f=u>>>26,l=67108863&u,d=Math.min(c,t.length-1),h=Math.max(0,c-e.length+1);h<=d;h++){var p=c-h|0;f+=(s=(i=0|e.words[p])*(o=0|t.words[h])+l)/67108864|0,l=67108863&s}n.words[c]=0|l,u=0|f}return 0!==u?n.words[c]=0|u:n.length--,n.strip()}o.prototype.toString=function(e,t){var n;if(t=0|t||1,16===(e=e||10)||"hex"===e){n="";for(var i=0,o=0,s=0;s<this.length;s++){var a=this.words[s],u=(16777215&(a<<i|o)).toString(16);n=0!==(o=a>>>24-i&16777215)||s!==this.length-1?c[6-u.length]+u+n:u+n,(i+=2)>=26&&(i-=26,s--)}for(0!==o&&(n=o.toString(16)+n);n.length%t!=0;)n="0"+n;return 0!==this.negative&&(n="-"+n),n}if(e===(0|e)&&e>=2&&e<=36){var d=f[e],h=l[e];n="";var p=this.clone();for(p.negative=0;!p.isZero();){var v=p.modn(h).toString(e);n=(p=p.idivn(h)).isZero()?v+n:c[d-v.length]+v+n}for(this.isZero()&&(n="0"+n);n.length%t!=0;)n="0"+n;return 0!==this.negative&&(n="-"+n),n}r(!1,"Base should be between 2 and 36")},o.prototype.toNumber=function(){var e=this.words[0];return 2===this.length?e+=67108864*this.words[1]:3===this.length&&1===this.words[2]?e+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-e:e},o.prototype.toJSON=function(){return this.toString(16)},o.prototype.toBuffer=function(e,t){return r(void 0!==s),this.toArrayLike(s,e,t)},o.prototype.toArray=function(e,t){return this.toArrayLike(Array,e,t)},o.prototype.toArrayLike=function(e,t,n){var i=this.byteLength(),o=n||Math.max(1,i);r(i<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0"),this.strip();var s,a,u="le"===t,c=new e(o),f=this.clone();if(u){for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),c[a]=s;for(;a<o;a++)c[a]=0}else{for(a=0;a<o-i;a++)c[a]=0;for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),c[o-a-1]=s}return c},Math.clz32?o.prototype._countBits=function(e){return 32-Math.clz32(e)}:o.prototype._countBits=function(e){var t=e,n=0;return t>=4096&&(n+=13,t>>>=13),t>=64&&(n+=7,t>>>=7),t>=8&&(n+=4,t>>>=4),t>=2&&(n+=2,t>>>=2),n+t},o.prototype._zeroBits=function(e){if(0===e)return 26;var t=e,n=0;return 0==(8191&t)&&(n+=13,t>>>=13),0==(127&t)&&(n+=7,t>>>=7),0==(15&t)&&(n+=4,t>>>=4),0==(3&t)&&(n+=2,t>>>=2),0==(1&t)&&n++,n},o.prototype.bitLength=function(){var e=this.words[this.length-1],t=this._countBits(e);return 26*(this.length-1)+t},o.prototype.zeroBits=function(){if(this.isZero())return 0;for(var e=0,t=0;t<this.length;t++){var n=this._zeroBits(this.words[t]);if(e+=n,26!==n)break}return e},o.prototype.byteLength=function(){return Math.ceil(this.bitLength()/8)},o.prototype.toTwos=function(e){return 0!==this.negative?this.abs().inotn(e).iaddn(1):this.clone()},o.prototype.fromTwos=function(e){return this.testn(e-1)?this.notn(e).iaddn(1).ineg():this.clone()},o.prototype.isNeg=function(){return 0!==this.negative},o.prototype.neg=function(){return this.clone().ineg()},o.prototype.ineg=function(){return this.isZero()||(this.negative^=1),this},o.prototype.iuor=function(e){for(;this.length<e.length;)this.words[this.length++]=0;for(var t=0;t<e.length;t++)this.words[t]=this.words[t]|e.words[t];return this.strip()},o.prototype.ior=function(e){return r(0==(this.negative|e.negative)),this.iuor(e)},o.prototype.or=function(e){return this.length>e.length?this.clone().ior(e):e.clone().ior(this)},o.prototype.uor=function(e){return this.length>e.length?this.clone().iuor(e):e.clone().iuor(this)},o.prototype.iuand=function(e){var t;t=this.length>e.length?e:this;for(var n=0;n<t.length;n++)this.words[n]=this.words[n]&e.words[n];return this.length=t.length,this.strip()},o.prototype.iand=function(e){return r(0==(this.negative|e.negative)),this.iuand(e)},o.prototype.and=function(e){return this.length>e.length?this.clone().iand(e):e.clone().iand(this)},o.prototype.uand=function(e){return this.length>e.length?this.clone().iuand(e):e.clone().iuand(this)},o.prototype.iuxor=function(e){var t,n;this.length>e.length?(t=this,n=e):(t=e,n=this);for(var r=0;r<n.length;r++)this.words[r]=t.words[r]^n.words[r];if(this!==t)for(;r<t.length;r++)this.words[r]=t.words[r];return this.length=t.length,this.strip()},o.prototype.ixor=function(e){return r(0==(this.negative|e.negative)),this.iuxor(e)},o.prototype.xor=function(e){return this.length>e.length?this.clone().ixor(e):e.clone().ixor(this)},o.prototype.uxor=function(e){return this.length>e.length?this.clone().iuxor(e):e.clone().iuxor(this)},o.prototype.inotn=function(e){r("number"==typeof e&&e>=0);var t=0|Math.ceil(e/26),n=e%26;this._expand(t),n>0&&t--;for(var i=0;i<t;i++)this.words[i]=67108863&~this.words[i];return n>0&&(this.words[i]=~this.words[i]&67108863>>26-n),this.strip()},o.prototype.notn=function(e){return this.clone().inotn(e)},o.prototype.setn=function(e,t){r("number"==typeof e&&e>=0);var n=e/26|0,i=e%26;return this._expand(n+1),this.words[n]=t?this.words[n]|1<<i:this.words[n]&~(1<<i),this.strip()},o.prototype.iadd=function(e){var t,n,r;if(0!==this.negative&&0===e.negative)return this.negative=0,t=this.isub(e),this.negative^=1,this._normSign();if(0===this.negative&&0!==e.negative)return e.negative=0,t=this.isub(e),e.negative=1,t._normSign();this.length>e.length?(n=this,r=e):(n=e,r=this);for(var i=0,o=0;o<r.length;o++)t=(0|n.words[o])+(0|r.words[o])+i,this.words[o]=67108863&t,i=t>>>26;for(;0!==i&&o<n.length;o++)t=(0|n.words[o])+i,this.words[o]=67108863&t,i=t>>>26;if(this.length=n.length,0!==i)this.words[this.length]=i,this.length++;else if(n!==this)for(;o<n.length;o++)this.words[o]=n.words[o];return this},o.prototype.add=function(e){var t;return 0!==e.negative&&0===this.negative?(e.negative=0,t=this.sub(e),e.negative^=1,t):0===e.negative&&0!==this.negative?(this.negative=0,t=e.sub(this),this.negative=1,t):this.length>e.length?this.clone().iadd(e):e.clone().iadd(this)},o.prototype.isub=function(e){if(0!==e.negative){e.negative=0;var t=this.iadd(e);return e.negative=1,t._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(e),this.negative=1,this._normSign();var n,r,i=this.cmp(e);if(0===i)return this.negative=0,this.length=1,this.words[0]=0,this;i>0?(n=this,r=e):(n=e,r=this);for(var o=0,s=0;s<r.length;s++)o=(t=(0|n.words[s])-(0|r.words[s])+o)>>26,this.words[s]=67108863&t;for(;0!==o&&s<n.length;s++)o=(t=(0|n.words[s])+o)>>26,this.words[s]=67108863&t;if(0===o&&s<n.length&&n!==this)for(;s<n.length;s++)this.words[s]=n.words[s];return this.length=Math.max(this.length,s),n!==this&&(this.negative=1),this.strip()},o.prototype.sub=function(e){return this.clone().isub(e)};var h=function(e,t,n){var r,i,o,s=e.words,a=t.words,u=n.words,c=0,f=0|s[0],l=8191&f,d=f>>>13,h=0|s[1],p=8191&h,v=h>>>13,g=0|s[2],m=8191&g,b=g>>>13,y=0|s[3],w=8191&y,_=y>>>13,S=0|s[4],E=8191&S,M=S>>>13,A=0|s[5],I=8191&A,k=A>>>13,O=0|s[6],x=8191&O,C=O>>>13,T=0|s[7],P=8191&T,N=T>>>13,R=0|s[8],L=8191&R,j=R>>>13,D=0|s[9],U=8191&D,B=D>>>13,F=0|a[0],z=8191&F,q=F>>>13,K=0|a[1],H=8191&K,V=K>>>13,G=0|a[2],W=8191&G,$=G>>>13,Y=0|a[3],J=8191&Y,Z=Y>>>13,X=0|a[4],Q=8191&X,ee=X>>>13,te=0|a[5],ne=8191&te,re=te>>>13,ie=0|a[6],oe=8191&ie,se=ie>>>13,ae=0|a[7],ue=8191&ae,ce=ae>>>13,fe=0|a[8],le=8191&fe,de=fe>>>13,he=0|a[9],pe=8191&he,ve=he>>>13;n.negative=e.negative^t.negative,n.length=19;var ge=(c+(r=Math.imul(l,z))|0)+((8191&(i=(i=Math.imul(l,q))+Math.imul(d,z)|0))<<13)|0;c=((o=Math.imul(d,q))+(i>>>13)|0)+(ge>>>26)|0,ge&=67108863,r=Math.imul(p,z),i=(i=Math.imul(p,q))+Math.imul(v,z)|0,o=Math.imul(v,q);var me=(c+(r=r+Math.imul(l,H)|0)|0)+((8191&(i=(i=i+Math.imul(l,V)|0)+Math.imul(d,H)|0))<<13)|0;c=((o=o+Math.imul(d,V)|0)+(i>>>13)|0)+(me>>>26)|0,me&=67108863,r=Math.imul(m,z),i=(i=Math.imul(m,q))+Math.imul(b,z)|0,o=Math.imul(b,q),r=r+Math.imul(p,H)|0,i=(i=i+Math.imul(p,V)|0)+Math.imul(v,H)|0,o=o+Math.imul(v,V)|0;var be=(c+(r=r+Math.imul(l,W)|0)|0)+((8191&(i=(i=i+Math.imul(l,$)|0)+Math.imul(d,W)|0))<<13)|0;c=((o=o+Math.imul(d,$)|0)+(i>>>13)|0)+(be>>>26)|0,be&=67108863,r=Math.imul(w,z),i=(i=Math.imul(w,q))+Math.imul(_,z)|0,o=Math.imul(_,q),r=r+Math.imul(m,H)|0,i=(i=i+Math.imul(m,V)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,V)|0,r=r+Math.imul(p,W)|0,i=(i=i+Math.imul(p,$)|0)+Math.imul(v,W)|0,o=o+Math.imul(v,$)|0;var ye=(c+(r=r+Math.imul(l,J)|0)|0)+((8191&(i=(i=i+Math.imul(l,Z)|0)+Math.imul(d,J)|0))<<13)|0;c=((o=o+Math.imul(d,Z)|0)+(i>>>13)|0)+(ye>>>26)|0,ye&=67108863,r=Math.imul(E,z),i=(i=Math.imul(E,q))+Math.imul(M,z)|0,o=Math.imul(M,q),r=r+Math.imul(w,H)|0,i=(i=i+Math.imul(w,V)|0)+Math.imul(_,H)|0,o=o+Math.imul(_,V)|0,r=r+Math.imul(m,W)|0,i=(i=i+Math.imul(m,$)|0)+Math.imul(b,W)|0,o=o+Math.imul(b,$)|0,r=r+Math.imul(p,J)|0,i=(i=i+Math.imul(p,Z)|0)+Math.imul(v,J)|0,o=o+Math.imul(v,Z)|0;var we=(c+(r=r+Math.imul(l,Q)|0)|0)+((8191&(i=(i=i+Math.imul(l,ee)|0)+Math.imul(d,Q)|0))<<13)|0;c=((o=o+Math.imul(d,ee)|0)+(i>>>13)|0)+(we>>>26)|0,we&=67108863,r=Math.imul(I,z),i=(i=Math.imul(I,q))+Math.imul(k,z)|0,o=Math.imul(k,q),r=r+Math.imul(E,H)|0,i=(i=i+Math.imul(E,V)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,V)|0,r=r+Math.imul(w,W)|0,i=(i=i+Math.imul(w,$)|0)+Math.imul(_,W)|0,o=o+Math.imul(_,$)|0,r=r+Math.imul(m,J)|0,i=(i=i+Math.imul(m,Z)|0)+Math.imul(b,J)|0,o=o+Math.imul(b,Z)|0,r=r+Math.imul(p,Q)|0,i=(i=i+Math.imul(p,ee)|0)+Math.imul(v,Q)|0,o=o+Math.imul(v,ee)|0;var _e=(c+(r=r+Math.imul(l,ne)|0)|0)+((8191&(i=(i=i+Math.imul(l,re)|0)+Math.imul(d,ne)|0))<<13)|0;c=((o=o+Math.imul(d,re)|0)+(i>>>13)|0)+(_e>>>26)|0,_e&=67108863,r=Math.imul(x,z),i=(i=Math.imul(x,q))+Math.imul(C,z)|0,o=Math.imul(C,q),r=r+Math.imul(I,H)|0,i=(i=i+Math.imul(I,V)|0)+Math.imul(k,H)|0,o=o+Math.imul(k,V)|0,r=r+Math.imul(E,W)|0,i=(i=i+Math.imul(E,$)|0)+Math.imul(M,W)|0,o=o+Math.imul(M,$)|0,r=r+Math.imul(w,J)|0,i=(i=i+Math.imul(w,Z)|0)+Math.imul(_,J)|0,o=o+Math.imul(_,Z)|0,r=r+Math.imul(m,Q)|0,i=(i=i+Math.imul(m,ee)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,ee)|0,r=r+Math.imul(p,ne)|0,i=(i=i+Math.imul(p,re)|0)+Math.imul(v,ne)|0,o=o+Math.imul(v,re)|0;var Se=(c+(r=r+Math.imul(l,oe)|0)|0)+((8191&(i=(i=i+Math.imul(l,se)|0)+Math.imul(d,oe)|0))<<13)|0;c=((o=o+Math.imul(d,se)|0)+(i>>>13)|0)+(Se>>>26)|0,Se&=67108863,r=Math.imul(P,z),i=(i=Math.imul(P,q))+Math.imul(N,z)|0,o=Math.imul(N,q),r=r+Math.imul(x,H)|0,i=(i=i+Math.imul(x,V)|0)+Math.imul(C,H)|0,o=o+Math.imul(C,V)|0,r=r+Math.imul(I,W)|0,i=(i=i+Math.imul(I,$)|0)+Math.imul(k,W)|0,o=o+Math.imul(k,$)|0,r=r+Math.imul(E,J)|0,i=(i=i+Math.imul(E,Z)|0)+Math.imul(M,J)|0,o=o+Math.imul(M,Z)|0,r=r+Math.imul(w,Q)|0,i=(i=i+Math.imul(w,ee)|0)+Math.imul(_,Q)|0,o=o+Math.imul(_,ee)|0,r=r+Math.imul(m,ne)|0,i=(i=i+Math.imul(m,re)|0)+Math.imul(b,ne)|0,o=o+Math.imul(b,re)|0,r=r+Math.imul(p,oe)|0,i=(i=i+Math.imul(p,se)|0)+Math.imul(v,oe)|0,o=o+Math.imul(v,se)|0;var Ee=(c+(r=r+Math.imul(l,ue)|0)|0)+((8191&(i=(i=i+Math.imul(l,ce)|0)+Math.imul(d,ue)|0))<<13)|0;c=((o=o+Math.imul(d,ce)|0)+(i>>>13)|0)+(Ee>>>26)|0,Ee&=67108863,r=Math.imul(L,z),i=(i=Math.imul(L,q))+Math.imul(j,z)|0,o=Math.imul(j,q),r=r+Math.imul(P,H)|0,i=(i=i+Math.imul(P,V)|0)+Math.imul(N,H)|0,o=o+Math.imul(N,V)|0,r=r+Math.imul(x,W)|0,i=(i=i+Math.imul(x,$)|0)+Math.imul(C,W)|0,o=o+Math.imul(C,$)|0,r=r+Math.imul(I,J)|0,i=(i=i+Math.imul(I,Z)|0)+Math.imul(k,J)|0,o=o+Math.imul(k,Z)|0,r=r+Math.imul(E,Q)|0,i=(i=i+Math.imul(E,ee)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,ee)|0,r=r+Math.imul(w,ne)|0,i=(i=i+Math.imul(w,re)|0)+Math.imul(_,ne)|0,o=o+Math.imul(_,re)|0,r=r+Math.imul(m,oe)|0,i=(i=i+Math.imul(m,se)|0)+Math.imul(b,oe)|0,o=o+Math.imul(b,se)|0,r=r+Math.imul(p,ue)|0,i=(i=i+Math.imul(p,ce)|0)+Math.imul(v,ue)|0,o=o+Math.imul(v,ce)|0;var Me=(c+(r=r+Math.imul(l,le)|0)|0)+((8191&(i=(i=i+Math.imul(l,de)|0)+Math.imul(d,le)|0))<<13)|0;c=((o=o+Math.imul(d,de)|0)+(i>>>13)|0)+(Me>>>26)|0,Me&=67108863,r=Math.imul(U,z),i=(i=Math.imul(U,q))+Math.imul(B,z)|0,o=Math.imul(B,q),r=r+Math.imul(L,H)|0,i=(i=i+Math.imul(L,V)|0)+Math.imul(j,H)|0,o=o+Math.imul(j,V)|0,r=r+Math.imul(P,W)|0,i=(i=i+Math.imul(P,$)|0)+Math.imul(N,W)|0,o=o+Math.imul(N,$)|0,r=r+Math.imul(x,J)|0,i=(i=i+Math.imul(x,Z)|0)+Math.imul(C,J)|0,o=o+Math.imul(C,Z)|0,r=r+Math.imul(I,Q)|0,i=(i=i+Math.imul(I,ee)|0)+Math.imul(k,Q)|0,o=o+Math.imul(k,ee)|0,r=r+Math.imul(E,ne)|0,i=(i=i+Math.imul(E,re)|0)+Math.imul(M,ne)|0,o=o+Math.imul(M,re)|0,r=r+Math.imul(w,oe)|0,i=(i=i+Math.imul(w,se)|0)+Math.imul(_,oe)|0,o=o+Math.imul(_,se)|0,r=r+Math.imul(m,ue)|0,i=(i=i+Math.imul(m,ce)|0)+Math.imul(b,ue)|0,o=o+Math.imul(b,ce)|0,r=r+Math.imul(p,le)|0,i=(i=i+Math.imul(p,de)|0)+Math.imul(v,le)|0,o=o+Math.imul(v,de)|0;var Ae=(c+(r=r+Math.imul(l,pe)|0)|0)+((8191&(i=(i=i+Math.imul(l,ve)|0)+Math.imul(d,pe)|0))<<13)|0;c=((o=o+Math.imul(d,ve)|0)+(i>>>13)|0)+(Ae>>>26)|0,Ae&=67108863,r=Math.imul(U,H),i=(i=Math.imul(U,V))+Math.imul(B,H)|0,o=Math.imul(B,V),r=r+Math.imul(L,W)|0,i=(i=i+Math.imul(L,$)|0)+Math.imul(j,W)|0,o=o+Math.imul(j,$)|0,r=r+Math.imul(P,J)|0,i=(i=i+Math.imul(P,Z)|0)+Math.imul(N,J)|0,o=o+Math.imul(N,Z)|0,r=r+Math.imul(x,Q)|0,i=(i=i+Math.imul(x,ee)|0)+Math.imul(C,Q)|0,o=o+Math.imul(C,ee)|0,r=r+Math.imul(I,ne)|0,i=(i=i+Math.imul(I,re)|0)+Math.imul(k,ne)|0,o=o+Math.imul(k,re)|0,r=r+Math.imul(E,oe)|0,i=(i=i+Math.imul(E,se)|0)+Math.imul(M,oe)|0,o=o+Math.imul(M,se)|0,r=r+Math.imul(w,ue)|0,i=(i=i+Math.imul(w,ce)|0)+Math.imul(_,ue)|0,o=o+Math.imul(_,ce)|0,r=r+Math.imul(m,le)|0,i=(i=i+Math.imul(m,de)|0)+Math.imul(b,le)|0,o=o+Math.imul(b,de)|0;var Ie=(c+(r=r+Math.imul(p,pe)|0)|0)+((8191&(i=(i=i+Math.imul(p,ve)|0)+Math.imul(v,pe)|0))<<13)|0;c=((o=o+Math.imul(v,ve)|0)+(i>>>13)|0)+(Ie>>>26)|0,Ie&=67108863,r=Math.imul(U,W),i=(i=Math.imul(U,$))+Math.imul(B,W)|0,o=Math.imul(B,$),r=r+Math.imul(L,J)|0,i=(i=i+Math.imul(L,Z)|0)+Math.imul(j,J)|0,o=o+Math.imul(j,Z)|0,r=r+Math.imul(P,Q)|0,i=(i=i+Math.imul(P,ee)|0)+Math.imul(N,Q)|0,o=o+Math.imul(N,ee)|0,r=r+Math.imul(x,ne)|0,i=(i=i+Math.imul(x,re)|0)+Math.imul(C,ne)|0,o=o+Math.imul(C,re)|0,r=r+Math.imul(I,oe)|0,i=(i=i+Math.imul(I,se)|0)+Math.imul(k,oe)|0,o=o+Math.imul(k,se)|0,r=r+Math.imul(E,ue)|0,i=(i=i+Math.imul(E,ce)|0)+Math.imul(M,ue)|0,o=o+Math.imul(M,ce)|0,r=r+Math.imul(w,le)|0,i=(i=i+Math.imul(w,de)|0)+Math.imul(_,le)|0,o=o+Math.imul(_,de)|0;var ke=(c+(r=r+Math.imul(m,pe)|0)|0)+((8191&(i=(i=i+Math.imul(m,ve)|0)+Math.imul(b,pe)|0))<<13)|0;c=((o=o+Math.imul(b,ve)|0)+(i>>>13)|0)+(ke>>>26)|0,ke&=67108863,r=Math.imul(U,J),i=(i=Math.imul(U,Z))+Math.imul(B,J)|0,o=Math.imul(B,Z),r=r+Math.imul(L,Q)|0,i=(i=i+Math.imul(L,ee)|0)+Math.imul(j,Q)|0,o=o+Math.imul(j,ee)|0,r=r+Math.imul(P,ne)|0,i=(i=i+Math.imul(P,re)|0)+Math.imul(N,ne)|0,o=o+Math.imul(N,re)|0,r=r+Math.imul(x,oe)|0,i=(i=i+Math.imul(x,se)|0)+Math.imul(C,oe)|0,o=o+Math.imul(C,se)|0,r=r+Math.imul(I,ue)|0,i=(i=i+Math.imul(I,ce)|0)+Math.imul(k,ue)|0,o=o+Math.imul(k,ce)|0,r=r+Math.imul(E,le)|0,i=(i=i+Math.imul(E,de)|0)+Math.imul(M,le)|0,o=o+Math.imul(M,de)|0;var Oe=(c+(r=r+Math.imul(w,pe)|0)|0)+((8191&(i=(i=i+Math.imul(w,ve)|0)+Math.imul(_,pe)|0))<<13)|0;c=((o=o+Math.imul(_,ve)|0)+(i>>>13)|0)+(Oe>>>26)|0,Oe&=67108863,r=Math.imul(U,Q),i=(i=Math.imul(U,ee))+Math.imul(B,Q)|0,o=Math.imul(B,ee),r=r+Math.imul(L,ne)|0,i=(i=i+Math.imul(L,re)|0)+Math.imul(j,ne)|0,o=o+Math.imul(j,re)|0,r=r+Math.imul(P,oe)|0,i=(i=i+Math.imul(P,se)|0)+Math.imul(N,oe)|0,o=o+Math.imul(N,se)|0,r=r+Math.imul(x,ue)|0,i=(i=i+Math.imul(x,ce)|0)+Math.imul(C,ue)|0,o=o+Math.imul(C,ce)|0,r=r+Math.imul(I,le)|0,i=(i=i+Math.imul(I,de)|0)+Math.imul(k,le)|0,o=o+Math.imul(k,de)|0;var xe=(c+(r=r+Math.imul(E,pe)|0)|0)+((8191&(i=(i=i+Math.imul(E,ve)|0)+Math.imul(M,pe)|0))<<13)|0;c=((o=o+Math.imul(M,ve)|0)+(i>>>13)|0)+(xe>>>26)|0,xe&=67108863,r=Math.imul(U,ne),i=(i=Math.imul(U,re))+Math.imul(B,ne)|0,o=Math.imul(B,re),r=r+Math.imul(L,oe)|0,i=(i=i+Math.imul(L,se)|0)+Math.imul(j,oe)|0,o=o+Math.imul(j,se)|0,r=r+Math.imul(P,ue)|0,i=(i=i+Math.imul(P,ce)|0)+Math.imul(N,ue)|0,o=o+Math.imul(N,ce)|0,r=r+Math.imul(x,le)|0,i=(i=i+Math.imul(x,de)|0)+Math.imul(C,le)|0,o=o+Math.imul(C,de)|0;var Ce=(c+(r=r+Math.imul(I,pe)|0)|0)+((8191&(i=(i=i+Math.imul(I,ve)|0)+Math.imul(k,pe)|0))<<13)|0;c=((o=o+Math.imul(k,ve)|0)+(i>>>13)|0)+(Ce>>>26)|0,Ce&=67108863,r=Math.imul(U,oe),i=(i=Math.imul(U,se))+Math.imul(B,oe)|0,o=Math.imul(B,se),r=r+Math.imul(L,ue)|0,i=(i=i+Math.imul(L,ce)|0)+Math.imul(j,ue)|0,o=o+Math.imul(j,ce)|0,r=r+Math.imul(P,le)|0,i=(i=i+Math.imul(P,de)|0)+Math.imul(N,le)|0,o=o+Math.imul(N,de)|0;var Te=(c+(r=r+Math.imul(x,pe)|0)|0)+((8191&(i=(i=i+Math.imul(x,ve)|0)+Math.imul(C,pe)|0))<<13)|0;c=((o=o+Math.imul(C,ve)|0)+(i>>>13)|0)+(Te>>>26)|0,Te&=67108863,r=Math.imul(U,ue),i=(i=Math.imul(U,ce))+Math.imul(B,ue)|0,o=Math.imul(B,ce),r=r+Math.imul(L,le)|0,i=(i=i+Math.imul(L,de)|0)+Math.imul(j,le)|0,o=o+Math.imul(j,de)|0;var Pe=(c+(r=r+Math.imul(P,pe)|0)|0)+((8191&(i=(i=i+Math.imul(P,ve)|0)+Math.imul(N,pe)|0))<<13)|0;c=((o=o+Math.imul(N,ve)|0)+(i>>>13)|0)+(Pe>>>26)|0,Pe&=67108863,r=Math.imul(U,le),i=(i=Math.imul(U,de))+Math.imul(B,le)|0,o=Math.imul(B,de);var Ne=(c+(r=r+Math.imul(L,pe)|0)|0)+((8191&(i=(i=i+Math.imul(L,ve)|0)+Math.imul(j,pe)|0))<<13)|0;c=((o=o+Math.imul(j,ve)|0)+(i>>>13)|0)+(Ne>>>26)|0,Ne&=67108863;var Re=(c+(r=Math.imul(U,pe))|0)+((8191&(i=(i=Math.imul(U,ve))+Math.imul(B,pe)|0))<<13)|0;return c=((o=Math.imul(B,ve))+(i>>>13)|0)+(Re>>>26)|0,Re&=67108863,u[0]=ge,u[1]=me,u[2]=be,u[3]=ye,u[4]=we,u[5]=_e,u[6]=Se,u[7]=Ee,u[8]=Me,u[9]=Ae,u[10]=Ie,u[11]=ke,u[12]=Oe,u[13]=xe,u[14]=Ce,u[15]=Te,u[16]=Pe,u[17]=Ne,u[18]=Re,0!==c&&(u[19]=c,n.length++),n};function p(e,t,n){return(new v).mulp(e,t,n)}function v(e,t){this.x=e,this.y=t}Math.imul||(h=d),o.prototype.mulTo=function(e,t){var n=this.length+e.length;return 10===this.length&&10===e.length?h(this,e,t):n<63?d(this,e,t):n<1024?function(e,t,n){n.negative=t.negative^e.negative,n.length=e.length+t.length;for(var r=0,i=0,o=0;o<n.length-1;o++){var s=i;i=0;for(var a=67108863&r,u=Math.min(o,t.length-1),c=Math.max(0,o-e.length+1);c<=u;c++){var f=o-c,l=(0|e.words[f])*(0|t.words[c]),d=67108863&l;a=67108863&(d=d+a|0),i+=(s=(s=s+(l/67108864|0)|0)+(d>>>26)|0)>>>26,s&=67108863}n.words[o]=a,r=s,s=i}return 0!==r?n.words[o]=r:n.length--,n.strip()}(this,e,t):p(this,e,t)},v.prototype.makeRBT=function(e){for(var t=new Array(e),n=o.prototype._countBits(e)-1,r=0;r<e;r++)t[r]=this.revBin(r,n,e);return t},v.prototype.revBin=function(e,t,n){if(0===e||e===n-1)return e;for(var r=0,i=0;i<t;i++)r|=(1&e)<<t-i-1,e>>=1;return r},v.prototype.permute=function(e,t,n,r,i,o){for(var s=0;s<o;s++)r[s]=t[e[s]],i[s]=n[e[s]]},v.prototype.transform=function(e,t,n,r,i,o){this.permute(o,e,t,n,r,i);for(var s=1;s<i;s<<=1)for(var a=s<<1,u=Math.cos(2*Math.PI/a),c=Math.sin(2*Math.PI/a),f=0;f<i;f+=a)for(var l=u,d=c,h=0;h<s;h++){var p=n[f+h],v=r[f+h],g=n[f+h+s],m=r[f+h+s],b=l*g-d*m;m=l*m+d*g,g=b,n[f+h]=p+g,r[f+h]=v+m,n[f+h+s]=p-g,r[f+h+s]=v-m,h!==a&&(b=u*l-c*d,d=u*d+c*l,l=b)}},v.prototype.guessLen13b=function(e,t){var n=1|Math.max(t,e),r=1&n,i=0;for(n=n/2|0;n;n>>>=1)i++;return 1<<i+1+r},v.prototype.conjugate=function(e,t,n){if(!(n<=1))for(var r=0;r<n/2;r++){var i=e[r];e[r]=e[n-r-1],e[n-r-1]=i,i=t[r],t[r]=-t[n-r-1],t[n-r-1]=-i}},v.prototype.normalize13b=function(e,t){for(var n=0,r=0;r<t/2;r++){var i=8192*Math.round(e[2*r+1]/t)+Math.round(e[2*r]/t)+n;e[r]=67108863&i,n=i<67108864?0:i/67108864|0}return e},v.prototype.convert13b=function(e,t,n,i){for(var o=0,s=0;s<t;s++)o+=0|e[s],n[2*s]=8191&o,o>>>=13,n[2*s+1]=8191&o,o>>>=13;for(s=2*t;s<i;++s)n[s]=0;r(0===o),r(0==(-8192&o))},v.prototype.stub=function(e){for(var t=new Array(e),n=0;n<e;n++)t[n]=0;return t},v.prototype.mulp=function(e,t,n){var r=2*this.guessLen13b(e.length,t.length),i=this.makeRBT(r),o=this.stub(r),s=new Array(r),a=new Array(r),u=new Array(r),c=new Array(r),f=new Array(r),l=new Array(r),d=n.words;d.length=r,this.convert13b(e.words,e.length,s,r),this.convert13b(t.words,t.length,c,r),this.transform(s,o,a,u,r,i),this.transform(c,o,f,l,r,i);for(var h=0;h<r;h++){var p=a[h]*f[h]-u[h]*l[h];u[h]=a[h]*l[h]+u[h]*f[h],a[h]=p}return this.conjugate(a,u,r),this.transform(a,u,d,o,r,i),this.conjugate(d,o,r),this.normalize13b(d,r),n.negative=e.negative^t.negative,n.length=e.length+t.length,n.strip()},o.prototype.mul=function(e){var t=new o(null);return t.words=new Array(this.length+e.length),this.mulTo(e,t)},o.prototype.mulf=function(e){var t=new o(null);return t.words=new Array(this.length+e.length),p(this,e,t)},o.prototype.imul=function(e){return this.clone().mulTo(e,this)},o.prototype.imuln=function(e){r("number"==typeof e),r(e<67108864);for(var t=0,n=0;n<this.length;n++){var i=(0|this.words[n])*e,o=(67108863&i)+(67108863&t);t>>=26,t+=i/67108864|0,t+=o>>>26,this.words[n]=67108863&o}return 0!==t&&(this.words[n]=t,this.length++),this},o.prototype.muln=function(e){return this.clone().imuln(e)},o.prototype.sqr=function(){return this.mul(this)},o.prototype.isqr=function(){return this.imul(this.clone())},o.prototype.pow=function(e){var t=function(e){for(var t=new Array(e.bitLength()),n=0;n<t.length;n++){var r=n/26|0,i=n%26;t[n]=(e.words[r]&1<<i)>>>i}return t}(e);if(0===t.length)return new o(1);for(var n=this,r=0;r<t.length&&0===t[r];r++,n=n.sqr());if(++r<t.length)for(var i=n.sqr();r<t.length;r++,i=i.sqr())0!==t[r]&&(n=n.mul(i));return n},o.prototype.iushln=function(e){r("number"==typeof e&&e>=0);var t,n=e%26,i=(e-n)/26,o=67108863>>>26-n<<26-n;if(0!==n){var s=0;for(t=0;t<this.length;t++){var a=this.words[t]&o,u=(0|this.words[t])-a<<n;this.words[t]=u|s,s=a>>>26-n}s&&(this.words[t]=s,this.length++)}if(0!==i){for(t=this.length-1;t>=0;t--)this.words[t+i]=this.words[t];for(t=0;t<i;t++)this.words[t]=0;this.length+=i}return this.strip()},o.prototype.ishln=function(e){return r(0===this.negative),this.iushln(e)},o.prototype.iushrn=function(e,t,n){var i;r("number"==typeof e&&e>=0),i=t?(t-t%26)/26:0;var o=e%26,s=Math.min((e-o)/26,this.length),a=67108863^67108863>>>o<<o,u=n;if(i-=s,i=Math.max(0,i),u){for(var c=0;c<s;c++)u.words[c]=this.words[c];u.length=s}if(0===s);else if(this.length>s)for(this.length-=s,c=0;c<this.length;c++)this.words[c]=this.words[c+s];else this.words[0]=0,this.length=1;var f=0;for(c=this.length-1;c>=0&&(0!==f||c>=i);c--){var l=0|this.words[c];this.words[c]=f<<26-o|l>>>o,f=l&a}return u&&0!==f&&(u.words[u.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},o.prototype.ishrn=function(e,t,n){return r(0===this.negative),this.iushrn(e,t,n)},o.prototype.shln=function(e){return this.clone().ishln(e)},o.prototype.ushln=function(e){return this.clone().iushln(e)},o.prototype.shrn=function(e){return this.clone().ishrn(e)},o.prototype.ushrn=function(e){return this.clone().iushrn(e)},o.prototype.testn=function(e){r("number"==typeof e&&e>=0);var t=e%26,n=(e-t)/26,i=1<<t;return!(this.length<=n)&&!!(this.words[n]&i)},o.prototype.imaskn=function(e){r("number"==typeof e&&e>=0);var t=e%26,n=(e-t)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=n)return this;if(0!==t&&n++,this.length=Math.min(n,this.length),0!==t){var i=67108863^67108863>>>t<<t;this.words[this.length-1]&=i}return this.strip()},o.prototype.maskn=function(e){return this.clone().imaskn(e)},o.prototype.iaddn=function(e){return r("number"==typeof e),r(e<67108864),e<0?this.isubn(-e):0!==this.negative?1===this.length&&(0|this.words[0])<e?(this.words[0]=e-(0|this.words[0]),this.negative=0,this):(this.negative=0,this.isubn(e),this.negative=1,this):this._iaddn(e)},o.prototype._iaddn=function(e){this.words[0]+=e;for(var t=0;t<this.length&&this.words[t]>=67108864;t++)this.words[t]-=67108864,t===this.length-1?this.words[t+1]=1:this.words[t+1]++;return this.length=Math.max(this.length,t+1),this},o.prototype.isubn=function(e){if(r("number"==typeof e),r(e<67108864),e<0)return this.iaddn(-e);if(0!==this.negative)return this.negative=0,this.iaddn(e),this.negative=1,this;if(this.words[0]-=e,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var t=0;t<this.length&&this.words[t]<0;t++)this.words[t]+=67108864,this.words[t+1]-=1;return this.strip()},o.prototype.addn=function(e){return this.clone().iaddn(e)},o.prototype.subn=function(e){return this.clone().isubn(e)},o.prototype.iabs=function(){return this.negative=0,this},o.prototype.abs=function(){return this.clone().iabs()},o.prototype._ishlnsubmul=function(e,t,n){var i,o,s=e.length+n;this._expand(s);var a=0;for(i=0;i<e.length;i++){o=(0|this.words[i+n])+a;var u=(0|e.words[i])*t;a=((o-=67108863&u)>>26)-(u/67108864|0),this.words[i+n]=67108863&o}for(;i<this.length-n;i++)a=(o=(0|this.words[i+n])+a)>>26,this.words[i+n]=67108863&o;if(0===a)return this.strip();for(r(-1===a),a=0,i=0;i<this.length;i++)a=(o=-(0|this.words[i])+a)>>26,this.words[i]=67108863&o;return this.negative=1,this.strip()},o.prototype._wordDiv=function(e,t){var n=(this.length,e.length),r=this.clone(),i=e,s=0|i.words[i.length-1];0!==(n=26-this._countBits(s))&&(i=i.ushln(n),r.iushln(n),s=0|i.words[i.length-1]);var a,u=r.length-i.length;if("mod"!==t){(a=new o(null)).length=u+1,a.words=new Array(a.length);for(var c=0;c<a.length;c++)a.words[c]=0}var f=r.clone()._ishlnsubmul(i,1,u);0===f.negative&&(r=f,a&&(a.words[u]=1));for(var l=u-1;l>=0;l--){var d=67108864*(0|r.words[i.length+l])+(0|r.words[i.length+l-1]);for(d=Math.min(d/s|0,67108863),r._ishlnsubmul(i,d,l);0!==r.negative;)d--,r.negative=0,r._ishlnsubmul(i,1,l),r.isZero()||(r.negative^=1);a&&(a.words[l]=d)}return a&&a.strip(),r.strip(),"div"!==t&&0!==n&&r.iushrn(n),{div:a||null,mod:r}},o.prototype.divmod=function(e,t,n){return r(!e.isZero()),this.isZero()?{div:new o(0),mod:new o(0)}:0!==this.negative&&0===e.negative?(a=this.neg().divmod(e,t),"mod"!==t&&(i=a.div.neg()),"div"!==t&&(s=a.mod.neg(),n&&0!==s.negative&&s.iadd(e)),{div:i,mod:s}):0===this.negative&&0!==e.negative?(a=this.divmod(e.neg(),t),"mod"!==t&&(i=a.div.neg()),{div:i,mod:a.mod}):0!=(this.negative&e.negative)?(a=this.neg().divmod(e.neg(),t),"div"!==t&&(s=a.mod.neg(),n&&0!==s.negative&&s.isub(e)),{div:a.div,mod:s}):e.length>this.length||this.cmp(e)<0?{div:new o(0),mod:this}:1===e.length?"div"===t?{div:this.divn(e.words[0]),mod:null}:"mod"===t?{div:null,mod:new o(this.modn(e.words[0]))}:{div:this.divn(e.words[0]),mod:new o(this.modn(e.words[0]))}:this._wordDiv(e,t);var i,s,a},o.prototype.div=function(e){return this.divmod(e,"div",!1).div},o.prototype.mod=function(e){return this.divmod(e,"mod",!1).mod},o.prototype.umod=function(e){return this.divmod(e,"mod",!0).mod},o.prototype.divRound=function(e){var t=this.divmod(e);if(t.mod.isZero())return t.div;var n=0!==t.div.negative?t.mod.isub(e):t.mod,r=e.ushrn(1),i=e.andln(1),o=n.cmp(r);return o<0||1===i&&0===o?t.div:0!==t.div.negative?t.div.isubn(1):t.div.iaddn(1)},o.prototype.modn=function(e){r(e<=67108863);for(var t=(1<<26)%e,n=0,i=this.length-1;i>=0;i--)n=(t*n+(0|this.words[i]))%e;return n},o.prototype.idivn=function(e){r(e<=67108863);for(var t=0,n=this.length-1;n>=0;n--){var i=(0|this.words[n])+67108864*t;this.words[n]=i/e|0,t=i%e}return this.strip()},o.prototype.divn=function(e){return this.clone().idivn(e)},o.prototype.egcd=function(e){r(0===e.negative),r(!e.isZero());var t=this,n=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i=new o(1),s=new o(0),a=new o(0),u=new o(1),c=0;t.isEven()&&n.isEven();)t.iushrn(1),n.iushrn(1),++c;for(var f=n.clone(),l=t.clone();!t.isZero();){for(var d=0,h=1;0==(t.words[0]&h)&&d<26;++d,h<<=1);if(d>0)for(t.iushrn(d);d-- >0;)(i.isOdd()||s.isOdd())&&(i.iadd(f),s.isub(l)),i.iushrn(1),s.iushrn(1);for(var p=0,v=1;0==(n.words[0]&v)&&p<26;++p,v<<=1);if(p>0)for(n.iushrn(p);p-- >0;)(a.isOdd()||u.isOdd())&&(a.iadd(f),u.isub(l)),a.iushrn(1),u.iushrn(1);t.cmp(n)>=0?(t.isub(n),i.isub(a),s.isub(u)):(n.isub(t),a.isub(i),u.isub(s))}return{a:a,b:u,gcd:n.iushln(c)}},o.prototype._invmp=function(e){r(0===e.negative),r(!e.isZero());var t=this,n=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i,s=new o(1),a=new o(0),u=n.clone();t.cmpn(1)>0&&n.cmpn(1)>0;){for(var c=0,f=1;0==(t.words[0]&f)&&c<26;++c,f<<=1);if(c>0)for(t.iushrn(c);c-- >0;)s.isOdd()&&s.iadd(u),s.iushrn(1);for(var l=0,d=1;0==(n.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(n.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(u),a.iushrn(1);t.cmp(n)>=0?(t.isub(n),s.isub(a)):(n.isub(t),a.isub(s))}return(i=0===t.cmpn(1)?s:a).cmpn(0)<0&&i.iadd(e),i},o.prototype.gcd=function(e){if(this.isZero())return e.abs();if(e.isZero())return this.abs();var t=this.clone(),n=e.clone();t.negative=0,n.negative=0;for(var r=0;t.isEven()&&n.isEven();r++)t.iushrn(1),n.iushrn(1);for(;;){for(;t.isEven();)t.iushrn(1);for(;n.isEven();)n.iushrn(1);var i=t.cmp(n);if(i<0){var o=t;t=n,n=o}else if(0===i||0===n.cmpn(1))break;t.isub(n)}return n.iushln(r)},o.prototype.invm=function(e){return this.egcd(e).a.umod(e)},o.prototype.isEven=function(){return 0==(1&this.words[0])},o.prototype.isOdd=function(){return 1==(1&this.words[0])},o.prototype.andln=function(e){return this.words[0]&e},o.prototype.bincn=function(e){r("number"==typeof e);var t=e%26,n=(e-t)/26,i=1<<t;if(this.length<=n)return this._expand(n+1),this.words[n]|=i,this;for(var o=i,s=n;0!==o&&s<this.length;s++){var a=0|this.words[s];o=(a+=o)>>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},o.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},o.prototype.cmpn=function(e){var t,n=e<0;if(0!==this.negative&&!n)return-1;if(0===this.negative&&n)return 1;if(this.strip(),this.length>1)t=1;else{n&&(e=-e),r(e<=67108863,"Number is too big");var i=0|this.words[0];t=i===e?0:i<e?-1:1}return 0!==this.negative?0|-t:t},o.prototype.cmp=function(e){if(0!==this.negative&&0===e.negative)return-1;if(0===this.negative&&0!==e.negative)return 1;var t=this.ucmp(e);return 0!==this.negative?0|-t:t},o.prototype.ucmp=function(e){if(this.length>e.length)return 1;if(this.length<e.length)return-1;for(var t=0,n=this.length-1;n>=0;n--){var r=0|this.words[n],i=0|e.words[n];if(r!==i){r<i?t=-1:r>i&&(t=1);break}}return t},o.prototype.gtn=function(e){return 1===this.cmpn(e)},o.prototype.gt=function(e){return 1===this.cmp(e)},o.prototype.gten=function(e){return this.cmpn(e)>=0},o.prototype.gte=function(e){return this.cmp(e)>=0},o.prototype.ltn=function(e){return-1===this.cmpn(e)},o.prototype.lt=function(e){return-1===this.cmp(e)},o.prototype.lten=function(e){return this.cmpn(e)<=0},o.prototype.lte=function(e){return this.cmp(e)<=0},o.prototype.eqn=function(e){return 0===this.cmpn(e)},o.prototype.eq=function(e){return 0===this.cmp(e)},o.red=function(e){return new S(e)},o.prototype.toRed=function(e){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),e.convertTo(this)._forceRed(e)},o.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},o.prototype._forceRed=function(e){return this.red=e,this},o.prototype.forceRed=function(e){return r(!this.red,"Already a number in reduction context"),this._forceRed(e)},o.prototype.redAdd=function(e){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,e)},o.prototype.redIAdd=function(e){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,e)},o.prototype.redSub=function(e){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,e)},o.prototype.redISub=function(e){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,e)},o.prototype.redShl=function(e){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,e)},o.prototype.redMul=function(e){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.mul(this,e)},o.prototype.redIMul=function(e){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.imul(this,e)},o.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},o.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},o.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},o.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},o.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},o.prototype.redPow=function(e){return r(this.red&&!e.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,e)};var g={k256:null,p224:null,p192:null,p25519:null};function m(e,t){this.name=e,this.p=new o(t,16),this.n=this.p.bitLength(),this.k=new o(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function b(){m.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function y(){m.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){m.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function _(){m.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function S(e){if("string"==typeof e){var t=o._prime(e);this.m=t.p,this.prime=t}else r(e.gtn(1),"modulus must be greater than 1"),this.m=e,this.prime=null}function E(e){S.call(this,e),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new o(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}m.prototype._tmp=function(){var e=new o(null);return e.words=new Array(Math.ceil(this.n/13)),e},m.prototype.ireduce=function(e){var t,n=e;do{this.split(n,this.tmp),t=(n=(n=this.imulK(n)).iadd(this.tmp)).bitLength()}while(t>this.n);var r=t<this.n?-1:n.ucmp(this.p);return 0===r?(n.words[0]=0,n.length=1):r>0?n.isub(this.p):void 0!==n.strip?n.strip():n._strip(),n},m.prototype.split=function(e,t){e.iushrn(this.n,0,t)},m.prototype.imulK=function(e){return e.imul(this.k)},i(b,m),b.prototype.split=function(e,t){for(var n=Math.min(e.length,9),r=0;r<n;r++)t.words[r]=e.words[r];if(t.length=n,e.length<=9)return e.words[0]=0,void(e.length=1);var i=e.words[9];for(t.words[t.length++]=4194303&i,r=10;r<e.length;r++){var o=0|e.words[r];e.words[r-10]=(4194303&o)<<4|i>>>22,i=o}i>>>=22,e.words[r-10]=i,0===i&&e.length>10?e.length-=10:e.length-=9},b.prototype.imulK=function(e){e.words[e.length]=0,e.words[e.length+1]=0,e.length+=2;for(var t=0,n=0;n<e.length;n++){var r=0|e.words[n];t+=977*r,e.words[n]=67108863&t,t=64*r+(t/67108864|0)}return 0===e.words[e.length-1]&&(e.length--,0===e.words[e.length-1]&&e.length--),e},i(y,m),i(w,m),i(_,m),_.prototype.imulK=function(e){for(var t=0,n=0;n<e.length;n++){var r=19*(0|e.words[n])+t,i=67108863&r;r>>>=26,e.words[n]=i,t=r}return 0!==t&&(e.words[e.length++]=t),e},o._prime=function(e){if(g[e])return g[e];var t;if("k256"===e)t=new b;else if("p224"===e)t=new y;else if("p192"===e)t=new w;else{if("p25519"!==e)throw new Error("Unknown prime "+e);t=new _}return g[e]=t,t},S.prototype._verify1=function(e){r(0===e.negative,"red works only with positives"),r(e.red,"red works only with red numbers")},S.prototype._verify2=function(e,t){r(0==(e.negative|t.negative),"red works only with positives"),r(e.red&&e.red===t.red,"red works only with red numbers")},S.prototype.imod=function(e){return this.prime?this.prime.ireduce(e)._forceRed(this):e.umod(this.m)._forceRed(this)},S.prototype.neg=function(e){return e.isZero()?e.clone():this.m.sub(e)._forceRed(this)},S.prototype.add=function(e,t){this._verify2(e,t);var n=e.add(t);return n.cmp(this.m)>=0&&n.isub(this.m),n._forceRed(this)},S.prototype.iadd=function(e,t){this._verify2(e,t);var n=e.iadd(t);return n.cmp(this.m)>=0&&n.isub(this.m),n},S.prototype.sub=function(e,t){this._verify2(e,t);var n=e.sub(t);return n.cmpn(0)<0&&n.iadd(this.m),n._forceRed(this)},S.prototype.isub=function(e,t){this._verify2(e,t);var n=e.isub(t);return n.cmpn(0)<0&&n.iadd(this.m),n},S.prototype.shl=function(e,t){return this._verify1(e),this.imod(e.ushln(t))},S.prototype.imul=function(e,t){return this._verify2(e,t),this.imod(e.imul(t))},S.prototype.mul=function(e,t){return this._verify2(e,t),this.imod(e.mul(t))},S.prototype.isqr=function(e){return this.imul(e,e.clone())},S.prototype.sqr=function(e){return this.mul(e,e)},S.prototype.sqrt=function(e){if(e.isZero())return e.clone();var t=this.m.andln(3);if(r(t%2==1),3===t){var n=this.m.add(new o(1)).iushrn(2);return this.pow(e,n)}for(var i=this.m.subn(1),s=0;!i.isZero()&&0===i.andln(1);)s++,i.iushrn(1);r(!i.isZero());var a=new o(1).toRed(this),u=a.redNeg(),c=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new o(2*f*f).toRed(this);0!==this.pow(f,c).cmp(u);)f.redIAdd(u);for(var l=this.pow(f,i),d=this.pow(e,i.addn(1).iushrn(1)),h=this.pow(e,i),p=s;0!==h.cmp(a);){for(var v=h,g=0;0!==v.cmp(a);g++)v=v.redSqr();r(g<p);var m=this.pow(l,new o(1).iushln(p-g-1));d=d.redMul(m),l=m.redSqr(),h=h.redMul(l),p=g}return d},S.prototype.invm=function(e){var t=e._invmp(this.m);return 0!==t.negative?(t.negative=0,this.imod(t).redNeg()):this.imod(t)},S.prototype.pow=function(e,t){if(t.isZero())return new o(1).toRed(this);if(0===t.cmpn(1))return e.clone();var n=new Array(16);n[0]=new o(1).toRed(this),n[1]=e;for(var r=2;r<n.length;r++)n[r]=this.mul(n[r-1],e);var i=n[0],s=0,a=0,u=t.bitLength()%26;for(0===u&&(u=26),r=t.length-1;r>=0;r--){for(var c=t.words[r],f=u-1;f>=0;f--){var l=c>>f&1;i!==n[0]&&(i=this.sqr(i)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===r&&0===f)&&(i=this.mul(i,n[s]),a=0,s=0)):a=0}u=26}return i},S.prototype.convertTo=function(e){var t=e.umod(this.m);return t===e?t.clone():t},S.prototype.convertFrom=function(e){var t=e.clone();return t.red=null,t},o.mont=function(e){return new E(e)},i(E,S),E.prototype.convertTo=function(e){return this.imod(e.ushln(this.shift))},E.prototype.convertFrom=function(e){var t=this.imod(e.mul(this.rinv));return t.red=null,t},E.prototype.imul=function(e,t){if(e.isZero()||t.isZero())return e.words[0]=0,e.length=1,e;var n=e.imul(t),r=n.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=n.isub(r).iushrn(this.shift),o=i;return i.cmp(this.m)>=0?o=i.isub(this.m):i.cmpn(0)<0&&(o=i.iadd(this.m)),o._forceRed(this)},E.prototype.mul=function(e,t){if(e.isZero()||t.isZero())return new o(0)._forceRed(this);var n=e.mul(t),r=n.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=n.isub(r).iushrn(this.shift),s=i;return i.cmp(this.m)>=0?s=i.isub(this.m):i.cmpn(0)<0&&(s=i.iadd(this.m)),s._forceRed(this)},E.prototype.invm=function(e){return this.imod(e._invmp(this.m).mul(this.r2))._forceRed(this)}}(e,this)}).call(this,n(57)(e))},function(e,t,n){"use strict";n.d(t,"a",(function(){return r})),n.d(t,"b",(function(){return C})),n.d(t,"c",(function(){return P})),n.d(t,"d",(function(){return N})),n.d(t,"e",(function(){return V})),n.d(t,"f",(function(){return F})),n.d(t,"g",(function(){return te})),n.d(t,"h",(function(){return j})),n.d(t,"i",(function(){return re})); -/*! - * Copyright 2016 Amazon.com, - * Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the - * License. A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, express or implied. See the License - * for the specific language governing permissions and - * limitations under the License. - */ -var r=function(){function e(e){var t=e||{},n=t.ValidationData,r=t.Username,i=t.Password,o=t.AuthParameters,s=t.ClientMetadata;this.validationData=n||{},this.authParameters=o||{},this.clientMetadata=s||{},this.username=r,this.password=i}var t=e.prototype;return t.getUsername=function(){return this.username},t.getPassword=function(){return this.password},t.getValidationData=function(){return this.validationData},t.getAuthParameters=function(){return this.authParameters},t.getClientMetadata=function(){return this.clientMetadata},e}(),i=n(6),o=n(32),s=n.n(o),a=(n(161),n(87)),u=n.n(a),c=n(75),f=n.n(c),l=n(250);var d,h=function(){function e(e,t){e=this.words=e||[],this.sigBytes=null!=t?t:4*e.length}var t=e.prototype;return t.random=function(t){for(var n=[],r=0;r<t;r+=4)n.push(Object(l.a)());return new e(n,t)},t.toString=function(){return function(e){for(var t=e.words,n=e.sigBytes,r=[],i=0;i<n;i++){var o=t[i>>>2]>>>24-i%4*8&255;r.push((o>>>4).toString(16)),r.push((15&o).toString(16))}return r.join("")}(this)},e}(),p=v;function v(e,t){null!=e&&this.fromString(e,t)}function g(){return new v(null)}var m="undefined"!=typeof navigator;m&&"Microsoft Internet Explorer"==navigator.appName?(v.prototype.am=function(e,t,n,r,i,o){for(var s=32767&t,a=t>>15;--o>=0;){var u=32767&this[e],c=this[e++]>>15,f=a*u+c*s;i=((u=s*u+((32767&f)<<15)+n[r]+(1073741823&i))>>>30)+(f>>>15)+a*c+(i>>>30),n[r++]=1073741823&u}return i},d=30):m&&"Netscape"!=navigator.appName?(v.prototype.am=function(e,t,n,r,i,o){for(;--o>=0;){var s=t*this[e++]+n[r]+i;i=Math.floor(s/67108864),n[r++]=67108863&s}return i},d=26):(v.prototype.am=function(e,t,n,r,i,o){for(var s=16383&t,a=t>>14;--o>=0;){var u=16383&this[e],c=this[e++]>>14,f=a*u+c*s;i=((u=s*u+((16383&f)<<14)+n[r]+i)>>28)+(f>>14)+a*c,n[r++]=268435455&u}return i},d=28),v.prototype.DB=d,v.prototype.DM=(1<<d)-1,v.prototype.DV=1<<d;v.prototype.FV=Math.pow(2,52),v.prototype.F1=52-d,v.prototype.F2=2*d-52;var b,y,w=new Array;for(b="0".charCodeAt(0),y=0;y<=9;++y)w[b++]=y;for(b="a".charCodeAt(0),y=10;y<36;++y)w[b++]=y;for(b="A".charCodeAt(0),y=10;y<36;++y)w[b++]=y;function _(e){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(e)}function S(e,t){var n=w[e.charCodeAt(t)];return null==n?-1:n}function E(e){var t=g();return t.fromInt(e),t}function M(e){var t,n=1;return 0!=(t=e>>>16)&&(e=t,n+=16),0!=(t=e>>8)&&(e=t,n+=8),0!=(t=e>>4)&&(e=t,n+=4),0!=(t=e>>2)&&(e=t,n+=2),0!=(t=e>>1)&&(e=t,n+=1),n}function A(e){this.m=e,this.mp=e.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<<e.DB-15)-1,this.mt2=2*e.t}A.prototype.convert=function(e){var t=g();return e.abs().dlShiftTo(this.m.t,t),t.divRemTo(this.m,null,t),e.s<0&&t.compareTo(v.ZERO)>0&&this.m.subTo(t,t),t},A.prototype.revert=function(e){var t=g();return e.copyTo(t),this.reduce(t),t},A.prototype.reduce=function(e){for(;e.t<=this.mt2;)e[e.t++]=0;for(var t=0;t<this.m.t;++t){var n=32767&e[t],r=n*this.mpl+((n*this.mph+(e[t]>>15)*this.mpl&this.um)<<15)&e.DM;for(e[n=t+this.m.t]+=this.m.am(0,r,e,t,0,this.m.t);e[n]>=e.DV;)e[n]-=e.DV,e[++n]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)},A.prototype.mulTo=function(e,t,n){e.multiplyTo(t,n),this.reduce(n)},A.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},v.prototype.copyTo=function(e){for(var t=this.t-1;t>=0;--t)e[t]=this[t];e.t=this.t,e.s=this.s},v.prototype.fromInt=function(e){this.t=1,this.s=e<0?-1:0,e>0?this[0]=e:e<-1?this[0]=e+this.DV:this.t=0},v.prototype.fromString=function(e,t){var n;if(16==t)n=4;else if(8==t)n=3;else if(2==t)n=1;else if(32==t)n=5;else{if(4!=t)throw new Error("Only radix 2, 4, 8, 16, 32 are supported");n=2}this.t=0,this.s=0;for(var r=e.length,i=!1,o=0;--r>=0;){var s=S(e,r);s<0?"-"==e.charAt(r)&&(i=!0):(i=!1,0==o?this[this.t++]=s:o+n>this.DB?(this[this.t-1]|=(s&(1<<this.DB-o)-1)<<o,this[this.t++]=s>>this.DB-o):this[this.t-1]|=s<<o,(o+=n)>=this.DB&&(o-=this.DB))}this.clamp(),i&&v.ZERO.subTo(this,this)},v.prototype.clamp=function(){for(var e=this.s&this.DM;this.t>0&&this[this.t-1]==e;)--this.t},v.prototype.dlShiftTo=function(e,t){var n;for(n=this.t-1;n>=0;--n)t[n+e]=this[n];for(n=e-1;n>=0;--n)t[n]=0;t.t=this.t+e,t.s=this.s},v.prototype.drShiftTo=function(e,t){for(var n=e;n<this.t;++n)t[n-e]=this[n];t.t=Math.max(this.t-e,0),t.s=this.s},v.prototype.lShiftTo=function(e,t){var n,r=e%this.DB,i=this.DB-r,o=(1<<i)-1,s=Math.floor(e/this.DB),a=this.s<<r&this.DM;for(n=this.t-1;n>=0;--n)t[n+s+1]=this[n]>>i|a,a=(this[n]&o)<<r;for(n=s-1;n>=0;--n)t[n]=0;t[s]=a,t.t=this.t+s+1,t.s=this.s,t.clamp()},v.prototype.rShiftTo=function(e,t){t.s=this.s;var n=Math.floor(e/this.DB);if(n>=this.t)t.t=0;else{var r=e%this.DB,i=this.DB-r,o=(1<<r)-1;t[0]=this[n]>>r;for(var s=n+1;s<this.t;++s)t[s-n-1]|=(this[s]&o)<<i,t[s-n]=this[s]>>r;r>0&&(t[this.t-n-1]|=(this.s&o)<<i),t.t=this.t-n,t.clamp()}},v.prototype.subTo=function(e,t){for(var n=0,r=0,i=Math.min(e.t,this.t);n<i;)r+=this[n]-e[n],t[n++]=r&this.DM,r>>=this.DB;if(e.t<this.t){for(r-=e.s;n<this.t;)r+=this[n],t[n++]=r&this.DM,r>>=this.DB;r+=this.s}else{for(r+=this.s;n<e.t;)r-=e[n],t[n++]=r&this.DM,r>>=this.DB;r-=e.s}t.s=r<0?-1:0,r<-1?t[n++]=this.DV+r:r>0&&(t[n++]=r),t.t=n,t.clamp()},v.prototype.multiplyTo=function(e,t){var n=this.abs(),r=e.abs(),i=n.t;for(t.t=i+r.t;--i>=0;)t[i]=0;for(i=0;i<r.t;++i)t[i+n.t]=n.am(0,r[i],t,i,0,n.t);t.s=0,t.clamp(),this.s!=e.s&&v.ZERO.subTo(t,t)},v.prototype.squareTo=function(e){for(var t=this.abs(),n=e.t=2*t.t;--n>=0;)e[n]=0;for(n=0;n<t.t-1;++n){var r=t.am(n,t[n],e,2*n,0,1);(e[n+t.t]+=t.am(n+1,2*t[n],e,2*n+1,r,t.t-n-1))>=t.DV&&(e[n+t.t]-=t.DV,e[n+t.t+1]=1)}e.t>0&&(e[e.t-1]+=t.am(n,t[n],e,2*n,0,1)),e.s=0,e.clamp()},v.prototype.divRemTo=function(e,t,n){var r=e.abs();if(!(r.t<=0)){var i=this.abs();if(i.t<r.t)return null!=t&&t.fromInt(0),void(null!=n&&this.copyTo(n));null==n&&(n=g());var o=g(),s=this.s,a=e.s,u=this.DB-M(r[r.t-1]);u>0?(r.lShiftTo(u,o),i.lShiftTo(u,n)):(r.copyTo(o),i.copyTo(n));var c=o.t,f=o[c-1];if(0!=f){var l=f*(1<<this.F1)+(c>1?o[c-2]>>this.F2:0),d=this.FV/l,h=(1<<this.F1)/l,p=1<<this.F2,m=n.t,b=m-c,y=null==t?g():t;for(o.dlShiftTo(b,y),n.compareTo(y)>=0&&(n[n.t++]=1,n.subTo(y,n)),v.ONE.dlShiftTo(c,y),y.subTo(o,o);o.t<c;)o[o.t++]=0;for(;--b>=0;){var w=n[--m]==f?this.DM:Math.floor(n[m]*d+(n[m-1]+p)*h);if((n[m]+=o.am(0,w,n,b,0,c))<w)for(o.dlShiftTo(b,y),n.subTo(y,n);n[m]<--w;)n.subTo(y,n)}null!=t&&(n.drShiftTo(c,t),s!=a&&v.ZERO.subTo(t,t)),n.t=c,n.clamp(),u>0&&n.rShiftTo(u,n),s<0&&v.ZERO.subTo(n,n)}}},v.prototype.invDigit=function(){if(this.t<1)return 0;var e=this[0];if(0==(1&e))return 0;var t=3&e;return(t=(t=(t=(t=t*(2-(15&e)*t)&15)*(2-(255&e)*t)&255)*(2-((65535&e)*t&65535))&65535)*(2-e*t%this.DV)%this.DV)>0?this.DV-t:-t},v.prototype.addTo=function(e,t){for(var n=0,r=0,i=Math.min(e.t,this.t);n<i;)r+=this[n]+e[n],t[n++]=r&this.DM,r>>=this.DB;if(e.t<this.t){for(r+=e.s;n<this.t;)r+=this[n],t[n++]=r&this.DM,r>>=this.DB;r+=this.s}else{for(r+=this.s;n<e.t;)r+=e[n],t[n++]=r&this.DM,r>>=this.DB;r+=e.s}t.s=r<0?-1:0,r>0?t[n++]=r:r<-1&&(t[n++]=this.DV+r),t.t=n,t.clamp()},v.prototype.toString=function(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(16==e)t=4;else if(8==e)t=3;else if(2==e)t=1;else if(32==e)t=5;else{if(4!=e)throw new Error("Only radix 2, 4, 8, 16, 32 are supported");t=2}var n,r=(1<<t)-1,i=!1,o="",s=this.t,a=this.DB-s*this.DB%t;if(s-- >0)for(a<this.DB&&(n=this[s]>>a)>0&&(i=!0,o=_(n));s>=0;)a<t?(n=(this[s]&(1<<a)-1)<<t-a,n|=this[--s]>>(a+=this.DB-t)):(n=this[s]>>(a-=t)&r,a<=0&&(a+=this.DB,--s)),n>0&&(i=!0),i&&(o+=_(n));return i?o:"0"},v.prototype.negate=function(){var e=g();return v.ZERO.subTo(this,e),e},v.prototype.abs=function(){return this.s<0?this.negate():this},v.prototype.compareTo=function(e){var t=this.s-e.s;if(0!=t)return t;var n=this.t;if(0!=(t=n-e.t))return this.s<0?-t:t;for(;--n>=0;)if(0!=(t=this[n]-e[n]))return t;return 0},v.prototype.bitLength=function(){return this.t<=0?0:this.DB*(this.t-1)+M(this[this.t-1]^this.s&this.DM)},v.prototype.mod=function(e){var t=g();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(v.ZERO)>0&&e.subTo(t,t),t},v.prototype.equals=function(e){return 0==this.compareTo(e)},v.prototype.add=function(e){var t=g();return this.addTo(e,t),t},v.prototype.subtract=function(e){var t=g();return this.subTo(e,t),t},v.prototype.multiply=function(e){var t=g();return this.multiplyTo(e,t),t},v.prototype.divide=function(e){var t=g();return this.divRemTo(e,t,null),t},v.prototype.modPow=function(e,t,n){var r,i=e.bitLength(),o=E(1),s=new A(t);if(i<=0)return o;r=i<18?1:i<48?3:i<144?4:i<768?5:6;var a=new Array,u=3,c=r-1,f=(1<<r)-1;if(a[1]=s.convert(this),r>1){var l=g();for(s.sqrTo(a[1],l);u<=f;)a[u]=g(),s.mulTo(l,a[u-2],a[u]),u+=2}var d,h,p=e.t-1,v=!0,m=g();for(i=M(e[p])-1;p>=0;){for(i>=c?d=e[p]>>i-c&f:(d=(e[p]&(1<<i+1)-1)<<c-i,p>0&&(d|=e[p-1]>>this.DB+i-c)),u=r;0==(1&d);)d>>=1,--u;if((i-=u)<0&&(i+=this.DB,--p),v)a[d].copyTo(o),v=!1;else{for(;u>1;)s.sqrTo(o,m),s.sqrTo(m,o),u-=2;u>0?s.sqrTo(o,m):(h=o,o=m,m=h),s.mulTo(m,a[d],o)}for(;p>=0&&0==(e[p]&1<<i);)s.sqrTo(o,m),h=o,o=m,m=h,--i<0&&(i=this.DB-1,--p)}var b=s.revert(o);return n(null,b),b},v.ZERO=E(0),v.ONE=E(1); -/*! - * Copyright 2016 Amazon.com, - * Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the - * License. A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, express or implied. See the License - * for the specific language governing permissions and - * limitations under the License. - */ -var I=function(e){return i.Buffer.from((new h).random(e).toString(),"hex")},k=function(){function e(e){this.N=new p("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",16),this.g=new p("2",16),this.k=new p(this.hexHash("00"+this.N.toString(16)+"0"+this.g.toString(16)),16),this.smallAValue=this.generateRandomSmallA(),this.getLargeAValue((function(){})),this.infoBits=i.Buffer.from("Caldera Derived Key","utf8"),this.poolName=e}var t=e.prototype;return t.getSmallAValue=function(){return this.smallAValue},t.getLargeAValue=function(e){var t=this;this.largeAValue?e(null,this.largeAValue):this.calculateA(this.smallAValue,(function(n,r){n&&e(n,null),t.largeAValue=r,e(null,t.largeAValue)}))},t.generateRandomSmallA=function(){var e=I(128).toString("hex");return new p(e,16).mod(this.N)},t.generateRandomString=function(){return I(40).toString("base64")},t.getRandomPassword=function(){return this.randomPassword},t.getSaltDevices=function(){return this.SaltToHashDevices},t.getVerifierDevices=function(){return this.verifierDevices},t.generateHashDevice=function(e,t,n){var r=this;this.randomPassword=this.generateRandomString();var i=""+e+t+":"+this.randomPassword,o=this.hash(i),s=I(16).toString("hex");this.SaltToHashDevices=this.padHex(new p(s,16)),this.g.modPow(new p(this.hexHash(this.SaltToHashDevices+o),16),this.N,(function(e,t){e&&n(e,null),r.verifierDevices=r.padHex(t),n(null,null)}))},t.calculateA=function(e,t){var n=this;this.g.modPow(e,this.N,(function(e,r){e&&t(e,null),r.mod(n.N).equals(p.ZERO)&&t(new Error("Illegal paramater. A mod N cannot be 0."),null),t(null,r)}))},t.calculateU=function(e,t){return this.UHexHash=this.hexHash(this.padHex(e)+this.padHex(t)),new p(this.UHexHash,16)},t.hash=function(e){var t=e instanceof i.Buffer?s.a.lib.WordArray.create(e):e,n=u()(t).toString();return new Array(64-n.length).join("0")+n},t.hexHash=function(e){return this.hash(i.Buffer.from(e,"hex"))},t.computehkdf=function(e,t){var n=s.a.lib.WordArray.create(i.Buffer.concat([this.infoBits,i.Buffer.from(String.fromCharCode(1),"utf8")])),r=e instanceof i.Buffer?s.a.lib.WordArray.create(e):e,o=t instanceof i.Buffer?s.a.lib.WordArray.create(t):t,a=f()(r,o),u=f()(n,a);return i.Buffer.from(u.toString(),"hex").slice(0,16)},t.getPasswordAuthenticationKey=function(e,t,n,r,o){var s=this;if(n.mod(this.N).equals(p.ZERO))throw new Error("B cannot be zero.");if(this.UValue=this.calculateU(this.largeAValue,n),this.UValue.equals(p.ZERO))throw new Error("U cannot be zero.");var a=""+this.poolName+e+":"+t,u=this.hash(a),c=new p(this.hexHash(this.padHex(r)+u),16);this.calculateS(c,n,(function(e,t){e&&o(e,null);var n=s.computehkdf(i.Buffer.from(s.padHex(t),"hex"),i.Buffer.from(s.padHex(s.UValue.toString(16)),"hex"));o(null,n)}))},t.calculateS=function(e,t,n){var r=this;this.g.modPow(e,this.N,(function(i,o){i&&n(i,null),t.subtract(r.k.multiply(o)).modPow(r.smallAValue.add(r.UValue.multiply(e)),r.N,(function(e,t){e&&n(e,null),n(null,t.mod(r.N))}))}))},t.getNewPasswordRequiredChallengeUserAttributePrefix=function(){return"userAttributes."},t.padHex=function(e){var t=e.toString(16);return t.length%2==1?t="0"+t:-1!=="89ABCDEFabcdef".indexOf(t[0])&&(t="00"+t),t},e}(),O=function(){function e(e){this.jwtToken=e||"",this.payload=this.decodePayload()}var t=e.prototype;return t.getJwtToken=function(){return this.jwtToken},t.getExpiration=function(){return this.payload.exp},t.getIssuedAt=function(){return this.payload.iat},t.decodePayload=function(){var e=this.jwtToken.split(".")[1];try{return JSON.parse(i.Buffer.from(e,"base64").toString("utf8"))}catch(e){return{}}},e}();function x(e,t){return(x=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}var C=function(e){var t,n;function r(t){var n=(void 0===t?{}:t).AccessToken;return e.call(this,n||"")||this}return n=e,(t=r).prototype=Object.create(n.prototype),t.prototype.constructor=t,x(t,n),r}(O);function T(e,t){return(T=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)} -/*! - * Copyright 2016 Amazon.com, - * Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the - * License. A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, express or implied. See the License - * for the specific language governing permissions and - * limitations under the License. - */var P=function(e){var t,n;function r(t){var n=(void 0===t?{}:t).IdToken;return e.call(this,n||"")||this}return n=e,(t=r).prototype=Object.create(n.prototype),t.prototype.constructor=t,T(t,n),r}(O),N=function(){function e(e){var t=(void 0===e?{}:e).RefreshToken;this.token=t||""}return e.prototype.getToken=function(){return this.token},e}(),R=n(90),L=n.n(R),j=function(){function e(e){var t=void 0===e?{}:e,n=t.IdToken,r=t.RefreshToken,i=t.AccessToken,o=t.ClockDrift;if(null==i||null==n)throw new Error("Id token and Access Token must be present.");this.idToken=n,this.refreshToken=r,this.accessToken=i,this.clockDrift=void 0===o?this.calculateClockDrift():o}var t=e.prototype;return t.getIdToken=function(){return this.idToken},t.getRefreshToken=function(){return this.refreshToken},t.getAccessToken=function(){return this.accessToken},t.getClockDrift=function(){return this.clockDrift},t.calculateClockDrift=function(){return Math.floor(new Date/1e3)-Math.min(this.accessToken.getIssuedAt(),this.idToken.getIssuedAt())},t.isValid=function(){var e=Math.floor(new Date/1e3)-this.clockDrift;return e<this.accessToken.getExpiration()&&e<this.idToken.getExpiration()},e}(),D=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],U=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],B=function(){function e(){}return e.prototype.getNowString=function(){var e=new Date,t=U[e.getUTCDay()],n=D[e.getUTCMonth()],r=e.getUTCDate(),i=e.getUTCHours();i<10&&(i="0"+i);var o=e.getUTCMinutes();o<10&&(o="0"+o);var s=e.getUTCSeconds();return s<10&&(s="0"+s),t+" "+n+" "+r+" "+i+":"+o+":"+s+" UTC "+e.getUTCFullYear()},e}(),F=function(){function e(e){var t=void 0===e?{}:e,n=t.Name,r=t.Value;this.Name=n||"",this.Value=r||""}var t=e.prototype;return t.getValue=function(){return this.Value},t.setValue=function(e){return this.Value=e,this},t.getName=function(){return this.Name},t.setName=function(e){return this.Name=e,this},t.toString=function(){return JSON.stringify(this)},t.toJSON=function(){return{Name:this.Name,Value:this.Value}},e}(),z={},q=function(){function e(){}return e.setItem=function(e,t){return z[e]=t,z[e]},e.getItem=function(e){return Object.prototype.hasOwnProperty.call(z,e)?z[e]:void 0},e.removeItem=function(e){return delete z[e]},e.clear=function(){return z={}},e}(),K=function(){function e(){try{this.storageWindow=window.localStorage,this.storageWindow.setItem("aws.cognito.test-ls",1),this.storageWindow.removeItem("aws.cognito.test-ls")}catch(e){this.storageWindow=q}}return e.prototype.getStorage=function(){return this.storageWindow},e}(),H="undefined"!=typeof navigator?navigator.userAgent:"nodejs",V=function(){function e(e){if(null==e||null==e.Username||null==e.Pool)throw new Error("Username and Pool information are required.");this.username=e.Username||"",this.pool=e.Pool,this.Session=null,this.client=e.Pool.client,this.signInUserSession=null,this.authenticationFlowType="USER_SRP_AUTH",this.storage=e.Storage||(new K).getStorage(),this.keyPrefix="CognitoIdentityServiceProvider."+this.pool.getClientId(),this.userDataKey=this.keyPrefix+"."+this.username+".userData"}var t=e.prototype;return t.setSignInUserSession=function(e){this.clearCachedUserData(),this.signInUserSession=e,this.cacheTokens()},t.getSignInUserSession=function(){return this.signInUserSession},t.getUsername=function(){return this.username},t.getAuthenticationFlowType=function(){return this.authenticationFlowType},t.setAuthenticationFlowType=function(e){this.authenticationFlowType=e},t.initiateAuth=function(e,t){var n=this,r=e.getAuthParameters();r.USERNAME=this.username;var i=0!==Object.keys(e.getValidationData()).length?e.getValidationData():e.getClientMetadata(),o={AuthFlow:"CUSTOM_AUTH",ClientId:this.pool.getClientId(),AuthParameters:r,ClientMetadata:i};this.getUserContextData()&&(o.UserContextData=this.getUserContextData()),this.client.request("InitiateAuth",o,(function(e,r){if(e)return t.onFailure(e);var i=r.ChallengeName,o=r.ChallengeParameters;return"CUSTOM_CHALLENGE"===i?(n.Session=r.Session,t.customChallenge(o)):(n.signInUserSession=n.getCognitoUserSession(r.AuthenticationResult),n.cacheTokens(),t.onSuccess(n.signInUserSession))}))},t.authenticateUser=function(e,t){return"USER_PASSWORD_AUTH"===this.authenticationFlowType?this.authenticateUserPlainUsernamePassword(e,t):"USER_SRP_AUTH"===this.authenticationFlowType||"CUSTOM_AUTH"===this.authenticationFlowType?this.authenticateUserDefaultAuth(e,t):t.onFailure(new Error("Authentication flow type is invalid."))},t.authenticateUserDefaultAuth=function(e,t){var n,r,o=this,a=new k(this.pool.getUserPoolId().split("_")[1]),u=new B,c={};null!=this.deviceKey&&(c.DEVICE_KEY=this.deviceKey),c.USERNAME=this.username,a.getLargeAValue((function(l,d){l&&t.onFailure(l),c.SRP_A=d.toString(16),"CUSTOM_AUTH"===o.authenticationFlowType&&(c.CHALLENGE_NAME="SRP_A");var h=0!==Object.keys(e.getValidationData()).length?e.getValidationData():e.getClientMetadata(),v={AuthFlow:o.authenticationFlowType,ClientId:o.pool.getClientId(),AuthParameters:c,ClientMetadata:h};o.getUserContextData(o.username)&&(v.UserContextData=o.getUserContextData(o.username)),o.client.request("InitiateAuth",v,(function(c,l){if(c)return t.onFailure(c);var d=l.ChallengeParameters;o.username=d.USER_ID_FOR_SRP,n=new p(d.SRP_B,16),r=new p(d.SALT,16),o.getCachedDeviceKeyAndPassword(),a.getPasswordAuthenticationKey(o.username,e.getPassword(),n,r,(function(e,n){e&&t.onFailure(e);var r=u.getNowString(),c=s.a.lib.WordArray.create(i.Buffer.concat([i.Buffer.from(o.pool.getUserPoolId().split("_")[1],"utf8"),i.Buffer.from(o.username,"utf8"),i.Buffer.from(d.SECRET_BLOCK,"base64"),i.Buffer.from(r,"utf8")])),p=s.a.lib.WordArray.create(n),v=L.a.stringify(f()(c,p)),g={};g.USERNAME=o.username,g.PASSWORD_CLAIM_SECRET_BLOCK=d.SECRET_BLOCK,g.TIMESTAMP=r,g.PASSWORD_CLAIM_SIGNATURE=v,null!=o.deviceKey&&(g.DEVICE_KEY=o.deviceKey);var m={ChallengeName:"PASSWORD_VERIFIER",ClientId:o.pool.getClientId(),ChallengeResponses:g,Session:l.Session,ClientMetadata:h};o.getUserContextData()&&(m.UserContextData=o.getUserContextData()),function e(t,n){return o.client.request("RespondToAuthChallenge",t,(function(r,i){return r&&"ResourceNotFoundException"===r.code&&-1!==r.message.toLowerCase().indexOf("device")?(g.DEVICE_KEY=null,o.deviceKey=null,o.randomPassword=null,o.deviceGroupKey=null,o.clearCachedDeviceKeyAndPassword(),e(t,n)):n(r,i)}))}(m,(function(e,n){return e?t.onFailure(e):o.authenticateUserInternal(n,a,t)}))}))}))}))},t.authenticateUserPlainUsernamePassword=function(e,t){var n=this,r={};if(r.USERNAME=this.username,r.PASSWORD=e.getPassword(),r.PASSWORD){var i=new k(this.pool.getUserPoolId().split("_")[1]);this.getCachedDeviceKeyAndPassword(),null!=this.deviceKey&&(r.DEVICE_KEY=this.deviceKey);var o=0!==Object.keys(e.getValidationData()).length?e.getValidationData():e.getClientMetadata(),s={AuthFlow:"USER_PASSWORD_AUTH",ClientId:this.pool.getClientId(),AuthParameters:r,ClientMetadata:o};this.getUserContextData(this.username)&&(s.UserContextData=this.getUserContextData(this.username)),this.client.request("InitiateAuth",s,(function(e,r){return e?t.onFailure(e):n.authenticateUserInternal(r,i,t)}))}else t.onFailure(new Error("PASSWORD parameter is required"))},t.authenticateUserInternal=function(e,t,n){var r=this,o=e.ChallengeName,s=e.ChallengeParameters;if("SMS_MFA"===o)return this.Session=e.Session,n.mfaRequired(o,s);if("SELECT_MFA_TYPE"===o)return this.Session=e.Session,n.selectMFAType(o,s);if("MFA_SETUP"===o)return this.Session=e.Session,n.mfaSetup(o,s);if("SOFTWARE_TOKEN_MFA"===o)return this.Session=e.Session,n.totpRequired(o,s);if("CUSTOM_CHALLENGE"===o)return this.Session=e.Session,n.customChallenge(s);if("NEW_PASSWORD_REQUIRED"===o){this.Session=e.Session;var a=null,u=null,c=[],f=t.getNewPasswordRequiredChallengeUserAttributePrefix();if(s&&(a=JSON.parse(e.ChallengeParameters.userAttributes),u=JSON.parse(e.ChallengeParameters.requiredAttributes)),u)for(var l=0;l<u.length;l++)c[l]=u[l].substr(f.length);return n.newPasswordRequired(a,c)}if("DEVICE_SRP_AUTH"!==o){this.signInUserSession=this.getCognitoUserSession(e.AuthenticationResult),this.challengeName=o,this.cacheTokens();var d=e.AuthenticationResult.NewDeviceMetadata;if(null==d)return n.onSuccess(this.signInUserSession);t.generateHashDevice(e.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey,e.AuthenticationResult.NewDeviceMetadata.DeviceKey,(function(o){if(o)return n.onFailure(o);var s={Salt:i.Buffer.from(t.getSaltDevices(),"hex").toString("base64"),PasswordVerifier:i.Buffer.from(t.getVerifierDevices(),"hex").toString("base64")};r.verifierDevices=s.PasswordVerifier,r.deviceGroupKey=d.DeviceGroupKey,r.randomPassword=t.getRandomPassword(),r.client.request("ConfirmDevice",{DeviceKey:d.DeviceKey,AccessToken:r.signInUserSession.getAccessToken().getJwtToken(),DeviceSecretVerifierConfig:s,DeviceName:H},(function(t,i){return t?n.onFailure(t):(r.deviceKey=e.AuthenticationResult.NewDeviceMetadata.DeviceKey,r.cacheDeviceKeyAndPassword(),!0===i.UserConfirmationNecessary?n.onSuccess(r.signInUserSession,i.UserConfirmationNecessary):n.onSuccess(r.signInUserSession))}))}))}else this.getDeviceResponse(n)},t.completeNewPasswordChallenge=function(e,t,n,r){var i=this;if(!e)return n.onFailure(new Error("New password is required."));var o=new k(this.pool.getUserPoolId().split("_")[1]),s=o.getNewPasswordRequiredChallengeUserAttributePrefix(),a={};t&&Object.keys(t).forEach((function(e){a[s+e]=t[e]})),a.NEW_PASSWORD=e,a.USERNAME=this.username;var u={ChallengeName:"NEW_PASSWORD_REQUIRED",ClientId:this.pool.getClientId(),ChallengeResponses:a,Session:this.Session,ClientMetadata:r};this.getUserContextData()&&(u.UserContextData=this.getUserContextData()),this.client.request("RespondToAuthChallenge",u,(function(e,t){return e?n.onFailure(e):i.authenticateUserInternal(t,o,n)}))},t.getDeviceResponse=function(e,t){var n=this,r=new k(this.deviceGroupKey),o=new B,a={};a.USERNAME=this.username,a.DEVICE_KEY=this.deviceKey,r.getLargeAValue((function(u,c){u&&e.onFailure(u),a.SRP_A=c.toString(16);var l={ChallengeName:"DEVICE_SRP_AUTH",ClientId:n.pool.getClientId(),ChallengeResponses:a,ClientMetadata:t};n.getUserContextData()&&(l.UserContextData=n.getUserContextData()),n.client.request("RespondToAuthChallenge",l,(function(t,a){if(t)return e.onFailure(t);var u=a.ChallengeParameters,c=new p(u.SRP_B,16),l=new p(u.SALT,16);r.getPasswordAuthenticationKey(n.deviceKey,n.randomPassword,c,l,(function(t,r){if(t)return e.onFailure(t);var c=o.getNowString(),l=s.a.lib.WordArray.create(i.Buffer.concat([i.Buffer.from(n.deviceGroupKey,"utf8"),i.Buffer.from(n.deviceKey,"utf8"),i.Buffer.from(u.SECRET_BLOCK,"base64"),i.Buffer.from(c,"utf8")])),d=s.a.lib.WordArray.create(r),h=L.a.stringify(f()(l,d)),p={};p.USERNAME=n.username,p.PASSWORD_CLAIM_SECRET_BLOCK=u.SECRET_BLOCK,p.TIMESTAMP=c,p.PASSWORD_CLAIM_SIGNATURE=h,p.DEVICE_KEY=n.deviceKey;var v={ChallengeName:"DEVICE_PASSWORD_VERIFIER",ClientId:n.pool.getClientId(),ChallengeResponses:p,Session:a.Session};n.getUserContextData()&&(v.UserContextData=n.getUserContextData()),n.client.request("RespondToAuthChallenge",v,(function(t,r){return t?e.onFailure(t):(n.signInUserSession=n.getCognitoUserSession(r.AuthenticationResult),n.cacheTokens(),e.onSuccess(n.signInUserSession))}))}))}))}))},t.confirmRegistration=function(e,t,n,r){var i={ClientId:this.pool.getClientId(),ConfirmationCode:e,Username:this.username,ForceAliasCreation:t,ClientMetadata:r};this.getUserContextData()&&(i.UserContextData=this.getUserContextData()),this.client.request("ConfirmSignUp",i,(function(e){return e?n(e,null):n(null,"SUCCESS")}))},t.sendCustomChallengeAnswer=function(e,t,n){var r=this,i={};i.USERNAME=this.username,i.ANSWER=e;var o=new k(this.pool.getUserPoolId().split("_")[1]);this.getCachedDeviceKeyAndPassword(),null!=this.deviceKey&&(i.DEVICE_KEY=this.deviceKey);var s={ChallengeName:"CUSTOM_CHALLENGE",ChallengeResponses:i,ClientId:this.pool.getClientId(),Session:this.Session,ClientMetadata:n};this.getUserContextData()&&(s.UserContextData=this.getUserContextData()),this.client.request("RespondToAuthChallenge",s,(function(e,n){return e?t.onFailure(e):r.authenticateUserInternal(n,o,t)}))},t.sendMFACode=function(e,t,n,r){var o=this,s={};s.USERNAME=this.username,s.SMS_MFA_CODE=e;var a=n||"SMS_MFA";"SOFTWARE_TOKEN_MFA"===a&&(s.SOFTWARE_TOKEN_MFA_CODE=e),null!=this.deviceKey&&(s.DEVICE_KEY=this.deviceKey);var u={ChallengeName:a,ChallengeResponses:s,ClientId:this.pool.getClientId(),Session:this.Session,ClientMetadata:r};this.getUserContextData()&&(u.UserContextData=this.getUserContextData()),this.client.request("RespondToAuthChallenge",u,(function(e,n){if(e)return t.onFailure(e);if("DEVICE_SRP_AUTH"!==n.ChallengeName){if(o.signInUserSession=o.getCognitoUserSession(n.AuthenticationResult),o.cacheTokens(),null==n.AuthenticationResult.NewDeviceMetadata)return t.onSuccess(o.signInUserSession);var r=new k(o.pool.getUserPoolId().split("_")[1]);r.generateHashDevice(n.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey,n.AuthenticationResult.NewDeviceMetadata.DeviceKey,(function(e){if(e)return t.onFailure(e);var s={Salt:i.Buffer.from(r.getSaltDevices(),"hex").toString("base64"),PasswordVerifier:i.Buffer.from(r.getVerifierDevices(),"hex").toString("base64")};o.verifierDevices=s.PasswordVerifier,o.deviceGroupKey=n.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey,o.randomPassword=r.getRandomPassword(),o.client.request("ConfirmDevice",{DeviceKey:n.AuthenticationResult.NewDeviceMetadata.DeviceKey,AccessToken:o.signInUserSession.getAccessToken().getJwtToken(),DeviceSecretVerifierConfig:s,DeviceName:H},(function(e,r){return e?t.onFailure(e):(o.deviceKey=n.AuthenticationResult.NewDeviceMetadata.DeviceKey,o.cacheDeviceKeyAndPassword(),!0===r.UserConfirmationNecessary?t.onSuccess(o.signInUserSession,r.UserConfirmationNecessary):t.onSuccess(o.signInUserSession))}))}))}else o.getDeviceResponse(t)}))},t.changePassword=function(e,t,n,r){if(null==this.signInUserSession||!this.signInUserSession.isValid())return n(new Error("User is not authenticated"),null);this.client.request("ChangePassword",{PreviousPassword:e,ProposedPassword:t,AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),ClientMetadata:r},(function(e){return e?n(e,null):n(null,"SUCCESS")}))},t.enableMFA=function(e){if(null==this.signInUserSession||!this.signInUserSession.isValid())return e(new Error("User is not authenticated"),null);var t=[];t.push({DeliveryMedium:"SMS",AttributeName:"phone_number"}),this.client.request("SetUserSettings",{MFAOptions:t,AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(t){return t?e(t,null):e(null,"SUCCESS")}))},t.setUserMfaPreference=function(e,t,n){if(null==this.signInUserSession||!this.signInUserSession.isValid())return n(new Error("User is not authenticated"),null);this.client.request("SetUserMFAPreference",{SMSMfaSettings:e,SoftwareTokenMfaSettings:t,AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(e){return e?n(e,null):n(null,"SUCCESS")}))},t.disableMFA=function(e){if(null==this.signInUserSession||!this.signInUserSession.isValid())return e(new Error("User is not authenticated"),null);this.client.request("SetUserSettings",{MFAOptions:[],AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(t){return t?e(t,null):e(null,"SUCCESS")}))},t.deleteUser=function(e,t){var n=this;if(null==this.signInUserSession||!this.signInUserSession.isValid())return e(new Error("User is not authenticated"),null);this.client.request("DeleteUser",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),ClientMetadata:t},(function(t){return t?e(t,null):(n.clearCachedUser(),e(null,"SUCCESS"))}))},t.updateAttributes=function(e,t,n){var r=this;if(null==this.signInUserSession||!this.signInUserSession.isValid())return t(new Error("User is not authenticated"),null);this.client.request("UpdateUserAttributes",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),UserAttributes:e,ClientMetadata:n},(function(e){return e?t(e,null):r.getUserData((function(){return t(null,"SUCCESS")}),{bypassCache:!0})}))},t.getUserAttributes=function(e){if(null==this.signInUserSession||!this.signInUserSession.isValid())return e(new Error("User is not authenticated"),null);this.client.request("GetUser",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(t,n){if(t)return e(t,null);for(var r=[],i=0;i<n.UserAttributes.length;i++){var o={Name:n.UserAttributes[i].Name,Value:n.UserAttributes[i].Value},s=new F(o);r.push(s)}return e(null,r)}))},t.getMFAOptions=function(e){if(null==this.signInUserSession||!this.signInUserSession.isValid())return e(new Error("User is not authenticated"),null);this.client.request("GetUser",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(t,n){return t?e(t,null):e(null,n.MFAOptions)}))},t.createGetUserRequest=function(){return this.client.promisifyRequest("GetUser",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken()})},t.refreshSessionIfPossible=function(e){var t=this;return void 0===e&&(e={}),new Promise((function(n){var r=t.signInUserSession.getRefreshToken();r&&r.getToken()?t.refreshSession(r,n,e.clientMetadata):n()}))},t.getUserData=function(e,t){var n=this;if(null==this.signInUserSession||!this.signInUserSession.isValid())return this.clearCachedUserData(),e(new Error("User is not authenticated"),null);var r=this.getUserDataFromCache();if(r)if(this.isFetchUserDataAndTokenRequired(t))this.fetchUserData().then((function(e){return n.refreshSessionIfPossible(t).then((function(){return e}))})).then((function(t){return e(null,t)})).catch(e);else try{return void e(null,JSON.parse(r))}catch(t){return this.clearCachedUserData(),void e(t,null)}else this.fetchUserData().then((function(t){e(null,t)})).catch(e)},t.getUserDataFromCache=function(){return this.storage.getItem(this.userDataKey)},t.isFetchUserDataAndTokenRequired=function(e){var t=(e||{}).bypassCache;return void 0!==t&&t},t.fetchUserData=function(){var e=this;return this.createGetUserRequest().then((function(t){return e.cacheUserData(t),t}))},t.deleteAttributes=function(e,t){if(null==this.signInUserSession||!this.signInUserSession.isValid())return t(new Error("User is not authenticated"),null);this.client.request("DeleteUserAttributes",{UserAttributeNames:e,AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(e){return e?t(e,null):t(null,"SUCCESS")}))},t.resendConfirmationCode=function(e,t){var n={ClientId:this.pool.getClientId(),Username:this.username,ClientMetadata:t};this.client.request("ResendConfirmationCode",n,(function(t,n){return t?e(t,null):e(null,n)}))},t.getSession=function(e,t){if(void 0===t&&(t={}),null==this.username)return e(new Error("Username is null. Cannot retrieve a new session"),null);if(null!=this.signInUserSession&&this.signInUserSession.isValid())return e(null,this.signInUserSession);var n="CognitoIdentityServiceProvider."+this.pool.getClientId()+"."+this.username,r=n+".idToken",i=n+".accessToken",o=n+".refreshToken",s=n+".clockDrift";if(this.storage.getItem(r)){var a=new P({IdToken:this.storage.getItem(r)}),u=new C({AccessToken:this.storage.getItem(i)}),c=new N({RefreshToken:this.storage.getItem(o)}),f=parseInt(this.storage.getItem(s),0)||0,l=new j({IdToken:a,AccessToken:u,RefreshToken:c,ClockDrift:f});if(l.isValid())return this.signInUserSession=l,e(null,this.signInUserSession);if(!c.getToken())return e(new Error("Cannot retrieve a new session. Please authenticate."),null);this.refreshSession(c,e,t.clientMetadata)}else e(new Error("Local storage is missing an ID Token, Please authenticate"),null)},t.refreshSession=function(e,t,n){var r=this,i=this.pool.wrapRefreshSessionCallback?this.pool.wrapRefreshSessionCallback(t):t,o={};o.REFRESH_TOKEN=e.getToken();var s="CognitoIdentityServiceProvider."+this.pool.getClientId(),a=s+".LastAuthUser";if(this.storage.getItem(a)){this.username=this.storage.getItem(a);var u=s+"."+this.username+".deviceKey";this.deviceKey=this.storage.getItem(u),o.DEVICE_KEY=this.deviceKey}var c={ClientId:this.pool.getClientId(),AuthFlow:"REFRESH_TOKEN_AUTH",AuthParameters:o,ClientMetadata:n};this.getUserContextData()&&(c.UserContextData=this.getUserContextData()),this.client.request("InitiateAuth",c,(function(t,n){if(t)return"NotAuthorizedException"===t.code&&r.clearCachedUser(),i(t,null);if(n){var o=n.AuthenticationResult;return Object.prototype.hasOwnProperty.call(o,"RefreshToken")||(o.RefreshToken=e.getToken()),r.signInUserSession=r.getCognitoUserSession(o),r.cacheTokens(),i(null,r.signInUserSession)}}))},t.cacheTokens=function(){var e="CognitoIdentityServiceProvider."+this.pool.getClientId(),t=e+"."+this.username+".idToken",n=e+"."+this.username+".accessToken",r=e+"."+this.username+".refreshToken",i=e+"."+this.username+".clockDrift",o=e+".LastAuthUser";this.storage.setItem(t,this.signInUserSession.getIdToken().getJwtToken()),this.storage.setItem(n,this.signInUserSession.getAccessToken().getJwtToken()),this.storage.setItem(r,this.signInUserSession.getRefreshToken().getToken()),this.storage.setItem(i,""+this.signInUserSession.getClockDrift()),this.storage.setItem(o,this.username)},t.cacheUserData=function(e){this.storage.setItem(this.userDataKey,JSON.stringify(e))},t.clearCachedUserData=function(){this.storage.removeItem(this.userDataKey)},t.clearCachedUser=function(){this.clearCachedTokens(),this.clearCachedUserData()},t.cacheDeviceKeyAndPassword=function(){var e="CognitoIdentityServiceProvider."+this.pool.getClientId()+"."+this.username,t=e+".deviceKey",n=e+".randomPasswordKey",r=e+".deviceGroupKey";this.storage.setItem(t,this.deviceKey),this.storage.setItem(n,this.randomPassword),this.storage.setItem(r,this.deviceGroupKey)},t.getCachedDeviceKeyAndPassword=function(){var e="CognitoIdentityServiceProvider."+this.pool.getClientId()+"."+this.username,t=e+".deviceKey",n=e+".randomPasswordKey",r=e+".deviceGroupKey";this.storage.getItem(t)&&(this.deviceKey=this.storage.getItem(t),this.randomPassword=this.storage.getItem(n),this.deviceGroupKey=this.storage.getItem(r))},t.clearCachedDeviceKeyAndPassword=function(){var e="CognitoIdentityServiceProvider."+this.pool.getClientId()+"."+this.username,t=e+".deviceKey",n=e+".randomPasswordKey",r=e+".deviceGroupKey";this.storage.removeItem(t),this.storage.removeItem(n),this.storage.removeItem(r)},t.clearCachedTokens=function(){var e="CognitoIdentityServiceProvider."+this.pool.getClientId(),t=e+"."+this.username+".idToken",n=e+"."+this.username+".accessToken",r=e+"."+this.username+".refreshToken",i=e+".LastAuthUser",o=e+"."+this.username+".clockDrift";this.storage.removeItem(t),this.storage.removeItem(n),this.storage.removeItem(r),this.storage.removeItem(i),this.storage.removeItem(o)},t.getCognitoUserSession=function(e){var t=new P(e),n=new C(e),r=new N(e);return new j({IdToken:t,AccessToken:n,RefreshToken:r})},t.forgotPassword=function(e,t){var n={ClientId:this.pool.getClientId(),Username:this.username,ClientMetadata:t};this.getUserContextData()&&(n.UserContextData=this.getUserContextData()),this.client.request("ForgotPassword",n,(function(t,n){return t?e.onFailure(t):"function"==typeof e.inputVerificationCode?e.inputVerificationCode(n):e.onSuccess(n)}))},t.confirmPassword=function(e,t,n,r){var i={ClientId:this.pool.getClientId(),Username:this.username,ConfirmationCode:e,Password:t,ClientMetadata:r};this.getUserContextData()&&(i.UserContextData=this.getUserContextData()),this.client.request("ConfirmForgotPassword",i,(function(e){return e?n.onFailure(e):n.onSuccess()}))},t.getAttributeVerificationCode=function(e,t,n){if(null==this.signInUserSession||!this.signInUserSession.isValid())return t.onFailure(new Error("User is not authenticated"));this.client.request("GetUserAttributeVerificationCode",{AttributeName:e,AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),ClientMetadata:n},(function(e,n){return e?t.onFailure(e):"function"==typeof t.inputVerificationCode?t.inputVerificationCode(n):t.onSuccess()}))},t.verifyAttribute=function(e,t,n){if(null==this.signInUserSession||!this.signInUserSession.isValid())return n.onFailure(new Error("User is not authenticated"));this.client.request("VerifyUserAttribute",{AttributeName:e,Code:t,AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(e){return e?n.onFailure(e):n.onSuccess("SUCCESS")}))},t.getDevice=function(e){if(null==this.signInUserSession||!this.signInUserSession.isValid())return e.onFailure(new Error("User is not authenticated"));this.client.request("GetDevice",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),DeviceKey:this.deviceKey},(function(t,n){return t?e.onFailure(t):e.onSuccess(n)}))},t.forgetSpecificDevice=function(e,t){if(null==this.signInUserSession||!this.signInUserSession.isValid())return t.onFailure(new Error("User is not authenticated"));this.client.request("ForgetDevice",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),DeviceKey:e},(function(e){return e?t.onFailure(e):t.onSuccess("SUCCESS")}))},t.forgetDevice=function(e){var t=this;this.forgetSpecificDevice(this.deviceKey,{onFailure:e.onFailure,onSuccess:function(n){return t.deviceKey=null,t.deviceGroupKey=null,t.randomPassword=null,t.clearCachedDeviceKeyAndPassword(),e.onSuccess(n)}})},t.setDeviceStatusRemembered=function(e){if(null==this.signInUserSession||!this.signInUserSession.isValid())return e.onFailure(new Error("User is not authenticated"));this.client.request("UpdateDeviceStatus",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),DeviceKey:this.deviceKey,DeviceRememberedStatus:"remembered"},(function(t){return t?e.onFailure(t):e.onSuccess("SUCCESS")}))},t.setDeviceStatusNotRemembered=function(e){if(null==this.signInUserSession||!this.signInUserSession.isValid())return e.onFailure(new Error("User is not authenticated"));this.client.request("UpdateDeviceStatus",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),DeviceKey:this.deviceKey,DeviceRememberedStatus:"not_remembered"},(function(t){return t?e.onFailure(t):e.onSuccess("SUCCESS")}))},t.listDevices=function(e,t,n){if(null==this.signInUserSession||!this.signInUserSession.isValid())return n.onFailure(new Error("User is not authenticated"));var r={AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),Limit:e};t&&(r.PaginationToken=t),this.client.request("ListDevices",r,(function(e,t){return e?n.onFailure(e):n.onSuccess(t)}))},t.globalSignOut=function(e){var t=this;if(null==this.signInUserSession||!this.signInUserSession.isValid())return e.onFailure(new Error("User is not authenticated"));this.client.request("GlobalSignOut",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(n){return n?e.onFailure(n):(t.clearCachedUser(),e.onSuccess("SUCCESS"))}))},t.signOut=function(){this.signInUserSession=null,this.clearCachedUser()},t.sendMFASelectionAnswer=function(e,t){var n=this,r={};r.USERNAME=this.username,r.ANSWER=e;var i={ChallengeName:"SELECT_MFA_TYPE",ChallengeResponses:r,ClientId:this.pool.getClientId(),Session:this.Session};this.getUserContextData()&&(i.UserContextData=this.getUserContextData()),this.client.request("RespondToAuthChallenge",i,(function(r,i){return r?t.onFailure(r):(n.Session=i.Session,"SMS_MFA"===e?t.mfaRequired(i.ChallengeName,i.ChallengeParameters):"SOFTWARE_TOKEN_MFA"===e?t.totpRequired(i.ChallengeName,i.ChallengeParameters):void 0)}))},t.getUserContextData=function(){return this.pool.getUserContextData(this.username)},t.associateSoftwareToken=function(e){var t=this;null!=this.signInUserSession&&this.signInUserSession.isValid()?this.client.request("AssociateSoftwareToken",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken()},(function(t,n){return t?e.onFailure(t):e.associateSecretCode(n.SecretCode)})):this.client.request("AssociateSoftwareToken",{Session:this.Session},(function(n,r){return n?e.onFailure(n):(t.Session=r.Session,e.associateSecretCode(r.SecretCode))}))},t.verifySoftwareToken=function(e,t,n){var r=this;null!=this.signInUserSession&&this.signInUserSession.isValid()?this.client.request("VerifySoftwareToken",{AccessToken:this.signInUserSession.getAccessToken().getJwtToken(),UserCode:e,FriendlyDeviceName:t},(function(e,t){return e?n.onFailure(e):n.onSuccess(t)})):this.client.request("VerifySoftwareToken",{Session:this.Session,UserCode:e,FriendlyDeviceName:t},(function(e,t){if(e)return n.onFailure(e);r.Session=t.Session;var i={};i.USERNAME=r.username;var o={ChallengeName:"MFA_SETUP",ClientId:r.pool.getClientId(),ChallengeResponses:i,Session:r.Session};r.getUserContextData()&&(o.UserContextData=r.getUserContextData()),r.client.request("RespondToAuthChallenge",o,(function(e,t){return e?n.onFailure(e):(r.signInUserSession=r.getCognitoUserSession(t.AuthenticationResult),r.cacheTokens(),n.onSuccess(r.signInUserSession))}))}))},e}(); -/*! - * Copyright 2016 Amazon.com, - * Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the - * License. A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, express or implied. See the License - * for the specific language governing permissions and - * limitations under the License. - */n(365);function G(){}G.prototype.userAgent="aws-amplify/0.1.x js";var W=G;function $(e){var t="function"==typeof Map?new Map:void 0;return($=function(e){if(null===e||(n=e,-1===Function.toString.call(n).indexOf("[native code]")))return e;var n;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return Y(e,arguments,X(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),Z(r,e)})(e)}function Y(e,t,n){return(Y=J()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&Z(i,n.prototype),i}).apply(null,arguments)}function J(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}function Z(e,t){return(Z=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function X(e){return(X=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}var Q=function(e){var t,n;function r(t,n,r,i){var o;return(o=e.call(this,t)||this).code=n,o.name=r,o.statusCode=i,o}return n=e,(t=r).prototype=Object.create(n.prototype),t.prototype.constructor=t,Z(t,n),r}($(Error)),ee=function(){function e(e,t,n){this.endpoint=t||"https://cognito-idp."+e+".amazonaws.com/";var r=(n||{}).credentials;this.fetchOptions=r?{credentials:r}:{}}var t=e.prototype;return t.promisifyRequest=function(e,t){var n=this;return new Promise((function(r,i){n.request(e,t,(function(e,t){e?i(new Q(e.message,e.code,e.name,e.statusCode)):r(t)}))}))},t.request=function(e,t,n){var r,i={"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AWSCognitoIdentityProviderService."+e,"X-Amz-User-Agent":W.prototype.userAgent},o=Object.assign({},this.fetchOptions,{headers:i,method:"POST",mode:"cors",cache:"no-cache",body:JSON.stringify(t)});fetch(this.endpoint,o).then((function(e){return r=e,e}),(function(e){if(e instanceof TypeError)throw new Error("Network error");throw e})).then((function(e){return e.json().catch((function(){return{}}))})).then((function(e){if(r.ok)return n(null,e);e;var t=(e.__type||e.code).split("#").pop(),i={code:t,name:t,message:e.message||e.Message||null};return n(i)})).catch((function(e){if(!(r&&r.headers&&r.headers.get("x-amzn-errortype"))){if(e instanceof Error&&"Network error"===e.message){var t={code:"NetworkError",name:e.name,message:e.message};return n(t)}return n(e)}try{var i=r.headers.get("x-amzn-errortype").split(":")[0],o={code:i,name:i,statusCode:r.status,message:r.status?r.status.toString():null};return n(o)}catch(t){return n(e)}}))},e}(),te=function(){function e(e,t){var n=e||{},r=n.UserPoolId,i=n.ClientId,o=n.endpoint,s=n.fetchOptions,a=n.AdvancedSecurityDataCollectionFlag;if(!r||!i)throw new Error("Both UserPoolId and ClientId are required.");if(!/^[\w-]+_.+$/.test(r))throw new Error("Invalid UserPoolId format.");var u=r.split("_")[0];this.userPoolId=r,this.clientId=i,this.client=new ee(u,o,s),this.advancedSecurityDataCollectionFlag=!1!==a,this.storage=e.Storage||(new K).getStorage(),t&&(this.wrapRefreshSessionCallback=t)}var t=e.prototype;return t.getUserPoolId=function(){return this.userPoolId},t.getClientId=function(){return this.clientId},t.signUp=function(e,t,n,r,i,o){var s=this,a={ClientId:this.clientId,Username:e,Password:t,UserAttributes:n,ValidationData:r,ClientMetadata:o};this.getUserContextData(e)&&(a.UserContextData=this.getUserContextData(e)),this.client.request("SignUp",a,(function(t,n){if(t)return i(t,null);var r={Username:e,Pool:s,Storage:s.storage},o={user:new V(r),userConfirmed:n.UserConfirmed,userSub:n.UserSub,codeDeliveryDetails:n.CodeDeliveryDetails};return i(null,o)}))},t.getCurrentUser=function(){var e="CognitoIdentityServiceProvider."+this.clientId+".LastAuthUser",t=this.storage.getItem(e);if(t){var n={Username:t,Pool:this,Storage:this.storage};return new V(n)}return null},t.getUserContextData=function(e){if("undefined"!=typeof AmazonCognitoAdvancedSecurityData){var t=AmazonCognitoAdvancedSecurityData;if(this.advancedSecurityDataCollectionFlag){var n=t.getData(e,this.userPoolId,this.clientId);if(n)return{EncodedData:n}}return{}}},e}(),ne=n(65),re=function(){function e(e){if(!e.domain)throw new Error("The domain of cookieStorage can not be undefined.");if(this.domain=e.domain,e.path?this.path=e.path:this.path="/",Object.prototype.hasOwnProperty.call(e,"expires")?this.expires=e.expires:this.expires=365,Object.prototype.hasOwnProperty.call(e,"secure")?this.secure=e.secure:this.secure=!0,Object.prototype.hasOwnProperty.call(e,"sameSite")){if(!["strict","lax","none"].includes(e.sameSite))throw new Error('The sameSite value of cookieStorage must be "lax", "strict" or "none".');if("none"===e.sameSite&&!this.secure)throw new Error("sameSite = None requires the Secure attribute in latest browser versions.");this.sameSite=e.sameSite}else this.sameSite=null}var t=e.prototype;return t.setItem=function(e,t){var n={path:this.path,expires:this.expires,domain:this.domain,secure:this.secure};return this.sameSite&&(n.sameSite=this.sameSite),ne.set(e,t,n),ne.get(e)},t.getItem=function(e){return ne.get(e)},t.removeItem=function(e){var t={path:this.path,expires:this.expires,domain:this.domain,secure:this.secure};return this.sameSite&&(t.sameSite=this.sameSite),ne.remove(e,t)},t.clear=function(){var e,t=ne.get();for(e=0;e<t.length;++e)ne.remove(t[e]);return{}},e}()},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){var r;e.exports=(r=r||function(e,t){var n=Object.create||function(){function e(){}return function(t){var n;return e.prototype=t,n=new e,e.prototype=null,n}}(),r={},i=r.lib={},o=i.Base={extend:function(e){var t=n(this);return e&&t.mixIn(e),t.hasOwnProperty("init")&&this.init!==t.init||(t.init=function(){t.$super.init.apply(this,arguments)}),t.init.prototype=t,t.$super=this,t},create:function(){var e=this.extend();return e.init.apply(e,arguments),e},init:function(){},mixIn:function(e){for(var t in e)e.hasOwnProperty(t)&&(this[t]=e[t]);e.hasOwnProperty("toString")&&(this.toString=e.toString)},clone:function(){return this.init.prototype.extend(this)}},s=i.WordArray=o.extend({init:function(e,t){e=this.words=e||[],this.sigBytes=null!=t?t:4*e.length},toString:function(e){return(e||u).stringify(this)},concat:function(e){var t=this.words,n=e.words,r=this.sigBytes,i=e.sigBytes;if(this.clamp(),r%4)for(var o=0;o<i;o++){var s=n[o>>>2]>>>24-o%4*8&255;t[r+o>>>2]|=s<<24-(r+o)%4*8}else for(o=0;o<i;o+=4)t[r+o>>>2]=n[o>>>2];return this.sigBytes+=i,this},clamp:function(){var t=this.words,n=this.sigBytes;t[n>>>2]&=4294967295<<32-n%4*8,t.length=e.ceil(n/4)},clone:function(){var e=o.clone.call(this);return e.words=this.words.slice(0),e},random:function(t){for(var n,r=[],i=function(t){t=t;var n=987654321,r=4294967295;return function(){var i=((n=36969*(65535&n)+(n>>16)&r)<<16)+(t=18e3*(65535&t)+(t>>16)&r)&r;return i/=4294967296,(i+=.5)*(e.random()>.5?1:-1)}},o=0;o<t;o+=4){var a=i(4294967296*(n||e.random()));n=987654071*a(),r.push(4294967296*a()|0)}return new s.init(r,t)}}),a=r.enc={},u=a.Hex={stringify:function(e){for(var t=e.words,n=e.sigBytes,r=[],i=0;i<n;i++){var o=t[i>>>2]>>>24-i%4*8&255;r.push((o>>>4).toString(16)),r.push((15&o).toString(16))}return r.join("")},parse:function(e){for(var t=e.length,n=[],r=0;r<t;r+=2)n[r>>>3]|=parseInt(e.substr(r,2),16)<<24-r%8*4;return new s.init(n,t/2)}},c=a.Latin1={stringify:function(e){for(var t=e.words,n=e.sigBytes,r=[],i=0;i<n;i++){var o=t[i>>>2]>>>24-i%4*8&255;r.push(String.fromCharCode(o))}return r.join("")},parse:function(e){for(var t=e.length,n=[],r=0;r<t;r++)n[r>>>2]|=(255&e.charCodeAt(r))<<24-r%4*8;return new s.init(n,t)}},f=a.Utf8={stringify:function(e){try{return decodeURIComponent(escape(c.stringify(e)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(e){return c.parse(unescape(encodeURIComponent(e)))}},l=i.BufferedBlockAlgorithm=o.extend({reset:function(){this._data=new s.init,this._nDataBytes=0},_append:function(e){"string"==typeof e&&(e=f.parse(e)),this._data.concat(e),this._nDataBytes+=e.sigBytes},_process:function(t){var n=this._data,r=n.words,i=n.sigBytes,o=this.blockSize,a=i/(4*o),u=(a=t?e.ceil(a):e.max((0|a)-this._minBufferSize,0))*o,c=e.min(4*u,i);if(u){for(var f=0;f<u;f+=o)this._doProcessBlock(r,f);var l=r.splice(0,u);n.sigBytes-=c}return new s.init(l,c)},clone:function(){var e=o.clone.call(this);return e._data=this._data.clone(),e},_minBufferSize:0}),d=(i.Hasher=l.extend({cfg:o.extend(),init:function(e){this.cfg=this.cfg.extend(e),this.reset()},reset:function(){l.reset.call(this),this._doReset()},update:function(e){return this._append(e),this._process(),this},finalize:function(e){return e&&this._append(e),this._doFinalize()},blockSize:16,_createHelper:function(e){return function(t,n){return new e.init(n).finalize(t)}},_createHmacHelper:function(e){return function(t,n){return new d.HMAC.init(e,n).finalize(t)}}}),r.algo={});return r}(Math),r)},function(e,t,n){"use strict";(function(e){n.d(t,"d",(function(){return f})),n.d(t,"c",(function(){return l})),n.d(t,"b",(function(){return d})),n.d(t,"a",(function(){return g}));var r=[{type:"text/plain",ext:"txt"},{type:"text/html",ext:"html"},{type:"text/javascript",ext:"js"},{type:"text/css",ext:"css"},{type:"text/csv",ext:"csv"},{type:"text/yaml",ext:"yml"},{type:"text/yaml",ext:"yaml"},{type:"text/calendar",ext:"ics"},{type:"text/calendar",ext:"ical"},{type:"image/apng",ext:"apng"},{type:"image/bmp",ext:"bmp"},{type:"image/gif",ext:"gif"},{type:"image/x-icon",ext:"ico"},{type:"image/x-icon",ext:"cur"},{type:"image/jpeg",ext:"jpg"},{type:"image/jpeg",ext:"jpeg"},{type:"image/jpeg",ext:"jfif"},{type:"image/jpeg",ext:"pjp"},{type:"image/jpeg",ext:"pjpeg"},{type:"image/png",ext:"png"},{type:"image/svg+xml",ext:"svg"},{type:"image/tiff",ext:"tif"},{type:"image/tiff",ext:"tiff"},{type:"image/webp",ext:"webp"},{type:"application/json",ext:"json"},{type:"application/xml",ext:"xml"},{type:"application/x-sh",ext:"sh"},{type:"application/zip",ext:"zip"},{type:"application/x-rar-compressed",ext:"rar"},{type:"application/x-tar",ext:"tar"},{type:"application/x-bzip",ext:"bz"},{type:"application/x-bzip2",ext:"bz2"},{type:"application/pdf",ext:"pdf"},{type:"application/java-archive",ext:"jar"},{type:"application/msword",ext:"doc"},{type:"application/vnd.ms-excel",ext:"xls"},{type:"application/vnd.ms-excel",ext:"xlsx"},{type:"message/rfc822",ext:"eml"}],i=function(e){return void 0===e&&(e={}),0===Object.keys(e).length},o=function(e,t,n){if(!e||!e.sort)return!1;var r=n&&"desc"===n?-1:1;return e.sort((function(e,n){var i=e[t],o=n[t];return void 0===o?void 0===i?0:1*r:void 0===i||i<o?-1*r:i>o?1*r:0})),!0},s=function(e,t){var n=Object.assign({},e);return t&&("string"==typeof t?delete n[t]:t.forEach((function(e){delete n[e]}))),n},a=function(e,t){void 0===t&&(t="application/octet-stream");var n=e.toLowerCase(),i=r.filter((function(e){return n.endsWith("."+e.ext)}));return i.length>0?i[0].type:t},u=function(e){var t=e.toLowerCase();return!!t.startsWith("text/")||("application/json"===t||"application/xml"===t||"application/sh"===t)},c=function(){for(var e="",t="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",n=32;n>0;n-=1)e+=t[Math.floor(Math.random()*t.length)];return e},f=function(e){if(e.isResolved)return e;var t=!0,n=!1,r=!1,i=e.then((function(e){return r=!0,t=!1,e}),(function(e){throw n=!0,t=!1,e}));return i.isFullfilled=function(){return r},i.isPending=function(){return t},i.isRejected=function(){return n},i},l=function(){if("undefined"==typeof self)return!1;var e=self;return void 0!==e.WorkerGlobalScope&&self instanceof e.WorkerGlobalScope},d=function(){return{isBrowser:"undefined"!=typeof window&&void 0!==window.document,isNode:void 0!==e&&null!=e.versions&&null!=e.versions.node}},h=function e(t,n,r){if(void 0===n&&(n=[]),void 0===r&&(r=[]),!v(t))return t;var i={};for(var o in t){if(t.hasOwnProperty(o))i[n.includes(o)?o:o[0].toLowerCase()+o.slice(1)]=r.includes(o)?t[o]:e(t[o],n,r)}return i},p=function e(t,n,r){if(void 0===n&&(n=[]),void 0===r&&(r=[]),!v(t))return t;var i={};for(var o in t){if(t.hasOwnProperty(o))i[n.includes(o)?o:o[0].toUpperCase()+o.slice(1)]=r.includes(o)?t[o]:e(t[o],n,r)}return i},v=function(e){return!(!(e instanceof Object)||e instanceof Array||e instanceof Function||e instanceof Number||e instanceof String||e instanceof Boolean)},g=function(){function e(){}return e.isEmpty=i,e.sortByField=o,e.objectLessAttributes=s,e.filenameToContentType=a,e.isTextFile=u,e.generateRandomString=c,e.makeQuerablePromise=f,e.isWebWorker=l,e.browserOrNode=d,e.transferKeyToLowerCase=h,e.transferKeyToUpperCase=p,e.isStrictObject=v,e}()}).call(this,n(20))},function(e,t,n){"use strict";n.d(t,"a",(function(){return r}));var r,i=n(105);!function(e){e.CONNECTION_CLOSED="Connection closed",e.TIMEOUT_DISCONNECT="Timeout disconnect",e.SUBSCRIPTION_ACK="Subscription ack"}(r||(r={})),t.b=i.a},function(e,t,n){"use strict";var r;n.d(t,"a",(function(){return r})),function(e){e.DEFAULT_MSG="Authentication Error",e.EMPTY_USERNAME="Username cannot be empty",e.INVALID_USERNAME="The username should either be a string or one of the sign in types",e.EMPTY_PASSWORD="Password cannot be empty",e.EMPTY_CODE="Confirmation code cannot be empty",e.SIGN_UP_ERROR="Error creating account",e.NO_MFA="No valid MFA method provided",e.INVALID_MFA="Invalid MFA type",e.EMPTY_CHALLENGE="Challenge response cannot be empty",e.NO_USER_SESSION="Failed to get the session because the user is empty"}(r||(r={}))},function(e,t,n){var r=n(228),i=n(230),o=n(231),s=n(61),a=n(232),u=n(138),c=n(229),f=n(139),l=Object.prototype.hasOwnProperty;e.exports=function(e){if(null==e)return!0;if(a(e)&&(s(e)||"string"==typeof e||"function"==typeof e.splice||u(e)||f(e)||o(e)))return!e.length;var t=i(e);if("[object Map]"==t||"[object Set]"==t)return!e.size;if(c(e))return!r(e).length;for(var n in e)if(l.call(e,n))return!1;return!0}},function(e,t,n){"use strict";n.d(t,"a",(function(){return s}));var r=n(1),i=n(2);var o={step:"build",tags:["SET_CONTENT_LENGTH","CONTENT_LENGTH"],name:"contentLengthMiddleware"},s=function(e){return{applyToStack:function(t){t.add(function(e){var t=this;return function(n){return function(o){return Object(r.__awaiter)(t,void 0,void 0,(function(){var t,s,a,u,c;return Object(r.__generator)(this,(function(f){return t=o.request,i.a.isInstance(t)&&(s=t.body,a=t.headers,s&&-1===Object.keys(a).map((function(e){return e.toLowerCase()})).indexOf("content-length")&&void 0!==(u=e(s))&&(t.headers=Object(r.__assign)(Object(r.__assign)({},t.headers),((c={})["content-length"]=String(u),c)))),[2,n(Object(r.__assign)(Object(r.__assign)({},o),{request:t}))]}))}))}}}(e.bodyLengthChecker),o)}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.WebCryptoSha256=t.Ie11Sha256=void 0,n(1).__exportStar(n(373),t);var r=n(216);Object.defineProperty(t,"Ie11Sha256",{enumerable:!0,get:function(){return r.Sha256}});var i=n(219);Object.defineProperty(t,"WebCryptoSha256",{enumerable:!0,get:function(){return i.Sha256}})},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(106),i=function(e){var t,n=new URL(e),i=n.hostname,o=n.pathname,s=n.port,a=n.protocol,u=n.search;return u&&(t=Object(r.a)(u)),{hostname:i,port:s?parseInt(s):void 0,protocol:a,path:o,query:t}}},function(e,t,n){"use strict";function r(e){if("string"==typeof e){for(var t=e.length,n=t-1;n>=0;n--){var r=e.charCodeAt(n);r>127&&r<=2047?t++:r>2047&&r<=65535&&(t+=2)}return t}return"number"==typeof e.byteLength?e.byteLength:"number"==typeof e.size?e.size:void 0}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";function r(e,t){return"aws-sdk-js-v3-"+e+"/"+t+" "+("undefined"!=typeof navigator&&"string"==typeof navigator.userAgent?navigator.userAgent:"")}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";var r=n(63);n(30);t.a=r.a},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(1),i={name:"loggerMiddleware",tags:["LOGGER"],step:"initialize"},o=function(e){return{applyToStack:function(e){e.add((function(e,t){return function(n){return Object(r.__awaiter)(void 0,void 0,void 0,(function(){var i,o,s,a;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return i=t.logger,[4,e(n)];case 1:return o=r.sent(),i?(s=o.response,"function"==typeof i.info&&i.info({metadata:{statusCode:s.statusCode,requestId:null!==(a=s.headers["x-amzn-requestid"])&&void 0!==a?a:s.headers["x-amzn-request-id"],extendedRequestId:s.headers["x-amz-id-2"],cfId:s.headers["x-amz-cf-id"]}}),[2,o]):[2,o]}}))}))}}),i)}}}},function(e,t,n){"use strict";n.d(t,"a",(function(){return s}));var r=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},i=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(r(arguments[t]));return e},o={VERBOSE:1,DEBUG:2,INFO:3,WARN:4,ERROR:5},s=function(){function e(e,t){void 0===t&&(t="WARN"),this.name=e,this.level=t}return e.prototype._padding=function(e){return e<10?"0"+e:""+e},e.prototype._ts=function(){var e=new Date;return[this._padding(e.getMinutes()),this._padding(e.getSeconds())].join(":")+"."+e.getMilliseconds()},e.prototype._log=function(t){for(var n=[],r=1;r<arguments.length;r++)n[r-1]=arguments[r];var i=this.level;e.LOG_LEVEL&&(i=e.LOG_LEVEL),"undefined"!=typeof window&&window.LOG_LEVEL&&(i=window.LOG_LEVEL);var s=o[i],a=o[t];if(a>=s){var u=console.log.bind(console);"ERROR"===t&&console.error&&(u=console.error.bind(console)),"WARN"===t&&console.warn&&(u=console.warn.bind(console));var c="["+t+"] "+this._ts()+" "+this.name;if(1===n.length&&"string"==typeof n[0])u(c+" - "+n[0]);else if(1===n.length)u(c,n[0]);else if("string"==typeof n[0]){var f=n.slice(1);1===f.length&&(f=f[0]),u(c+" - "+n[0],f)}else u(c,n)}},e.prototype.log=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._log.apply(this,i(["INFO"],e))},e.prototype.info=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._log.apply(this,i(["INFO"],e))},e.prototype.warn=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._log.apply(this,i(["WARN"],e))},e.prototype.error=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._log.apply(this,i(["ERROR"],e))},e.prototype.debug=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._log.apply(this,i(["DEBUG"],e))},e.prototype.verbose=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._log.apply(this,i(["VERBOSE"],e))},e.LOG_LEVEL=null,e}()},function(e,t,n){"use strict";var r=n(235),i=Object.prototype.toString;function o(e){return"[object Array]"===i.call(e)}function s(e){return void 0===e}function a(e){return null!==e&&"object"==typeof e}function u(e){if("[object Object]"!==i.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function c(e){return"[object Function]"===i.call(e)}function f(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),o(e))for(var n=0,r=e.length;n<r;n++)t.call(null,e[n],n,e);else for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&t.call(null,e[i],i,e)}e.exports={isArray:o,isArrayBuffer:function(e){return"[object ArrayBuffer]"===i.call(e)},isBuffer:function(e){return null!==e&&!s(e)&&null!==e.constructor&&!s(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)},isFormData:function(e){return"undefined"!=typeof FormData&&e instanceof FormData},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer},isString:function(e){return"string"==typeof e},isNumber:function(e){return"number"==typeof e},isObject:a,isPlainObject:u,isUndefined:s,isDate:function(e){return"[object Date]"===i.call(e)},isFile:function(e){return"[object File]"===i.call(e)},isBlob:function(e){return"[object Blob]"===i.call(e)},isFunction:c,isStream:function(e){return a(e)&&c(e.pipe)},isURLSearchParams:function(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams},isStandardBrowserEnv:function(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)},forEach:f,merge:function e(){var t={};function n(n,r){u(t[r])&&u(n)?t[r]=e(t[r],n):u(n)?t[r]=e({},n):o(n)?t[r]=n.slice():t[r]=n}for(var r=0,i=arguments.length;r<i;r++)f(arguments[r],n);return t},extend:function(e,t,n){return f(t,(function(t,i){e[i]=n&&"function"==typeof t?r(t,n):t})),e},trim:function(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}}},function(e,t){function n(e,t){if(!e)throw new Error(t||"Assertion failed")}e.exports=n,n.equal=function(e,t,n){if(e!=t)throw new Error(n||"Assertion failed: "+e+" != "+t)}},function(e,t,n){"use strict";var r=t,i=n(29),o=n(46),s=n(198);r.assert=o,r.toArray=s.toArray,r.zero2=s.zero2,r.toHex=s.toHex,r.encode=s.encode,r.getNAF=function(e,t,n){var r=new Array(Math.max(e.bitLength(),n)+1);r.fill(0);for(var i=1<<t+1,o=e.clone(),s=0;s<r.length;s++){var a,u=o.andln(i-1);o.isOdd()?(a=u>(i>>1)-1?(i>>1)-u:u,o.isubn(a)):a=0,r[s]=a,o.iushrn(1)}return r},r.getJSF=function(e,t){var n=[[],[]];e=e.clone(),t=t.clone();for(var r,i=0,o=0;e.cmpn(-i)>0||t.cmpn(-o)>0;){var s,a,u=e.andln(3)+i&3,c=t.andln(3)+o&3;3===u&&(u=-1),3===c&&(c=-1),s=0==(1&u)?0:3!==(r=e.andln(7)+i&7)&&5!==r||2!==c?u:-u,n[0].push(s),a=0==(1&c)?0:3!==(r=t.andln(7)+o&7)&&5!==r||2!==u?c:-c,n[1].push(a),2*i===s+1&&(i=1-i),2*o===a+1&&(o=1-o),e.iushrn(1),t.iushrn(1)}return n},r.cachedProperty=function(e,t,n){var r="_"+t;e.prototype[t]=function(){return void 0!==this[r]?this[r]:this[r]=n.call(this)}},r.parseBytes=function(e){return"string"==typeof e?r.toArray(e,"hex"):e},r.intFromLE=function(e){return new i(e,"hex","le")}},,function(e,t,n){"use strict";var r,i="object"==typeof Reflect?Reflect:null,o=i&&"function"==typeof i.apply?i.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};r=i&&"function"==typeof i.ownKeys?i.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var s=Number.isNaN||function(e){return e!=e};function a(){a.init.call(this)}e.exports=a,e.exports.once=function(e,t){return new Promise((function(n,r){function i(){void 0!==o&&e.removeListener("error",o),n([].slice.call(arguments))}var o;"error"!==t&&(o=function(n){e.removeListener(t,i),r(n)},e.once("error",o)),e.once(t,i)}))},a.EventEmitter=a,a.prototype._events=void 0,a.prototype._eventsCount=0,a.prototype._maxListeners=void 0;var u=10;function c(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function f(e){return void 0===e._maxListeners?a.defaultMaxListeners:e._maxListeners}function l(e,t,n,r){var i,o,s,a;if(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.listener:n),o=e._events),s=o[t]),void 0===s)s=o[t]=n,++e._eventsCount;else if("function"==typeof s?s=o[t]=r?[n,s]:[s,n]:r?s.unshift(n):s.push(n),(i=f(e))>0&&s.length>i&&!s.warned){s.warned=!0;var u=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");u.name="MaxListenersExceededWarning",u.emitter=e,u.type=t,u.count=s.length,a=u,console&&console.warn&&console.warn(a)}return e}function d(){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)}function h(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},i=d.bind(r);return i.listener=n,r.wrapFn=i,i}function p(e,t,n){var r=e._events;if(void 0===r)return[];var i=r[t];return void 0===i?[]:"function"==typeof i?n?[i.listener||i]:[i]:n?function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(i):g(i,i.length)}function v(e){var t=this._events;if(void 0!==t){var n=t[e];if("function"==typeof n)return 1;if(void 0!==n)return n.length}return 0}function g(e,t){for(var n=new Array(t),r=0;r<t;++r)n[r]=e[r];return n}Object.defineProperty(a,"defaultMaxListeners",{enumerable:!0,get:function(){return u},set:function(e){if("number"!=typeof e||e<0||s(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");u=e}}),a.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},a.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||s(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},a.prototype.getMaxListeners=function(){return f(this)},a.prototype.emit=function(e){for(var t=[],n=1;n<arguments.length;n++)t.push(arguments[n]);var r="error"===e,i=this._events;if(void 0!==i)r=r&&void 0===i.error;else if(!r)return!1;if(r){var s;if(t.length>0&&(s=t[0]),s instanceof Error)throw s;var a=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw a.context=s,a}var u=i[e];if(void 0===u)return!1;if("function"==typeof u)o(u,this,t);else{var c=u.length,f=g(u,c);for(n=0;n<c;++n)o(f[n],this,t)}return!0},a.prototype.addListener=function(e,t){return l(this,e,t,!1)},a.prototype.on=a.prototype.addListener,a.prototype.prependListener=function(e,t){return l(this,e,t,!0)},a.prototype.once=function(e,t){return c(t),this.on(e,h(this,e,t)),this},a.prototype.prependOnceListener=function(e,t){return c(t),this.prependListener(e,h(this,e,t)),this},a.prototype.removeListener=function(e,t){var n,r,i,o,s;if(c(t),void 0===(r=this._events))return this;if(void 0===(n=r[e]))return this;if(n===t||n.listener===t)0==--this._eventsCount?this._events=Object.create(null):(delete r[e],r.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(i=-1,o=n.length-1;o>=0;o--)if(n[o]===t||n[o].listener===t){s=n[o].listener,i=o;break}if(i<0)return this;0===i?n.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(n,i),1===n.length&&(r[e]=n[0]),void 0!==r.removeListener&&this.emit("removeListener",e,s||t)}return this},a.prototype.off=a.prototype.removeListener,a.prototype.removeAllListeners=function(e){var t,n,r;if(void 0===(n=this._events))return this;if(void 0===n.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==n[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete n[e]),this;if(0===arguments.length){var i,o=Object.keys(n);for(r=0;r<o.length;++r)"removeListener"!==(i=o[r])&&this.removeAllListeners(i);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(void 0!==t)for(r=t.length-1;r>=0;r--)this.removeListener(e,t[r]);return this},a.prototype.listeners=function(e){return p(this,e,!0)},a.prototype.rawListeners=function(e){return p(this,e,!1)},a.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):v.call(e,t)},a.prototype.listenerCount=v,a.prototype.eventNames=function(){return this._eventsCount>0?r(this._events):[]}},function(e,t,n){"use strict";n.d(t,"a",(function(){return r})),n.d(t,"b",(function(){return i}));var r={userAgent:"aws-amplify/3.8.12 js",product:"",navigator:null,isReactNative:!1};if("undefined"!=typeof navigator&&navigator.product)switch(r.product=navigator.product||"",r.navigator=navigator||null,navigator.product){case"ReactNative":r.userAgent="aws-amplify/3.8.12 react-native",r.isReactNative=!0;break;default:r.userAgent="aws-amplify/3.8.12 js",r.isReactNative=!1}var i=function(){return r.userAgent}},function(e,t,n){"use strict";var r=n(46),i=n(7);function o(e,t){return 55296==(64512&e.charCodeAt(t))&&(!(t<0||t+1>=e.length)&&56320==(64512&e.charCodeAt(t+1)))}function s(e){return(e>>>24|e>>>8&65280|e<<8&16711680|(255&e)<<24)>>>0}function a(e){return 1===e.length?"0"+e:e}function u(e){return 7===e.length?"0"+e:6===e.length?"00"+e:5===e.length?"000"+e:4===e.length?"0000"+e:3===e.length?"00000"+e:2===e.length?"000000"+e:1===e.length?"0000000"+e:e}t.inherits=i,t.toArray=function(e,t){if(Array.isArray(e))return e.slice();if(!e)return[];var n=[];if("string"==typeof e)if(t){if("hex"===t)for((e=e.replace(/[^a-z0-9]+/gi,"")).length%2!=0&&(e="0"+e),i=0;i<e.length;i+=2)n.push(parseInt(e[i]+e[i+1],16))}else for(var r=0,i=0;i<e.length;i++){var s=e.charCodeAt(i);s<128?n[r++]=s:s<2048?(n[r++]=s>>6|192,n[r++]=63&s|128):o(e,i)?(s=65536+((1023&s)<<10)+(1023&e.charCodeAt(++i)),n[r++]=s>>18|240,n[r++]=s>>12&63|128,n[r++]=s>>6&63|128,n[r++]=63&s|128):(n[r++]=s>>12|224,n[r++]=s>>6&63|128,n[r++]=63&s|128)}else for(i=0;i<e.length;i++)n[i]=0|e[i];return n},t.toHex=function(e){for(var t="",n=0;n<e.length;n++)t+=a(e[n].toString(16));return t},t.htonl=s,t.toHex32=function(e,t){for(var n="",r=0;r<e.length;r++){var i=e[r];"little"===t&&(i=s(i)),n+=u(i.toString(16))}return n},t.zero2=a,t.zero8=u,t.join32=function(e,t,n,i){var o=n-t;r(o%4==0);for(var s=new Array(o/4),a=0,u=t;a<s.length;a++,u+=4){var c;c="big"===i?e[u]<<24|e[u+1]<<16|e[u+2]<<8|e[u+3]:e[u+3]<<24|e[u+2]<<16|e[u+1]<<8|e[u],s[a]=c>>>0}return s},t.split32=function(e,t){for(var n=new Array(4*e.length),r=0,i=0;r<e.length;r++,i+=4){var o=e[r];"big"===t?(n[i]=o>>>24,n[i+1]=o>>>16&255,n[i+2]=o>>>8&255,n[i+3]=255&o):(n[i+3]=o>>>24,n[i+2]=o>>>16&255,n[i+1]=o>>>8&255,n[i]=255&o)}return n},t.rotr32=function(e,t){return e>>>t|e<<32-t},t.rotl32=function(e,t){return e<<t|e>>>32-t},t.sum32=function(e,t){return e+t>>>0},t.sum32_3=function(e,t,n){return e+t+n>>>0},t.sum32_4=function(e,t,n,r){return e+t+n+r>>>0},t.sum32_5=function(e,t,n,r,i){return e+t+n+r+i>>>0},t.sum64=function(e,t,n,r){var i=e[t],o=r+e[t+1]>>>0,s=(o<r?1:0)+n+i;e[t]=s>>>0,e[t+1]=o},t.sum64_hi=function(e,t,n,r){return(t+r>>>0<t?1:0)+e+n>>>0},t.sum64_lo=function(e,t,n,r){return t+r>>>0},t.sum64_4_hi=function(e,t,n,r,i,o,s,a){var u=0,c=t;return u+=(c=c+r>>>0)<t?1:0,u+=(c=c+o>>>0)<o?1:0,e+n+i+s+(u+=(c=c+a>>>0)<a?1:0)>>>0},t.sum64_4_lo=function(e,t,n,r,i,o,s,a){return t+r+o+a>>>0},t.sum64_5_hi=function(e,t,n,r,i,o,s,a,u,c){var f=0,l=t;return f+=(l=l+r>>>0)<t?1:0,f+=(l=l+o>>>0)<o?1:0,f+=(l=l+a>>>0)<a?1:0,e+n+i+s+u+(f+=(l=l+c>>>0)<c?1:0)>>>0},t.sum64_5_lo=function(e,t,n,r,i,o,s,a,u,c){return t+r+o+a+c>>>0},t.rotr64_hi=function(e,t,n){return(t<<32-n|e>>>n)>>>0},t.rotr64_lo=function(e,t,n){return(e<<32-n|t>>>n)>>>0},t.shr64_hi=function(e,t,n){return e>>>n},t.shr64_lo=function(e,t,n){return(e<<32-n|t>>>n)>>>0}},function(e,t,n){"use strict";var r=n(62);t.a=r.a},function(e,t,n){var r=n(223),i="object"==typeof self&&self&&self.Object===Object&&self,o=r||i||Function("return this")();e.exports=o},function(e,t,n){"use strict";const r=":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD",i="["+r+"][:A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*",o=new RegExp("^"+i+"$");t.isExist=function(e){return void 0!==e},t.isEmptyObject=function(e){return 0===Object.keys(e).length},t.merge=function(e,t,n){if(t){const r=Object.keys(t),i=r.length;for(let o=0;o<i;o++)e[r[o]]="strict"===n?[t[r[o]]]:t[r[o]]}},t.getValue=function(e){return t.isExist(e)?e:""},t.buildOptions=function(e,t,n){var r={};if(!e)return t;for(let i=0;i<n.length;i++)void 0!==e[n[i]]?r[n[i]]=e[n[i]]:r[n[i]]=t[n[i]];return r},t.isTagNameInArrayMode=function(e,t,n){return!1!==t&&(t instanceof RegExp?t.test(e):"function"==typeof t?!!t(e,n):"strict"===t)},t.isName=function(e){const t=o.exec(e);return!(null==t)},t.getAllMatches=function(e,t){const n=[];let r=t.exec(e);for(;r;){const i=[],o=r.length;for(let e=0;e<o;e++)i.push(r[e]);n.push(i),r=t.exec(e)}return n},t.nameRegexp=i},function(e,t,n){"use strict";n.d(t,"a",(function(){return r}));var r=function(e){return encodeURIComponent(e).replace(/[!'()*]/g,i)},i=function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}},function(e,t,n){var r=n(8).Buffer,i=n(285).Transform,o=n(59).StringDecoder;function s(e){i.call(this),this.hashMode="string"==typeof e,this.hashMode?this[e]=this._finalOrDigest:this.final=this._finalOrDigest,this._final&&(this.__final=this._final,this._final=null),this._decoder=null,this._encoding=null}n(7)(s,i),s.prototype.update=function(e,t,n){"string"==typeof e&&(e=r.from(e,t));var i=this._update(e);return this.hashMode?this:(n&&(i=this._toString(i,n)),i)},s.prototype.setAutoPadding=function(){},s.prototype.getAuthTag=function(){throw new Error("trying to get auth tag in unsupported state")},s.prototype.setAuthTag=function(){throw new Error("trying to set auth tag in unsupported state")},s.prototype.setAAD=function(){throw new Error("trying to set aad in unsupported state")},s.prototype._transform=function(e,t,n){var r;try{this.hashMode?this._update(e):this.push(this._update(e))}catch(e){r=e}finally{n(r)}},s.prototype._flush=function(e){var t;try{this.push(this.__final())}catch(e){t=e}e(t)},s.prototype._finalOrDigest=function(e){var t=this.__final()||r.alloc(0);return e&&(t=this._toString(t,e,!0)),t},s.prototype._toString=function(e,t,n){if(this._decoder||(this._decoder=new o(t),this._encoding=t),this._encoding!==t)throw new Error("can't switch encodings");var r=this._decoder.write(e);return n&&(r+=this._decoder.end()),r},e.exports=s},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t,n){"use strict";var r;n.d(t,"a",(function(){return r})),function(e){e.API_KEY="API_KEY",e.AWS_IAM="AWS_IAM",e.OPENID_CONNECT="OPENID_CONNECT",e.AMAZON_COGNITO_USER_POOLS="AMAZON_COGNITO_USER_POOLS"}(r||(r={}))},function(e,t,n){"use strict";var r=n(8).Buffer,i=r.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function o(e){var t;switch(this.encoding=function(e){var t=function(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(r.isEncoding===i||!i(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=u,this.end=c,t=4;break;case"utf8":this.fillLast=a,t=4;break;case"base64":this.text=f,this.end=l,t=3;break;default:return this.write=d,void(this.end=h)}this.lastNeed=0,this.lastTotal=0,this.lastChar=r.allocUnsafe(t)}function s(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function a(e){var t=this.lastTotal-this.lastNeed,n=function(e,t,n){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function u(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function c(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function f(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function l(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function d(e){return e.toString(this.encoding)}function h(e){return e&&e.length?this.write(e):""}t.StringDecoder=o,o.prototype.write=function(e){if(0===e.length)return"";var t,n;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n<e.length?t?t+this.text(e,n):this.text(e,n):t||""},o.prototype.end=function(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"�":t},o.prototype.text=function(e,t){var n=function(e,t,n){var r=t.length-1;if(r<n)return 0;var i=s(t[r]);if(i>=0)return i>0&&(e.lastNeed=i-1),i;if(--r<n||-2===i)return 0;if((i=s(t[r]))>=0)return i>0&&(e.lastNeed=i-2),i;if(--r<n||-2===i)return 0;if((i=s(t[r]))>=0)return i>0&&(2===i?i=0:e.lastNeed=i-3),i;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)},o.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,n){"use strict";var r=n(92),i=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=l;var o=Object.create(n(80));o.inherits=n(7);var s=n(171),a=n(120);o.inherits(l,s);for(var u=i(a.prototype),c=0;c<u.length;c++){var f=u[c];l.prototype[f]||(l.prototype[f]=a.prototype[f])}function l(e){if(!(this instanceof l))return new l(e);s.call(this,e),a.call(this,e),e&&!1===e.readable&&(this.readable=!1),e&&!1===e.writable&&(this.writable=!1),this.allowHalfOpen=!0,e&&!1===e.allowHalfOpen&&(this.allowHalfOpen=!1),this.once("end",d)}function d(){this.allowHalfOpen||this._writableState.ended||r.nextTick(h,this)}function h(e){e.end()}Object.defineProperty(l.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),Object.defineProperty(l.prototype,"destroyed",{get:function(){return void 0!==this._readableState&&void 0!==this._writableState&&(this._readableState.destroyed&&this._writableState.destroyed)},set:function(e){void 0!==this._readableState&&void 0!==this._writableState&&(this._readableState.destroyed=e,this._writableState.destroyed=e)}}),l.prototype._destroy=function(e,t){this.push(null),this.end(),r.nextTick(t,e)}},function(e,t){var n=Array.isArray;e.exports=n},function(e,t,n){"use strict";n.d(t,"b",(function(){return _})),n.d(t,"a",(function(){return S}));var r=n(63),i=n(26),o=n(254),s=n(44),a=n(89),u=n(19),c=function(){return(c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},f=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},l=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},d=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},h=new s.a("RestAPI"),p=function(){function e(e){this._api=null,this.Credentials=a.a,this._options=e,h.debug("API Options",this._options)}return e.prototype.getModuleName=function(){return"RestAPI"},e.prototype.configure=function(e){var t=e||{},n=t.API,r=void 0===n?{}:n,i=d(t,["API"]),o=c(c({},i),r);if(h.debug("configure Rest API",{opt:o}),o.aws_project_region){if(o.aws_cloud_logic_custom){var s=o.aws_cloud_logic_custom;o.endpoints="string"==typeof s?JSON.parse(s):s}o=Object.assign({},o,{region:o.aws_project_region,header:{}})}return Array.isArray(o.endpoints)?o.endpoints.forEach((function(e){void 0!==e.custom_header&&"function"!=typeof e.custom_header&&(h.warn("Rest API "+e.name+", custom_header should be a function"),e.custom_header=void 0)})):this._options&&Array.isArray(this._options.endpoints)?o.endpoints=this._options.endpoints:o.endpoints=[],this._options=Object.assign({},this._options,o),this.createInstance(),this._options},e.prototype.createInstance=function(){return h.debug("create Rest API instance"),this._api=new o.a(this._options),this._api.Credentials=this.Credentials,!0},e.prototype.get=function(e,t,n){try{var r=this.getEndpointInfo(e,t),i=this._api.getCancellableToken(),o=Object.assign({},n);o.cancellableToken=i;var s=this._api.get(r,o);return this._api.updateRequestToBeCancellable(s,i),s}catch(e){return Promise.reject(e.message)}},e.prototype.post=function(e,t,n){try{var r=this.getEndpointInfo(e,t),i=this._api.getCancellableToken(),o=Object.assign({},n);o.cancellableToken=i;var s=this._api.post(r,o);return this._api.updateRequestToBeCancellable(s,i),s}catch(e){return Promise.reject(e.message)}},e.prototype.put=function(e,t,n){try{var r=this.getEndpointInfo(e,t),i=this._api.getCancellableToken(),o=Object.assign({},n);o.cancellableToken=i;var s=this._api.put(r,o);return this._api.updateRequestToBeCancellable(s,i),s}catch(e){return Promise.reject(e.message)}},e.prototype.patch=function(e,t,n){try{var r=this.getEndpointInfo(e,t),i=this._api.getCancellableToken(),o=Object.assign({},n);o.cancellableToken=i;var s=this._api.patch(r,o);return this._api.updateRequestToBeCancellable(s,i),s}catch(e){return Promise.reject(e.message)}},e.prototype.del=function(e,t,n){try{var r=this.getEndpointInfo(e,t),i=this._api.getCancellableToken(),o=Object.assign({},n);o.cancellableToken=i;var s=this._api.del(r,o);return this._api.updateRequestToBeCancellable(s,i),s}catch(e){return Promise.reject(e.message)}},e.prototype.head=function(e,t,n){try{var r=this.getEndpointInfo(e,t),i=this._api.getCancellableToken(),o=Object.assign({},n);o.cancellableToken=i;var s=this._api.head(r,o);return this._api.updateRequestToBeCancellable(s,i),s}catch(e){return Promise.reject(e.message)}},e.prototype.isCancel=function(e){return this._api.isCancel(e)},e.prototype.cancel=function(e,t){return this._api.cancel(e,t)},e.prototype.endpoint=function(e){return f(this,void 0,void 0,(function(){return l(this,(function(t){return[2,this._api.endpoint(e)]}))}))},e.prototype.getEndpointInfo=function(e,t){var n=this._options.endpoints;if(!Array.isArray(n))throw new Error("API category not configured");var r=n.find((function(t){return t.name===e}));if(!r)throw new Error("API "+e+" does not exist");var i={endpoint:r.endpoint+t};return"string"==typeof r.region?i.region=r.region:"string"==typeof this._options.region&&(i.region=this._options.region),"string"==typeof r.service?i.service=r.service||"execute-api":i.service="execute-api","function"==typeof r.custom_header?i.custom_header=r.custom_header:i.custom_header=void 0,i},e}(),v=new p(null);u.a.register(v);var g=n(248),m=function(){return(m=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},b=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},y=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},w=new s.a("API"),_=function(){function e(e){this.Auth=r.a,this.Cache=i.a,this.Credentials=a.a,this._options=e,this._restApi=new p(e),this._graphqlApi=new g.a(e),w.debug("API Options",this._options)}return e.prototype.getModuleName=function(){return"API"},e.prototype.configure=function(e){this._options=Object.assign({},this._options,e),this._restApi.Credentials=this.Credentials,this._graphqlApi.Auth=this.Auth,this._graphqlApi.Cache=this.Cache,this._graphqlApi.Credentials=this.Credentials;var t=this._restApi.configure(this._options),n=this._graphqlApi.configure(this._options);return m(m({},t),n)},e.prototype.get=function(e,t,n){return this._restApi.get(e,t,n)},e.prototype.post=function(e,t,n){return this._restApi.post(e,t,n)},e.prototype.put=function(e,t,n){return this._restApi.put(e,t,n)},e.prototype.patch=function(e,t,n){return this._restApi.patch(e,t,n)},e.prototype.del=function(e,t,n){return this._restApi.del(e,t,n)},e.prototype.head=function(e,t,n){return this._restApi.head(e,t,n)},e.prototype.isCancel=function(e){return this._restApi.isCancel(e)},e.prototype.cancel=function(e,t){return this._restApi.cancel(e,t)},e.prototype.endpoint=function(e){return b(this,void 0,void 0,(function(){return y(this,(function(t){return[2,this._restApi.endpoint(e)]}))}))},e.prototype.getGraphqlOperationType=function(e){return this._graphqlApi.getGraphqlOperationType(e)},e.prototype.graphql=function(e,t){return this._graphqlApi.graphql(e,t)},e}(),S=new _(null);u.a.register(S)},function(e,t,n){"use strict";n.d(t,"a",(function(){return q}));var r=n(12),i=n(44),o=n(88),s=n(89),a=n(221),u=n(146),c=n(86),f=n(33);var l,d=n(19),h=n(30),p=n(16),v=function(e){var t=window.open(e,"_self");return t?Promise.resolve(t):Promise.reject()},g=n(87),m=n.n(g),b=n(90),y=n.n(b),w=function(){return(w=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},_=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},S=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},E=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},M="undefined"!=typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("amplify_default"):"@@amplify_default",A=function(e,t,n){o.a.dispatch("auth",{event:e,data:t,message:n},"Auth",M)},I=new i.a("OAuth"),k=function(){function e(e){var t=e.config,n=e.cognitoClientId,r=e.scopes,i=void 0===r?[]:r;if(this._urlOpener=t.urlOpener||v,this._config=t,this._cognitoClientId=n,!this.isValidScopes(i))throw Error("scopes must be a String Array");this._scopes=i}return e.prototype.isValidScopes=function(e){return Array.isArray(e)&&e.every((function(e){return"string"==typeof e}))},e.prototype.oauthSignIn=function(e,t,n,i,o,s){void 0===e&&(e="code"),void 0===o&&(o=r.b.Cognito);var a=this._generateState(32),u=s?a+"-"+s.split("").map((function(e){return e.charCodeAt(0).toString(16).padStart(2,"0")})).join(""):a;!function(e){window.sessionStorage.setItem("oauth_state",e)}(u);var c,f=this._generateRandom(128);c=f,window.sessionStorage.setItem("ouath_pkce_key",c);var l=this._generateChallenge(f),d=this._scopes.join(" "),h="https://"+t+"/oauth2/authorize?"+Object.entries(w(w({redirect_uri:n,response_type:e,client_id:i,identity_provider:o,scope:d,state:u},"code"===e?{code_challenge:l}:{}),"code"===e?{code_challenge_method:"S256"}:{})).map((function(e){var t=E(e,2),n=t[0],r=t[1];return encodeURIComponent(n)+"="+encodeURIComponent(r)})).join("&");I.debug("Redirecting to "+h),this._urlOpener(h,n)},e.prototype._handleCodeFlow=function(e){return _(this,void 0,void 0,(function(){var t,n,i,o,s,a,u,c,f,l,d,h;return S(this,(function(v){switch(v.label){case 0:return(t=(Object(p.parse)(e).query||"").split("&").map((function(e){return e.split("=")})).reduce((function(e,t){var n,r=E(t,2),i=r[0],o=r[1];return w(w({},e),((n={})[i]=o,n))}),{code:void 0}).code)&&Object(p.parse)(e).pathname===Object(p.parse)(this._config.redirectSignIn).pathname?(n="https://"+this._config.domain+"/oauth2/token",A("codeFlow",{},"Retrieving tokens from "+n),i=Object(r.d)(this._config)?this._cognitoClientId:this._config.clientID,o=Object(r.d)(this._config)?this._config.redirectSignIn:this._config.redirectUri,g=window.sessionStorage.getItem("ouath_pkce_key"),window.sessionStorage.removeItem("ouath_pkce_key"),a=w({grant_type:"authorization_code",code:t,client_id:i,redirect_uri:o},(s=g)?{code_verifier:s}:{}),I.debug("Calling token endpoint: "+n+" with",a),u=Object.entries(a).map((function(e){var t=E(e,2),n=t[0],r=t[1];return encodeURIComponent(n)+"="+encodeURIComponent(r)})).join("&"),[4,fetch(n,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:u})]):[2];case 1:return[4,v.sent().json()];case 2:if(c=v.sent(),f=c.access_token,l=c.refresh_token,d=c.id_token,h=c.error)throw new Error(h);return[2,{accessToken:f,refreshToken:l,idToken:d}]}var g}))}))},e.prototype._handleImplicitFlow=function(e){return _(this,void 0,void 0,(function(){var t,n,r;return S(this,(function(i){return t=(Object(p.parse)(e).hash||"#").substr(1).split("&").map((function(e){return e.split("=")})).reduce((function(e,t){var n,r=E(t,2),i=r[0],o=r[1];return w(w({},e),((n={})[i]=o,n))}),{id_token:void 0,access_token:void 0}),n=t.id_token,r=t.access_token,A("implicitFlow",{},"Got tokens from "+e),I.debug("Retrieving implicit tokens from "+e+" with"),[2,{accessToken:r,idToken:n,refreshToken:null}]}))}))},e.prototype.handleAuthResponse=function(e){return _(this,void 0,void 0,(function(){var t,n,r,i,o,s,a;return S(this,(function(u){switch(u.label){case 0:if(u.trys.push([0,5,,6]),t=e?w(w({},(Object(p.parse)(e).hash||"#").substr(1).split("&").map((function(e){return e.split("=")})).reduce((function(e,t){var n=E(t,2),r=n[0],i=n[1];return e[r]=i,e}),{})),(Object(p.parse)(e).query||"").split("&").map((function(e){return e.split("=")})).reduce((function(e,t){var n=E(t,2),r=n[0],i=n[1];return e[r]=i,e}),{})):{},n=t.error,r=t.error_description,n)throw new Error(r);return i=this._validateState(t),I.debug("Starting "+this._config.responseType+" flow with "+e),"code"!==this._config.responseType?[3,2]:(o=[{}],[4,this._handleCodeFlow(e)]);case 1:return[2,w.apply(void 0,[w.apply(void 0,o.concat([u.sent()])),{state:i}])];case 2:return s=[{}],[4,this._handleImplicitFlow(e)];case 3:return[2,w.apply(void 0,[w.apply(void 0,s.concat([u.sent()])),{state:i}])];case 4:return[3,6];case 5:throw a=u.sent(),I.error("Error handling auth response.",a),a;case 6:return[2]}}))}))},e.prototype._validateState=function(e){if(e){var t,n=(t=window.sessionStorage.getItem("oauth_state"),window.sessionStorage.removeItem("oauth_state"),t),r=e.state;if(n&&n!==r)throw new Error("Invalid state in OAuth flow");return r}},e.prototype.signOut=function(){return _(this,void 0,void 0,(function(){var e,t,n;return S(this,(function(i){return e="https://"+this._config.domain+"/logout?",t=Object(r.d)(this._config)?this._cognitoClientId:this._config.oauth.clientID,n=Object(r.d)(this._config)?this._config.redirectSignOut:this._config.returnTo,e+=Object.entries({client_id:t,logout_uri:encodeURIComponent(n)}).map((function(e){var t=E(e,2);return t[0]+"="+t[1]})).join("&"),A("oAuthSignOut",{oAuth:"signOut"},"Signing out from "+e),I.debug("Signing out from "+e),[2,this._urlOpener(e)]}))}))},e.prototype._generateState=function(e){for(var t="",n=e,r="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";n>0;--n)t+=r[Math.round(Math.random()*(r.length-1))];return t},e.prototype._generateChallenge=function(e){return this._base64URL(m()(e))},e.prototype._base64URL=function(e){return e.toString(y.a).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")},e.prototype._generateRandom=function(e){var t=new Uint8Array(e);if("undefined"!=typeof window&&window.crypto)window.crypto.getRandomValues(t);else for(var n=0;n<e;n+=1)t[n]=Math.random()*"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~".length|0;return this._bufferToString(t)},e.prototype._bufferToString=function(e){for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",n=[],r=0;r<e.byteLength;r+=1){var i=e[r]%t.length;n.push(t[i])}return n.join("")},e}(),O=n(35),x=(l=function(e,t){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}l(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),C=new i.a("AuthError"),T=function(e){function t(n){var r=this,i=N[n],o=i.message,s=i.log;return(r=e.call(this,o)||this).constructor=t,Object.setPrototypeOf(r,t.prototype),r.name="AuthError",r.log=s||o,C.error(r.log),r}return x(t,e),t}(Error),P=function(e){function t(n){var r=e.call(this,n)||this;return r.constructor=t,Object.setPrototypeOf(r,t.prototype),r.name="NoUserPoolError",r}return x(t,e),t}(T),N={noConfig:{message:O.a.DEFAULT_MSG,log:"\n Error: Amplify has not been configured correctly.\n This error is typically caused by one of the following scenarios:\n\n 1. Make sure you're passing the awsconfig object to Amplify.configure() in your app's entry point\n See https://aws-amplify.github.io/docs/js/authentication#configure-your-app for more information\n \n 2. There might be multiple conflicting versions of aws-amplify or amplify packages in your node_modules.\n Try deleting your node_modules folder and reinstalling the dependencies with `yarn install`\n "},missingAuthConfig:{message:O.a.DEFAULT_MSG,log:"\n Error: Amplify has not been configured correctly. \n The configuration object is missing required auth properties. \n Did you run `amplify push` after adding auth via `amplify add auth`?\n See https://aws-amplify.github.io/docs/js/authentication#amplify-project-setup for more information\n "},emptyUsername:{message:O.a.EMPTY_USERNAME},invalidUsername:{message:O.a.INVALID_USERNAME},emptyPassword:{message:O.a.EMPTY_PASSWORD},emptyCode:{message:O.a.EMPTY_CODE},signUpError:{message:O.a.SIGN_UP_ERROR,log:"The first parameter should either be non-null string or object"},noMFA:{message:O.a.NO_MFA},invalidMFA:{message:O.a.INVALID_MFA},emptyChallengeResponse:{message:O.a.EMPTY_CHALLENGE},noUserSession:{message:O.a.NO_USER_SESSION},default:{message:O.a.DEFAULT_MSG}};function R(e){return(R="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)}var L=function(){return(L=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},j=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},D=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},U=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},B=new i.a("AuthClass"),F="undefined"!=typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("amplify_default"):"@@amplify_default",z=function(e,t,n){o.a.dispatch("auth",{event:e,data:t,message:n},"Auth",F)},q=new(function(){function e(e){var t=this;this.userPool=null,this.user=null,this.oAuthFlowInProgress=!1,this.Credentials=s.a,this.wrapRefreshSessionCallback=function(e){return function(t,n){return n?z("tokenRefresh",void 0,"New token retrieved"):z("tokenRefresh_failure",t,"Failed to retrieve new token"),e(t,n)}},this.configure(e),this.currentCredentials=this.currentCredentials.bind(this),this.currentUserCredentials=this.currentUserCredentials.bind(this),o.a.listen("auth",(function(e){switch(e.payload.event){case"signIn":t._storage.setItem("amplify-signin-with-hostedUI","false");break;case"signOut":t._storage.removeItem("amplify-signin-with-hostedUI");break;case"cognitoHostedUI":t._storage.setItem("amplify-signin-with-hostedUI","true")}}))}return e.prototype.getModuleName=function(){return"Auth"},e.prototype.configure=function(e){var t=this;if(!e)return this._config||{};B.debug("configure Auth");var n=Object.assign({},this._config,a.a.parseMobilehubConfig(e).Auth,e);this._config=n;var i=this._config,o=i.userPoolId,s=i.userPoolWebClientId,l=i.cookieStorage,d=i.oauth,p=i.region,v=i.identityPoolId,g=i.mandatorySignIn,m=i.refreshHandlers,b=i.identityPoolRegion,y=i.clientMetadata,w=i.endpoint;if(this._config.storage){if(!this._isValidAuthStorage(this._config.storage))throw B.error("The storage in the Auth config is not valid!"),new Error("Empty storage object");this._storage=this._config.storage}else this._storage=l?new h.i(l):e.ssr?new u.a:(new c.a).getStorage();if(this._storageSync=Promise.resolve(),"function"==typeof this._storage.sync&&(this._storageSync=this._storage.sync()),o){var _={UserPoolId:o,ClientId:s,endpoint:w};_.Storage=this._storage,this.userPool=new h.g(_,this.wrapRefreshSessionCallback)}this.Credentials.configure({mandatorySignIn:g,region:b||p,userPoolId:o,identityPoolId:v,refreshHandlers:m,storage:this._storage});var S=d?Object(r.d)(this._config.oauth)?d:d.awsCognito:void 0;if(S){var E=Object.assign({cognitoClientId:s,UserPoolId:o,domain:S.domain,scopes:S.scope,redirectSignIn:S.redirectSignIn,redirectSignOut:S.redirectSignOut,responseType:S.responseType,Storage:this._storage,urlOpener:S.urlOpener,clientMetadata:y},S.options);this._oAuthHandler=new k({scopes:E.scopes,config:E,cognitoClientId:E.cognitoClientId});var M={};!function(e){if(f.a.browserOrNode().isBrowser&&window.location)e({url:window.location.href});else if(!f.a.browserOrNode().isNode)throw new Error("Not supported")}((function(e){var n=e.url;M[n]||(M[n]=!0,t._handleAuthResponse(n))}))}return z("configured",null,"The Auth category has been configured successfully"),this._config},e.prototype.signUp=function(e){for(var t=this,n=[],i=1;i<arguments.length;i++)n[i-1]=arguments[i];if(!this.userPool)return this.rejectNoUserPool();var o,s=null,a=null,u=[],c=null;if(e&&"string"==typeof e){s=e,a=n?n[0]:null;var f=n?n[1]:null,l=n?n[2]:null;f&&u.push(new h.f({Name:"email",Value:f})),l&&u.push(new h.f({Name:"phone_number",Value:l}))}else{if(!e||"object"!==R(e))return this.rejectAuthError(r.a.SignUpError);s=e.username,a=e.password,e&&e.clientMetadata?o=e.clientMetadata:this._config.clientMetadata&&(o=this._config.clientMetadata);var d=e.attributes;d&&Object.keys(d).map((function(e){u.push(new h.f({Name:e,Value:d[e]}))}));var p=e.validationData;p&&(c=[],Object.keys(p).map((function(e){c.push(new h.f({Name:e,Value:d[e]}))})))}return s?a?(B.debug("signUp attrs:",u),B.debug("signUp validation data:",c),new Promise((function(e,n){t.userPool.signUp(s,a,u,c,(function(t,r){t?(z("signUp_failure",t,s+" failed to signup"),n(t)):(z("signUp",r,s+" has signed up successfully"),e(r))}),o)}))):this.rejectAuthError(r.a.EmptyPassword):this.rejectAuthError(r.a.EmptyUsername)},e.prototype.confirmSignUp=function(e,t,n){if(!this.userPool)return this.rejectNoUserPool();if(!e)return this.rejectAuthError(r.a.EmptyUsername);if(!t)return this.rejectAuthError(r.a.EmptyCode);var i,o=this.createCognitoUser(e),s=!n||"boolean"!=typeof n.forceAliasCreation||n.forceAliasCreation;return n&&n.clientMetadata?i=n.clientMetadata:this._config.clientMetadata&&(i=this._config.clientMetadata),new Promise((function(e,n){o.confirmRegistration(t,s,(function(t,r){t?n(t):e(r)}),i)}))},e.prototype.resendSignUp=function(e,t){if(void 0===t&&(t=this._config.clientMetadata),!this.userPool)return this.rejectNoUserPool();if(!e)return this.rejectAuthError(r.a.EmptyUsername);var n=this.createCognitoUser(e);return new Promise((function(e,r){n.resendConfirmationCode((function(t,n){t?r(t):e(n)}),t)}))},e.prototype.signIn=function(e,t,n){if(void 0===n&&(n=this._config.clientMetadata),!this.userPool)return this.rejectNoUserPool();var i=null,o=null,s={};if("string"==typeof e)i=e,o=t;else{if(!Object(r.g)(e))return this.rejectAuthError(r.a.InvalidUsername);void 0!==t&&B.warn("The password should be defined under the first parameter object!"),i=e.username,o=e.password,s=e.validationData}if(!i)return this.rejectAuthError(r.a.EmptyUsername);var a=new h.a({Username:i,Password:o,ValidationData:s,ClientMetadata:n});return o?this.signInWithPassword(a):this.signInWithoutPassword(a)},e.prototype.authCallbacks=function(e,t,n){var r=this,i=this;return{onSuccess:function(o){return j(r,void 0,void 0,(function(){var r,s,a,u;return D(this,(function(c){switch(c.label){case 0:B.debug(o),delete e.challengeName,delete e.challengeParam,c.label=1;case 1:return c.trys.push([1,4,5,9]),[4,this.Credentials.clear()];case 2:return c.sent(),[4,this.Credentials.set(o,"session")];case 3:return r=c.sent(),B.debug("succeed to get cognito credentials",r),[3,9];case 4:return s=c.sent(),B.debug("cannot get cognito credentials",s),[3,9];case 5:return c.trys.push([5,7,,8]),[4,this.currentUserPoolUser()];case 6:return a=c.sent(),i.user=a,z("signIn",a,"A user "+e.getUsername()+" has been signed in"),t(a),[3,8];case 7:return u=c.sent(),B.error("Failed to get the signed in user",u),n(u),[3,8];case 8:return[7];case 9:return[2]}}))}))},onFailure:function(t){B.debug("signIn failure",t),z("signIn_failure",t,e.getUsername()+" failed to signin"),n(t)},customChallenge:function(n){B.debug("signIn custom challenge answer required"),e.challengeName="CUSTOM_CHALLENGE",e.challengeParam=n,t(e)},mfaRequired:function(n,r){B.debug("signIn MFA required"),e.challengeName=n,e.challengeParam=r,t(e)},mfaSetup:function(n,r){B.debug("signIn mfa setup",n),e.challengeName=n,e.challengeParam=r,t(e)},newPasswordRequired:function(n,r){B.debug("signIn new password"),e.challengeName="NEW_PASSWORD_REQUIRED",e.challengeParam={userAttributes:n,requiredAttributes:r},t(e)},totpRequired:function(n,r){B.debug("signIn totpRequired"),e.challengeName=n,e.challengeParam=r,t(e)},selectMFAType:function(n,r){B.debug("signIn selectMFAType",n),e.challengeName=n,e.challengeParam=r,t(e)}}},e.prototype.signInWithPassword=function(e){var t=this;if(this.pendingSignIn)throw new Error("Pending sign-in attempt already in progress");var n=this.createCognitoUser(e.getUsername());return this.pendingSignIn=new Promise((function(r,i){n.authenticateUser(e,t.authCallbacks(n,(function(e){t.pendingSignIn=null,r(e)}),(function(e){t.pendingSignIn=null,i(e)})))})),this.pendingSignIn},e.prototype.signInWithoutPassword=function(e){var t=this,n=this.createCognitoUser(e.getUsername());return n.setAuthenticationFlowType("CUSTOM_AUTH"),new Promise((function(r,i){n.initiateAuth(e,t.authCallbacks(n,r,i))}))},e.prototype.getMFAOptions=function(e){return new Promise((function(t,n){e.getMFAOptions((function(e,r){if(e)return B.debug("get MFA Options failed",e),void n(e);B.debug("get MFA options success",r),t(r)}))}))},e.prototype.getPreferredMFA=function(e,t){var n=this,r=this;return new Promise((function(i,o){var s=n._config.clientMetadata,a=!!t&&t.bypassCache;e.getUserData((function(e,t){if(e)return B.debug("getting preferred mfa failed",e),void o(e);var n=r._getMfaTypeFromUserData(t);return n?void i(n):void o("invalid MFA Type")}),{bypassCache:a,clientMetadata:s})}))},e.prototype._getMfaTypeFromUserData=function(e){var t=null,n=e.PreferredMfaSetting;if(n)t=n;else{var r=e.UserMFASettingList;if(r)0===r.length?t="NOMFA":B.debug("invalid case for getPreferredMFA",e);else t=e.MFAOptions?"SMS_MFA":"NOMFA"}return t},e.prototype._getUserData=function(e,t){return new Promise((function(n,r){e.getUserData((function(e,t){return e?(B.debug("getting user data failed",e),void r(e)):void n(t)}),t)}))},e.prototype.setPreferredMFA=function(e,t){return j(this,void 0,void 0,(function(){var n,i,o,s,a,u;return D(this,(function(c){switch(c.label){case 0:return n=this._config.clientMetadata,[4,this._getUserData(e,{bypassCache:!0,clientMetadata:n})];case 1:switch(i=c.sent(),o=null,s=null,t){case"TOTP":return[3,2];case"SMS":return[3,3];case"NOMFA":return[3,4]}return[3,6];case 2:return s={PreferredMfa:!0,Enabled:!0},[3,7];case 3:return o={PreferredMfa:!0,Enabled:!0},[3,7];case 4:return a=i.UserMFASettingList,[4,this._getMfaTypeFromUserData(i)];case 5:if("NOMFA"===(u=c.sent()))return[2,Promise.resolve("No change for mfa type")];if("SMS_MFA"===u)o={PreferredMfa:!1,Enabled:!1};else{if("SOFTWARE_TOKEN_MFA"!==u)return[2,this.rejectAuthError(r.a.InvalidMFA)];s={PreferredMfa:!1,Enabled:!1}}return a&&0!==a.length&&a.forEach((function(e){"SMS_MFA"===e?o={PreferredMfa:!1,Enabled:!1}:"SOFTWARE_TOKEN_MFA"===e&&(s={PreferredMfa:!1,Enabled:!1})})),[3,7];case 6:return B.debug("no validmfa method provided"),[2,this.rejectAuthError(r.a.NoMFA)];case 7:return this,[2,new Promise((function(t,r){e.setUserMfaPreference(o,s,(function(i,o){if(i)return B.debug("Set user mfa preference error",i),r(i);B.debug("Set user mfa success",o),B.debug("Caching the latest user data into local"),e.getUserData((function(e,n){return e?(B.debug("getting user data failed",e),r(e)):t(o)}),{bypassCache:!0,clientMetadata:n})}))}))]}}))}))},e.prototype.disableSMS=function(e){return new Promise((function(t,n){e.disableMFA((function(e,r){if(e)return B.debug("disable mfa failed",e),void n(e);B.debug("disable mfa succeed",r),t(r)}))}))},e.prototype.enableSMS=function(e){return new Promise((function(t,n){e.enableMFA((function(e,r){if(e)return B.debug("enable mfa failed",e),void n(e);B.debug("enable mfa succeed",r),t(r)}))}))},e.prototype.setupTOTP=function(e){return new Promise((function(t,n){e.associateSoftwareToken({onFailure:function(e){B.debug("associateSoftwareToken failed",e),n(e)},associateSecretCode:function(e){B.debug("associateSoftwareToken sucess",e),t(e)}})}))},e.prototype.verifyTotpToken=function(e,t){return B.debug("verification totp token",e,t),new Promise((function(n,r){e.verifySoftwareToken(t,"My TOTP device",{onFailure:function(e){B.debug("verifyTotpToken failed",e),r(e)},onSuccess:function(t){z("signIn",e,"A user "+e.getUsername()+" has been signed in"),B.debug("verifyTotpToken success",t),n(t)}})}))},e.prototype.confirmSignIn=function(e,t,n,i){var o=this;if(void 0===i&&(i=this._config.clientMetadata),!t)return this.rejectAuthError(r.a.EmptyCode);var s=this;return new Promise((function(r,a){e.sendMFACode(t,{onSuccess:function(t){return j(o,void 0,void 0,(function(){var n,i;return D(this,(function(o){switch(o.label){case 0:B.debug(t),o.label=1;case 1:return o.trys.push([1,4,5,6]),[4,this.Credentials.clear()];case 2:return o.sent(),[4,this.Credentials.set(t,"session")];case 3:return n=o.sent(),B.debug("succeed to get cognito credentials",n),[3,6];case 4:return i=o.sent(),B.debug("cannot get cognito credentials",i),[3,6];case 5:return s.user=e,z("signIn",e,"A user "+e.getUsername()+" has been signed in"),r(e),[7];case 6:return[2]}}))}))},onFailure:function(e){B.debug("confirm signIn failure",e),a(e)}},n,i)}))},e.prototype.completeNewPassword=function(e,t,n,i){var o=this;if(void 0===n&&(n={}),void 0===i&&(i=this._config.clientMetadata),!t)return this.rejectAuthError(r.a.EmptyPassword);var s=this;return new Promise((function(r,a){e.completeNewPasswordChallenge(t,n,{onSuccess:function(t){return j(o,void 0,void 0,(function(){var n,i;return D(this,(function(o){switch(o.label){case 0:B.debug(t),o.label=1;case 1:return o.trys.push([1,4,5,6]),[4,this.Credentials.clear()];case 2:return o.sent(),[4,this.Credentials.set(t,"session")];case 3:return n=o.sent(),B.debug("succeed to get cognito credentials",n),[3,6];case 4:return i=o.sent(),B.debug("cannot get cognito credentials",i),[3,6];case 5:return s.user=e,z("signIn",e,"A user "+e.getUsername()+" has been signed in"),r(e),[7];case 6:return[2]}}))}))},onFailure:function(e){B.debug("completeNewPassword failure",e),z("completeNewPassword_failure",e,o.user+" failed to complete the new password flow"),a(e)},mfaRequired:function(t,n){B.debug("signIn MFA required"),e.challengeName=t,e.challengeParam=n,r(e)},mfaSetup:function(t,n){B.debug("signIn mfa setup",t),e.challengeName=t,e.challengeParam=n,r(e)},totpRequired:function(t,n){B.debug("signIn mfa setup",t),e.challengeName=t,e.challengeParam=n,r(e)}},i)}))},e.prototype.sendCustomChallengeAnswer=function(e,t,n){var i=this;if(void 0===n&&(n=this._config.clientMetadata),!this.userPool)return this.rejectNoUserPool();if(!t)return this.rejectAuthError(r.a.EmptyChallengeResponse);return new Promise((function(r,o){e.sendCustomChallengeAnswer(t,i.authCallbacks(e,r,o),n)}))},e.prototype.updateUserAttributes=function(e,t,n){void 0===n&&(n=this._config.clientMetadata);var r=[],i=this;return new Promise((function(o,s){i.userSession(e).then((function(i){for(var a in t)if("sub"!==a&&a.indexOf("_verified")<0){var u={Name:a,Value:t[a]};r.push(u)}e.updateAttributes(r,(function(e,t){return e?s(e):o(t)}),n)}))}))},e.prototype.userAttributes=function(e){var t=this;return new Promise((function(n,r){t.userSession(e).then((function(t){e.getUserAttributes((function(e,t){e?r(e):n(t)}))}))}))},e.prototype.verifiedContact=function(e){var t=this;return this.userAttributes(e).then((function(e){var n=t.attributesToObject(e),r={},i={};return n.email&&(n.email_verified?i.email=n.email:r.email=n.email),n.phone_number&&(n.phone_number_verified?i.phone_number=n.phone_number:r.phone_number=n.phone_number),{verified:i,unverified:r}}))},e.prototype.currentUserPoolUser=function(e){var t=this;return this.userPool?new Promise((function(n,r){t._storageSync.then((function(){return j(t,void 0,void 0,(function(){var t,i,s=this;return D(this,(function(a){switch(a.label){case 0:return this.isOAuthInProgress()?(B.debug("OAuth signIn in progress, waiting for resolution..."),[4,new Promise((function(e){var t=setTimeout((function(){B.debug("OAuth signIn in progress timeout"),o.a.remove("auth",n),e()}),1e4);function n(r){var i=r.payload.event;"cognitoHostedUI"!==i&&"cognitoHostedUI_failure"!==i||(B.debug("OAuth signIn resolved: "+i),clearTimeout(t),o.a.remove("auth",n),e())}o.a.listen("auth",n)}))]):[3,2];case 1:a.sent(),a.label=2;case 2:return(t=this.userPool.getCurrentUser())?(i=this._config.clientMetadata,t.getSession((function(i,o){return j(s,void 0,void 0,(function(){var s,a,u,c=this;return D(this,(function(f){switch(f.label){case 0:return i?(B.debug("Failed to get the user session",i),r(i),[2]):(s=!!e&&e.bypassCache)?[4,this.Credentials.clear()]:[3,2];case 1:f.sent(),f.label=2;case 2:return a=this._config.clientMetadata,u=o.getAccessToken().decodePayload().scope,(void 0===u?"":u).split(" ").includes("aws.cognito.signin.user.admin")?(t.getUserData((function(e,i){if(e)return B.debug("getting user data failed",e),void("User is disabled."===e.message||"User does not exist."===e.message||"Access Token has been revoked"===e.message?r(e):n(t));for(var o=i.PreferredMfaSetting||"NOMFA",s=[],a=0;a<i.UserAttributes.length;a++){var u={Name:i.UserAttributes[a].Name,Value:i.UserAttributes[a].Value},f=new h.f(u);s.push(f)}var l=c.attributesToObject(s);return Object.assign(t,{attributes:l,preferredMFA:o}),n(t)}),{bypassCache:s,clientMetadata:a}),[2]):(B.debug("Unable to get the user data because the aws.cognito.signin.user.admin is not in the scopes of the access token"),[2,n(t)])}}))}))}),{clientMetadata:i}),[2]):(B.debug("Failed to get user from user pool"),r("No current user"),[2])}}))}))})).catch((function(e){return B.debug("Failed to sync cache info into memory",e),r(e)}))})):this.rejectNoUserPool()},e.prototype.isOAuthInProgress=function(){return this.oAuthFlowInProgress},e.prototype.currentAuthenticatedUser=function(e){return j(this,void 0,void 0,(function(){var t,n,r,i,o;return D(this,(function(s){switch(s.label){case 0:B.debug("getting current authenticated user"),t=null,s.label=1;case 1:return s.trys.push([1,3,,4]),[4,this._storageSync];case 2:return s.sent(),[3,4];case 3:throw n=s.sent(),B.debug("Failed to sync cache info into memory",n),n;case 4:try{(r=JSON.parse(this._storage.getItem("aws-amplify-federatedInfo")))&&(t=L(L({},r.user),{token:r.token}))}catch(e){B.debug("cannot load federated user from auth storage")}return t?(this.user=t,B.debug("get current authenticated federated user",this.user),[2,this.user]):[3,5];case 5:B.debug("get current authenticated userpool user"),i=null,s.label=6;case 6:return s.trys.push([6,8,,9]),[4,this.currentUserPoolUser(e)];case 7:return i=s.sent(),[3,9];case 8:return"No userPool"===(o=s.sent())&&B.error("Cannot get the current user because the user pool is missing. Please make sure the Auth module is configured with a valid Cognito User Pool ID"),B.debug("The user is not authenticated by the error",o),[2,Promise.reject("The user is not authenticated")];case 9:return this.user=i,[2,this.user]}}))}))},e.prototype.currentSession=function(){var e=this;return B.debug("Getting current session"),this.userPool?new Promise((function(t,n){e.currentUserPoolUser().then((function(r){e.userSession(r).then((function(e){t(e)})).catch((function(e){B.debug("Failed to get the current session",e),n(e)}))})).catch((function(e){B.debug("Failed to get the current user",e),n(e)}))})):Promise.reject()},e.prototype.userSession=function(e){if(!e)return B.debug("the user is null"),this.rejectAuthError(r.a.NoUserSession);var t=this._config.clientMetadata;return new Promise((function(n,r){B.debug("Getting the session from this user:",e),e.getSession((function(t,i){return t?(B.debug("Failed to get the session from user",e),void r(t)):(B.debug("Succeed to get the user session",i),void n(i))}),{clientMetadata:t})}))},e.prototype.currentUserCredentials=function(){return j(this,void 0,void 0,(function(){var e,t,n=this;return D(this,(function(r){switch(r.label){case 0:B.debug("Getting current user credentials"),r.label=1;case 1:return r.trys.push([1,3,,4]),[4,this._storageSync];case 2:return r.sent(),[3,4];case 3:throw e=r.sent(),B.debug("Failed to sync cache info into memory",e),e;case 4:t=null;try{t=JSON.parse(this._storage.getItem("aws-amplify-federatedInfo"))}catch(e){B.debug("failed to get or parse item aws-amplify-federatedInfo",e)}return t?[2,this.Credentials.refreshFederatedToken(t)]:[2,this.currentSession().then((function(e){return B.debug("getting session success",e),n.Credentials.set(e,"session")})).catch((function(e){return B.debug("getting session failed",e),n.Credentials.set(null,"guest")}))]}}))}))},e.prototype.currentCredentials=function(){return B.debug("getting current credentials"),this.Credentials.get()},e.prototype.verifyUserAttribute=function(e,t,n){return void 0===n&&(n=this._config.clientMetadata),new Promise((function(r,i){e.getAttributeVerificationCode(t,{onSuccess:function(){return r()},onFailure:function(e){return i(e)}},n)}))},e.prototype.verifyUserAttributeSubmit=function(e,t,n){return n?new Promise((function(r,i){e.verifyAttribute(t,n,{onSuccess:function(e){r(e)},onFailure:function(e){i(e)}})})):this.rejectAuthError(r.a.EmptyCode)},e.prototype.verifyCurrentUserAttribute=function(e){var t=this;return t.currentUserPoolUser().then((function(n){return t.verifyUserAttribute(n,e)}))},e.prototype.verifyCurrentUserAttributeSubmit=function(e,t){var n=this;return n.currentUserPoolUser().then((function(r){return n.verifyUserAttributeSubmit(r,e,t)}))},e.prototype.cognitoIdentitySignOut=function(e,t){return j(this,void 0,void 0,(function(){var n,r,i=this;return D(this,(function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),[4,this._storageSync];case 1:return o.sent(),[3,3];case 2:throw n=o.sent(),B.debug("Failed to sync cache info into memory",n),n;case 3:return r=this._oAuthHandler&&"true"===this._storage.getItem("amplify-signin-with-hostedUI"),[2,new Promise((function(n,o){if(e&&e.global){B.debug("user global sign out",t);var s=i._config.clientMetadata;t.getSession((function(e,s){if(e)return B.debug("failed to get the user session",e),o(e);t.globalSignOut({onSuccess:function(e){if(B.debug("global sign out success"),!r)return n();i.oAuthSignOutRedirect(n,o)},onFailure:function(e){return B.debug("global sign out failed",e),o(e)}})}),{clientMetadata:s})}else{if(B.debug("user sign out",t),t.signOut(),!r)return n();i.oAuthSignOutRedirect(n,o)}}))]}}))}))},e.prototype.oAuthSignOutRedirect=function(e,t){f.a.browserOrNode().isBrowser?this.oAuthSignOutRedirectOrReject(t):this.oAuthSignOutAndResolve(e)},e.prototype.oAuthSignOutAndResolve=function(e){this._oAuthHandler.signOut(),e()},e.prototype.oAuthSignOutRedirectOrReject=function(e){this._oAuthHandler.signOut(),setTimeout((function(){return e("Signout timeout fail")}),3e3)},e.prototype.signOut=function(e){return j(this,void 0,void 0,(function(){var t;return D(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,this.cleanCachedItems()];case 1:return n.sent(),[3,3];case 2:return n.sent(),B.debug("failed to clear cached items"),[3,3];case 3:return this.userPool?(t=this.userPool.getCurrentUser())?[4,this.cognitoIdentitySignOut(e,t)]:[3,5]:[3,7];case 4:return n.sent(),[3,6];case 5:B.debug("no current Cognito user"),n.label=6;case 6:return[3,8];case 7:B.debug("no Congito User pool"),n.label=8;case 8:return z("signOut",this.user,"A user has been signed out"),this.user=null,[2]}}))}))},e.prototype.cleanCachedItems=function(){return j(this,void 0,void 0,(function(){return D(this,(function(e){switch(e.label){case 0:return[4,this.Credentials.clear()];case 1:return e.sent(),[2]}}))}))},e.prototype.changePassword=function(e,t,n,r){var i=this;return void 0===r&&(r=this._config.clientMetadata),new Promise((function(o,s){i.userSession(e).then((function(i){e.changePassword(t,n,(function(e,t){return e?(B.debug("change password failure",e),s(e)):o(t)}),r)}))}))},e.prototype.forgotPassword=function(e,t){if(void 0===t&&(t=this._config.clientMetadata),!this.userPool)return this.rejectNoUserPool();if(!e)return this.rejectAuthError(r.a.EmptyUsername);var n=this.createCognitoUser(e);return new Promise((function(r,i){n.forgotPassword({onSuccess:function(){r()},onFailure:function(t){B.debug("forgot password failure",t),z("forgotPassword_failure",t,e+" forgotPassword failed"),i(t)},inputVerificationCode:function(t){z("forgotPassword",n,e+" has initiated forgot password flow"),r(t)}},t)}))},e.prototype.forgotPasswordSubmit=function(e,t,n,i){if(void 0===i&&(i=this._config.clientMetadata),!this.userPool)return this.rejectNoUserPool();if(!e)return this.rejectAuthError(r.a.EmptyUsername);if(!t)return this.rejectAuthError(r.a.EmptyCode);if(!n)return this.rejectAuthError(r.a.EmptyPassword);var o=this.createCognitoUser(e);return new Promise((function(r,s){o.confirmPassword(t,n,{onSuccess:function(){z("forgotPasswordSubmit",o,e+" forgotPasswordSubmit successful"),r()},onFailure:function(t){z("forgotPasswordSubmit_failure",t,e+" forgotPasswordSubmit failed"),s(t)}},i)}))},e.prototype.currentUserInfo=function(){return j(this,void 0,void 0,(function(){var e,t,n,r,i,o,s;return D(this,(function(a){switch(a.label){case 0:return(e=this.Credentials.getCredSource())&&"aws"!==e&&"userPool"!==e?[3,9]:[4,this.currentUserPoolUser().catch((function(e){return B.debug(e)}))];case 1:if(!(s=a.sent()))return[2,null];a.label=2;case 2:return a.trys.push([2,8,,9]),[4,this.userAttributes(s)];case 3:t=a.sent(),n=this.attributesToObject(t),r=null,a.label=4;case 4:return a.trys.push([4,6,,7]),[4,this.currentCredentials()];case 5:return r=a.sent(),[3,7];case 6:return i=a.sent(),B.debug("Failed to retrieve credentials while getting current user info",i),[3,7];case 7:return[2,{id:r?r.identityId:void 0,username:s.getUsername(),attributes:n}];case 8:return o=a.sent(),B.debug("currentUserInfo error",o),[2,{}];case 9:return"federated"===e?[2,(s=this.user)||{}]:[2]}}))}))},e.prototype.federatedSignIn=function(e,t,n){return j(this,void 0,void 0,(function(){var i,o,s,a,u,c,f,l,d,h,p;return D(this,(function(v){switch(v.label){case 0:if(!this._config.identityPoolId&&!this._config.userPoolId)throw new Error("Federation requires either a User Pool or Identity Pool in config");if(void 0===e&&this._config.identityPoolId&&!this._config.userPoolId)throw new Error("Federation with Identity Pools requires tokens passed as arguments");return Object(r.e)(e)||Object(r.f)(e)||Object(r.c)(e)||void 0===e?(i=e||{provider:r.b.Cognito},u=Object(r.e)(i)?i.provider:i.customProvider,Object(r.e)(i),o=i.customState,this._config.userPoolId&&(s=Object(r.d)(this._config.oauth)?this._config.userPoolWebClientId:this._config.oauth.clientID,a=Object(r.d)(this._config.oauth)?this._config.oauth.redirectSignIn:this._config.oauth.redirectUri,this._oAuthHandler.oauthSignIn(this._config.oauth.responseType,this._config.oauth.domain,a,s,u,o)),[3,4]):[3,1];case 1:u=e;try{(c=JSON.stringify(JSON.parse(this._storage.getItem("aws-amplify-federatedInfo")).user))&&B.warn("There is already a signed in user: "+c+" in your app.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tYou should not call Auth.federatedSignIn method again as it may cause unexpected behavior.")}catch(e){}return f=t.token,l=t.identity_id,d=t.expires_at,[4,this.Credentials.set({provider:u,token:f,identity_id:l,user:n,expires_at:d},"federation")];case 2:return h=v.sent(),[4,this.currentAuthenticatedUser()];case 3:return p=v.sent(),z("signIn",p,"A user "+p.username+" has been signed in"),B.debug("federated sign in credentials",h),[2,h];case 4:return[2]}}))}))},e.prototype._handleAuthResponse=function(e){return j(this,void 0,void 0,(function(){var t,n,r,i,o,s,a,u,c,l,d,v,g,m;return D(this,(function(b){switch(b.label){case 0:if(this.oAuthFlowInProgress)return B.debug("Skipping URL "+e+" current flow in progress"),[2];b.label=1;case 1:if(b.trys.push([1,,8,9]),this.oAuthFlowInProgress=!0,!this._config.userPoolId)throw new Error("OAuth responses require a User Pool defined in config");if(z("parsingCallbackUrl",{url:e},"The callback url is being parsed"),t=e||(f.a.browserOrNode().isBrowser?window.location.href:""),n=!!(Object(p.parse)(t).query||"").split("&").map((function(e){return e.split("=")})).find((function(e){var t=U(e,1)[0];return"code"===t||"error"===t})),r=!!(Object(p.parse)(t).hash||"#").substr(1).split("&").map((function(e){return e.split("=")})).find((function(e){var t=U(e,1)[0];return"access_token"===t||"error"===t})),!n&&!r)return[3,7];this._storage.setItem("amplify-redirected-from-hosted-ui","true"),b.label=2;case 2:return b.trys.push([2,6,,7]),[4,this._oAuthHandler.handleAuthResponse(t)];case 3:return i=b.sent(),o=i.accessToken,s=i.idToken,a=i.refreshToken,u=i.state,c=new h.h({IdToken:new h.c({IdToken:s}),RefreshToken:new h.d({RefreshToken:a}),AccessToken:new h.b({AccessToken:o})}),l=void 0,this._config.identityPoolId?[4,this.Credentials.set(c,"session")]:[3,5];case 4:l=b.sent(),B.debug("AWS credentials",l),b.label=5;case 5:return d=/-/.test(u),(v=this.createCognitoUser(c.getIdToken().decodePayload()["cognito:username"])).setSignInUserSession(c),window&&void 0!==window.history&&window.history.replaceState({},null,this._config.oauth.redirectSignIn),z("signIn",v,"A user "+v.getUsername()+" has been signed in"),z("cognitoHostedUI",v,"A user "+v.getUsername()+" has been signed in via Cognito Hosted UI"),d&&(g=u.split("-").splice(1).join("-"),z("customOAuthState",g.match(/.{2}/g).map((function(e){return String.fromCharCode(parseInt(e,16))})).join(""),"State for user "+v.getUsername())),[2,l];case 6:return m=b.sent(),B.debug("Error in cognito hosted auth response",m),z("signIn_failure",m,"The OAuth response flow failed"),z("cognitoHostedUI_failure",m,"A failure occurred when returning to the Cognito Hosted UI"),z("customState_failure",m,"A failure occurred when returning state"),[3,7];case 7:return[3,9];case 8:return this.oAuthFlowInProgress=!1,[7];case 9:return[2]}}))}))},e.prototype.essentialCredentials=function(e){return{accessKeyId:e.accessKeyId,sessionToken:e.sessionToken,secretAccessKey:e.secretAccessKey,identityId:e.identityId,authenticated:e.authenticated}},e.prototype.attributesToObject=function(e){var t={};return e&&e.map((function(e){"email_verified"===e.Name||"phone_number_verified"===e.Name?t[e.Name]="true"===e.Value||!0===e.Value:t[e.Name]=e.Value})),t},e.prototype.createCognitoUser=function(e){var t={Username:e,Pool:this.userPool};t.Storage=this._storage;var n=this._config.authenticationFlowType,r=new h.e(t);return n&&r.setAuthenticationFlowType(n),r},e.prototype._isValidAuthStorage=function(e){return!!e&&"function"==typeof e.getItem&&"function"==typeof e.setItem&&"function"==typeof e.removeItem&&"function"==typeof e.clear},e.prototype.noUserPoolErrorHandler=function(e){return!e||e.userPoolId&&e.identityPoolId?r.a.NoConfig:r.a.MissingAuthConfig},e.prototype.rejectAuthError=function(e){return Promise.reject(new T(e))},e.prototype.rejectNoUserPool=function(){var e=this.noUserPoolErrorHandler(this._config);return Promise.reject(new P(e))},e}())(null);d.a.register(q)},function(e,t,n){e.exports=n(465)},function(e,t,n){var r,i; -/*! - * JavaScript Cookie v2.2.1 - * https://github.com/js-cookie/js-cookie - * - * Copyright 2006, 2015 Klaus Hartl & Fagner Brack - * Released under the MIT license - */!function(o){if(void 0===(i="function"==typeof(r=o)?r.call(t,n,t,e):r)||(e.exports=i),!0,e.exports=o(),!!0){var s=window.Cookies,a=window.Cookies=o();a.noConflict=function(){return window.Cookies=s,a}}}((function(){function e(){for(var e=0,t={};e<arguments.length;e++){var n=arguments[e];for(var r in n)t[r]=n[r]}return t}function t(e){return e.replace(/(%[0-9A-Z]{2})+/g,decodeURIComponent)}return function n(r){function i(){}function o(t,n,o){if("undefined"!=typeof document){"number"==typeof(o=e({path:"/"},i.defaults,o)).expires&&(o.expires=new Date(1*new Date+864e5*o.expires)),o.expires=o.expires?o.expires.toUTCString():"";try{var s=JSON.stringify(n);/^[\{\[]/.test(s)&&(n=s)}catch(e){}n=r.write?r.write(n,t):encodeURIComponent(String(n)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent).replace(/[\(\)]/g,escape);var a="";for(var u in o)o[u]&&(a+="; "+u,!0!==o[u]&&(a+="="+o[u].split(";")[0]));return document.cookie=t+"="+n+a}}function s(e,n){if("undefined"!=typeof document){for(var i={},o=document.cookie?document.cookie.split("; "):[],s=0;s<o.length;s++){var a=o[s].split("="),u=a.slice(1).join("=");n||'"'!==u.charAt(0)||(u=u.slice(1,-1));try{var c=t(a[0]);if(u=(r.read||r)(u,c)||t(u),n)try{u=JSON.parse(u)}catch(e){}if(i[c]=u,e===c)break}catch(e){}}return e?i[e]:i}}return i.set=o,i.get=function(e){return s(e,!1)},i.getJSON=function(e){return s(e,!0)},i.remove=function(t,n){o(t,"",e(n,{expires:-1}))},i.defaults={},i.withConverter=n,i}((function(){}))}))},function(e,t,n){"use strict";(function(t,r){var i=n(8).Buffer,o=t.crypto||t.msCrypto;o&&o.getRandomValues?e.exports=function(e,t){if(e>4294967295)throw new RangeError("requested too many random bytes");var n=i.allocUnsafe(e);if(e>0)if(e>65536)for(var s=0;s<e;s+=65536)o.getRandomValues(n.slice(s,s+65536));else o.getRandomValues(n);if("function"==typeof t)return r.nextTick((function(){t(null,n)}));return n}:e.exports=function(){throw new Error("Secure random number generation is not supported by this browser.\nUse Chrome, Firefox or Internet Explorer 11")}}).call(this,n(31),n(20))},function(e,t,n){"use strict";var r={};function i(e,t,n){n||(n=Error);var i=function(e){var n,r;function i(n,r,i){return e.call(this,function(e,n,r){return"string"==typeof t?t:t(e,n,r)}(n,r,i))||this}return r=e,(n=i).prototype=Object.create(r.prototype),n.prototype.constructor=n,n.__proto__=r,i}(n);i.prototype.name=n.name,i.prototype.code=e,r[e]=i}function o(e,t){if(Array.isArray(e)){var n=e.length;return e=e.map((function(e){return String(e)})),n>2?"one of ".concat(t," ").concat(e.slice(0,n-1).join(", "),", or ")+e[n-1]:2===n?"one of ".concat(t," ").concat(e[0]," or ").concat(e[1]):"of ".concat(t," ").concat(e[0])}return"of ".concat(t," ").concat(String(e))}i("ERR_INVALID_OPT_VALUE",(function(e,t){return'The value "'+t+'" is invalid for option "'+e+'"'}),TypeError),i("ERR_INVALID_ARG_TYPE",(function(e,t,n){var r,i,s,a;if("string"==typeof t&&(i="not ",t.substr(!s||s<0?0:+s,i.length)===i)?(r="must not be",t=t.replace(/^not /,"")):r="must be",function(e,t,n){return(void 0===n||n>e.length)&&(n=e.length),e.substring(n-t.length,n)===t}(e," argument"))a="The ".concat(e," ").concat(r," ").concat(o(t,"type"));else{var u=function(e,t,n){return"number"!=typeof n&&(n=0),!(n+t.length>e.length)&&-1!==e.indexOf(t,n)}(e,".")?"property":"argument";a='The "'.concat(e,'" ').concat(u," ").concat(r," ").concat(o(t,"type"))}return a+=". Received type ".concat(typeof n)}),TypeError),i("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF"),i("ERR_METHOD_NOT_IMPLEMENTED",(function(e){return"The "+e+" method is not implemented"})),i("ERR_STREAM_PREMATURE_CLOSE","Premature close"),i("ERR_STREAM_DESTROYED",(function(e){return"Cannot call "+e+" after a stream was destroyed"})),i("ERR_MULTIPLE_CALLBACK","Callback called multiple times"),i("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable"),i("ERR_STREAM_WRITE_AFTER_END","write after end"),i("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),i("ERR_UNKNOWN_ENCODING",(function(e){return"Unknown encoding: "+e}),TypeError),i("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event"),e.exports.codes=r},function(e,t,n){"use strict";(function(t){var r=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=c;var i=n(163),o=n(167);n(7)(c,i);for(var s=r(o.prototype),a=0;a<s.length;a++){var u=s[a];c.prototype[u]||(c.prototype[u]=o.prototype[u])}function c(e){if(!(this instanceof c))return new c(e);i.call(this,e),o.call(this,e),this.allowHalfOpen=!0,e&&(!1===e.readable&&(this.readable=!1),!1===e.writable&&(this.writable=!1),!1===e.allowHalfOpen&&(this.allowHalfOpen=!1,this.once("end",f)))}function f(){this._writableState.ended||t.nextTick(l,this)}function l(e){e.end()}Object.defineProperty(c.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),Object.defineProperty(c.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(c.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(c.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._readableState&&void 0!==this._writableState&&(this._readableState.destroyed&&this._writableState.destroyed)},set:function(e){void 0!==this._readableState&&void 0!==this._writableState&&(this._readableState.destroyed=e,this._writableState.destroyed=e)}})}).call(this,n(20))},function(e,t,n){var r=n(8).Buffer;function i(e,t){this._block=r.alloc(e),this._finalSize=t,this._blockSize=e,this._len=0}i.prototype.update=function(e,t){"string"==typeof e&&(t=t||"utf8",e=r.from(e,t));for(var n=this._block,i=this._blockSize,o=e.length,s=this._len,a=0;a<o;){for(var u=s%i,c=Math.min(o-a,i-u),f=0;f<c;f++)n[u+f]=e[a+f];a+=c,(s+=c)%i==0&&this._update(n)}return this._len+=o,this},i.prototype.digest=function(e){var t=this._len%this._blockSize;this._block[t]=128,this._block.fill(0,t+1),t>=this._finalSize&&(this._update(this._block),this._block.fill(0));var n=8*this._len;if(n<=4294967295)this._block.writeUInt32BE(n,this._blockSize-4);else{var r=(4294967295&n)>>>0,i=(n-r)/4294967296;this._block.writeUInt32BE(i,this._blockSize-8),this._block.writeUInt32BE(r,this._blockSize-4)}this._update(this._block);var o=this._hash();return e?o.toString(e):o},i.prototype._update=function(){throw new Error("_update must be implemented by subclass")},e.exports=i},function(e,t,n){"use strict";var r={};function i(e,t,n){n||(n=Error);var i=function(e){var n,r;function i(n,r,i){return e.call(this,function(e,n,r){return"string"==typeof t?t:t(e,n,r)}(n,r,i))||this}return r=e,(n=i).prototype=Object.create(r.prototype),n.prototype.constructor=n,n.__proto__=r,i}(n);i.prototype.name=n.name,i.prototype.code=e,r[e]=i}function o(e,t){if(Array.isArray(e)){var n=e.length;return e=e.map((function(e){return String(e)})),n>2?"one of ".concat(t," ").concat(e.slice(0,n-1).join(", "),", or ")+e[n-1]:2===n?"one of ".concat(t," ").concat(e[0]," or ").concat(e[1]):"of ".concat(t," ").concat(e[0])}return"of ".concat(t," ").concat(String(e))}i("ERR_INVALID_OPT_VALUE",(function(e,t){return'The value "'+t+'" is invalid for option "'+e+'"'}),TypeError),i("ERR_INVALID_ARG_TYPE",(function(e,t,n){var r,i,s,a;if("string"==typeof t&&(i="not ",t.substr(!s||s<0?0:+s,i.length)===i)?(r="must not be",t=t.replace(/^not /,"")):r="must be",function(e,t,n){return(void 0===n||n>e.length)&&(n=e.length),e.substring(n-t.length,n)===t}(e," argument"))a="The ".concat(e," ").concat(r," ").concat(o(t,"type"));else{var u=function(e,t,n){return"number"!=typeof n&&(n=0),!(n+t.length>e.length)&&-1!==e.indexOf(t,n)}(e,".")?"property":"argument";a='The "'.concat(e,'" ').concat(u," ").concat(r," ").concat(o(t,"type"))}return a+=". Received type ".concat(typeof n)}),TypeError),i("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF"),i("ERR_METHOD_NOT_IMPLEMENTED",(function(e){return"The "+e+" method is not implemented"})),i("ERR_STREAM_PREMATURE_CLOSE","Premature close"),i("ERR_STREAM_DESTROYED",(function(e){return"Cannot call "+e+" after a stream was destroyed"})),i("ERR_MULTIPLE_CALLBACK","Callback called multiple times"),i("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable"),i("ERR_STREAM_WRITE_AFTER_END","write after end"),i("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),i("ERR_UNKNOWN_ENCODING",(function(e){return"Unknown encoding: "+e}),TypeError),i("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event"),e.exports.codes=r},function(e,t,n){"use strict";(function(t){var r=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=c;var i=n(192),o=n(196);n(7)(c,i);for(var s=r(o.prototype),a=0;a<s.length;a++){var u=s[a];c.prototype[u]||(c.prototype[u]=o.prototype[u])}function c(e){if(!(this instanceof c))return new c(e);i.call(this,e),o.call(this,e),this.allowHalfOpen=!0,e&&(!1===e.readable&&(this.readable=!1),!1===e.writable&&(this.writable=!1),!1===e.allowHalfOpen&&(this.allowHalfOpen=!1,this.once("end",f)))}function f(){this._writableState.ended||t.nextTick(l,this)}function l(e){e.end()}Object.defineProperty(c.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),Object.defineProperty(c.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(c.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(c.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._readableState&&void 0!==this._writableState&&(this._readableState.destroyed&&this._writableState.destroyed)},set:function(e){void 0!==this._readableState&&void 0!==this._writableState&&(this._readableState.destroyed=e,this._writableState.destroyed=e)}})}).call(this,n(20))},function(e,t,n){var r=n(398),i=n(401);e.exports=function(e,t){var n=i(e,t);return r(n)?n:void 0}},function(e,t,n){"use strict";n.d(t,"b",(function(){return g})),n.d(t,"a",(function(){return m}));var r=n(44),i=n(33),o=n(514),s=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},a=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},u=new r.a("CognitoCredentials"),c=new Promise((function(e,t){return Object(i.b)().isBrowser?(window.gapi&&window.gapi.auth2?window.gapi.auth2:null)?(u.debug("google api already loaded"),e()):void setTimeout((function(){return e()}),2e3):(u.debug("not in the browser, directly resolved"),e())})),f=function(){function e(){this.initialized=!1,this.refreshGoogleToken=this.refreshGoogleToken.bind(this),this._refreshGoogleTokenImpl=this._refreshGoogleTokenImpl.bind(this)}return e.prototype.refreshGoogleToken=function(){return s(this,void 0,void 0,(function(){return a(this,(function(e){switch(e.label){case 0:return this.initialized?[3,2]:(u.debug("need to wait for the Google SDK loaded"),[4,c]);case 1:e.sent(),this.initialized=!0,u.debug("finish waiting"),e.label=2;case 2:return[2,this._refreshGoogleTokenImpl()]}}))}))},e.prototype._refreshGoogleTokenImpl=function(){var e=null;return Object(i.b)().isBrowser&&(e=window.gapi&&window.gapi.auth2?window.gapi.auth2:null),e?new Promise((function(t,n){e.getAuthInstance().then((function(e){e||(u.debug("google Auth undefined"),n(new o.a("google Auth undefined")));var r=e.currentUser.get();r.isSignedIn()?(u.debug("refreshing the google access token"),r.reloadAuthResponse().then((function(e){var n=e.id_token,r=e.expires_at;t({token:n,expires_at:r})})).catch((function(e){e&&"network_error"===e.error?n("Network error reloading google auth response"):n(new o.a("Failed to reload google auth response"))}))):n(new o.a("User is not signed in with Google"))})).catch((function(e){u.debug("Failed to refresh google token",e),n(new o.a("Failed to refresh google token"))}))})):(u.debug("no gapi auth2 available"),Promise.reject("no gapi auth2 available"))},e}(),l=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},d=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},h=new r.a("CognitoCredentials"),p=new Promise((function(e,t){return Object(i.b)().isBrowser?window.FB?(h.debug("FB SDK already loaded"),e()):void setTimeout((function(){return e()}),2e3):(h.debug("not in the browser, directly resolved"),e())})),v=function(){function e(){this.initialized=!1,this.refreshFacebookToken=this.refreshFacebookToken.bind(this),this._refreshFacebookTokenImpl=this._refreshFacebookTokenImpl.bind(this)}return e.prototype.refreshFacebookToken=function(){return l(this,void 0,void 0,(function(){return d(this,(function(e){switch(e.label){case 0:return this.initialized?[3,2]:(h.debug("need to wait for the Facebook SDK loaded"),[4,p]);case 1:e.sent(),this.initialized=!0,h.debug("finish waiting"),e.label=2;case 2:return[2,this._refreshFacebookTokenImpl()]}}))}))},e.prototype._refreshFacebookTokenImpl=function(){var e=null;if(Object(i.b)().isBrowser&&(e=window.FB),!e){return h.debug("no fb sdk available"),Promise.reject(new o.a("no fb sdk available"))}return new Promise((function(t,n){e.getLoginStatus((function(e){if(e&&e.authResponse){var r=e.authResponse,i=r.accessToken,s=1e3*r.expiresIn+(new Date).getTime();if(!i){a="the jwtToken is undefined";h.debug(a),n(new o.a(a))}t({token:i,expires_at:s})}else{var a="no response from facebook when refreshing the jwt token";h.debug(a),n(new o.a(a))}}),{scope:"public_profile,email"})}))},e}(),g=new f,m=new v},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(1),i=n(55);function o(e){var t,n,o=[];try{for(var s=Object(r.__values)(Object.keys(e).sort()),a=s.next();!a.done;a=s.next()){var u=a.value,c=e[u];if(u=Object(i.a)(u),Array.isArray(c))for(var f=0,l=c.length;f<l;f++)o.push(u+"="+Object(i.a)(c[f]));else{var d=u;(c||"string"==typeof c)&&(d+="="+Object(i.a)(c)),o.push(d)}}}catch(e){t={error:e}}finally{try{a&&!a.done&&(n=s.return)&&n.call(s)}finally{if(t)throw t.error}}return o.join("&")}},function(e,t,n){var r;e.exports=(r=n(32),n(87),n(271),r.HmacSHA256)},function(e,t,n){"use strict"; -/*! - * cookie - * Copyright(c) 2012-2014 Roman Shtylman - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - */t.parse=function(e,t){if("string"!=typeof e)throw new TypeError("argument str must be a string");for(var n={},i=t||{},s=e.split(o),u=i.decode||r,c=0;c<s.length;c++){var f=s[c],l=f.indexOf("=");if(!(l<0)){var d=f.substr(0,l).trim(),h=f.substr(++l,f.length).trim();'"'==h[0]&&(h=h.slice(1,-1)),null==n[d]&&(n[d]=a(h,u))}}return n},t.serialize=function(e,t,n){var r=n||{},o=r.encode||i;if("function"!=typeof o)throw new TypeError("option encode is invalid");if(!s.test(e))throw new TypeError("argument name is invalid");var a=o(t);if(a&&!s.test(a))throw new TypeError("argument val is invalid");var u=e+"="+a;if(null!=r.maxAge){var c=r.maxAge-0;if(isNaN(c)||!isFinite(c))throw new TypeError("option maxAge is invalid");u+="; Max-Age="+Math.floor(c)}if(r.domain){if(!s.test(r.domain))throw new TypeError("option domain is invalid");u+="; Domain="+r.domain}if(r.path){if(!s.test(r.path))throw new TypeError("option path is invalid");u+="; Path="+r.path}if(r.expires){if("function"!=typeof r.expires.toUTCString)throw new TypeError("option expires is invalid");u+="; Expires="+r.expires.toUTCString()}r.httpOnly&&(u+="; HttpOnly");r.secure&&(u+="; Secure");if(r.sameSite){switch("string"==typeof r.sameSite?r.sameSite.toLowerCase():r.sameSite){case!0:u+="; SameSite=Strict";break;case"lax":u+="; SameSite=Lax";break;case"strict":u+="; SameSite=Strict";break;case"none":u+="; SameSite=None";break;default:throw new TypeError("option sameSite is invalid")}}return u};var r=decodeURIComponent,i=encodeURIComponent,o=/; */,s=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;function a(e,t){try{return t(e)}catch(t){return e}}},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},i={clockOffset:0,getDateWithClockOffset:function(){return i.clockOffset?new Date((new Date).getTime()+i.clockOffset):new Date},getClockOffset:function(){return i.clockOffset},getHeaderStringFromDate:function(e){return void 0===e&&(e=i.getDateWithClockOffset()),e.toISOString().replace(/[:\-]|\.\d{3}/g,"")},getDateFromHeaderString:function(e){var t=r(e.match(/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2}).+/),7),n=t[1],i=t[2],o=t[3],s=t[4],a=t[5],u=t[6];return new Date(Date.UTC(Number(n),Number(i)-1,Number(o),Number(s),Number(a),Number(u)))},isClockSkewed:function(e){return Math.abs(e.getTime()-i.getDateWithClockOffset().getTime())>=3e5},isClockSkewError:function(e){if(!e.response||!e.response.headers)return!1;var t=e.response.headers;return Boolean("BadRequestException"===t["x-amzn-errortype"]&&(t.date||t.Date))},setClockOffset:function(e){i.clockOffset=e}}},,function(e,t,n){"use strict";var r=n(7),i=n(113),o=n(116),s=n(117),a=n(56);function u(e){a.call(this,"digest"),this._hash=e}r(u,a),u.prototype._update=function(e){this._hash.update(e)},u.prototype._final=function(){return this._hash.digest()},e.exports=function(e){return"md5"===(e=e.toLowerCase())?new i:"rmd160"===e||"ripemd160"===e?new o:new u(s(e))}},function(e,t,n){(function(e){function n(e){return Object.prototype.toString.call(e)}t.isArray=function(e){return Array.isArray?Array.isArray(e):"[object Array]"===n(e)},t.isBoolean=function(e){return"boolean"==typeof e},t.isNull=function(e){return null===e},t.isNullOrUndefined=function(e){return null==e},t.isNumber=function(e){return"number"==typeof e},t.isString=function(e){return"string"==typeof e},t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=function(e){return void 0===e},t.isRegExp=function(e){return"[object RegExp]"===n(e)},t.isObject=function(e){return"object"==typeof e&&null!==e},t.isDate=function(e){return"[object Date]"===n(e)},t.isError=function(e){return"[object Error]"===n(e)||e instanceof Error},t.isFunction=function(e){return"function"==typeof e},t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=e.isBuffer}).call(this,n(6).Buffer)},function(e,t,n){(function(t){e.exports=function(e,n){for(var r=Math.min(e.length,n.length),i=new t(r),o=0;o<r;++o)i[o]=e[o]^n[o];return i}}).call(this,n(6).Buffer)},function(e,t,n){"use strict";var r=n(51),i=n(46);function o(){this.pending=null,this.pendingTotal=0,this.blockSize=this.constructor.blockSize,this.outSize=this.constructor.outSize,this.hmacStrength=this.constructor.hmacStrength,this.padLength=this.constructor.padLength/8,this.endian="big",this._delta8=this.blockSize/8,this._delta32=this.blockSize/32}t.BlockHash=o,o.prototype.update=function(e,t){if(e=r.toArray(e,t),this.pending?this.pending=this.pending.concat(e):this.pending=e,this.pendingTotal+=e.length,this.pending.length>=this._delta8){var n=(e=this.pending).length%this._delta8;this.pending=e.slice(e.length-n,e.length),0===this.pending.length&&(this.pending=null),e=r.join32(e,0,e.length-n,this.endian);for(var i=0;i<e.length;i+=this._delta32)this._update(e,i,i+this._delta32)}return this},o.prototype.digest=function(e){return this.update(this._pad()),i(null===this.pending),this._digest(e)},o.prototype._pad=function(){var e=this.pendingTotal,t=this._delta8,n=t-(e+this.padLength)%t,r=new Array(n+this.padLength);r[0]=128;for(var i=1;i<n;i++)r[i]=0;if(e<<=3,"big"===this.endian){for(var o=8;o<this.padLength;o++)r[i++]=0;r[i++]=0,r[i++]=0,r[i++]=0,r[i++]=0,r[i++]=e>>>24&255,r[i++]=e>>>16&255,r[i++]=e>>>8&255,r[i++]=255&e}else for(r[i++]=255&e,r[i++]=e>>>8&255,r[i++]=e>>>16&255,r[i++]=e>>>24&255,r[i++]=0,r[i++]=0,r[i++]=0,r[i++]=0,o=8;o<this.padLength;o++)r[i++]=0;return r}},function(e,t,n){"use strict";const r=n(7),i=n(132).Reporter,o=n(130).Buffer;function s(e,t){i.call(this,t),o.isBuffer(e)?(this.base=e,this.offset=0,this.length=e.length):this.error("Input not Buffer")}function a(e,t){if(Array.isArray(e))this.length=0,this.value=e.map((function(e){return a.isEncoderBuffer(e)||(e=new a(e,t)),this.length+=e.length,e}),this);else if("number"==typeof e){if(!(0<=e&&e<=255))return t.error("non-byte EncoderBuffer value");this.value=e,this.length=1}else if("string"==typeof e)this.value=e,this.length=o.byteLength(e);else{if(!o.isBuffer(e))return t.error("Unsupported type: "+typeof e);this.value=e,this.length=e.length}}r(s,i),t.DecoderBuffer=s,s.isDecoderBuffer=function(e){if(e instanceof s)return!0;return"object"==typeof e&&o.isBuffer(e.base)&&"DecoderBuffer"===e.constructor.name&&"number"==typeof e.offset&&"number"==typeof e.length&&"function"==typeof e.save&&"function"==typeof e.restore&&"function"==typeof e.isEmpty&&"function"==typeof e.readUInt8&&"function"==typeof e.skip&&"function"==typeof e.raw},s.prototype.save=function(){return{offset:this.offset,reporter:i.prototype.save.call(this)}},s.prototype.restore=function(e){const t=new s(this.base);return t.offset=e.offset,t.length=this.offset,this.offset=e.offset,i.prototype.restore.call(this,e.reporter),t},s.prototype.isEmpty=function(){return this.offset===this.length},s.prototype.readUInt8=function(e){return this.offset+1<=this.length?this.base.readUInt8(this.offset++,!0):this.error(e||"DecoderBuffer overrun")},s.prototype.skip=function(e,t){if(!(this.offset+e<=this.length))return this.error(t||"DecoderBuffer overrun");const n=new s(this.base);return n._reporterState=this._reporterState,n.offset=this.offset,n.length=this.offset+e,this.offset+=e,n},s.prototype.raw=function(e){return this.base.slice(e?e.offset:this.offset,this.length)},t.EncoderBuffer=a,a.isEncoderBuffer=function(e){if(e instanceof a)return!0;return"object"==typeof e&&"EncoderBuffer"===e.constructor.name&&"number"==typeof e.length&&"function"==typeof e.join},a.prototype.join=function(e,t){return e||(e=o.alloc(this.length)),t||(t=0),0===this.length||(Array.isArray(this.value)?this.value.forEach((function(n){n.join(e,t),t+=n.length})):("number"==typeof this.value?e[t]=this.value:"string"==typeof this.value?e.write(this.value,t):o.isBuffer(this.value)&&this.value.copy(e,t),t+=this.length)),e}},function(e,t,n){var r=n(97),i=n(390),o=n(391),s=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":s&&s in Object(e)?i(e):o(e)}},function(e,t){e.exports=function(e){return null!=e&&"object"==typeof e}},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r={},i=function(){function e(){}return e.setItem=function(e,t){return r[e]=t,r[e]},e.getItem=function(e){return Object.prototype.hasOwnProperty.call(r,e)?r[e]:void 0},e.removeItem=function(e){return delete r[e]},e.clear=function(){return r={}},e}(),o=function(){function e(){try{this.storageWindow=window.localStorage,this.storageWindow.setItem("aws.amplify.test-ls",1),this.storageWindow.removeItem("aws.amplify.test-ls")}catch(e){this.storageWindow=i}}return e.prototype.getStorage=function(){return this.storageWindow},e}()},function(e,t,n){var r;e.exports=(r=n(32),function(e){var t=r,n=t.lib,i=n.WordArray,o=n.Hasher,s=t.algo,a=[],u=[];!function(){function t(t){for(var n=e.sqrt(t),r=2;r<=n;r++)if(!(t%r))return!1;return!0}function n(e){return 4294967296*(e-(0|e))|0}for(var r=2,i=0;i<64;)t(r)&&(i<8&&(a[i]=n(e.pow(r,.5))),u[i]=n(e.pow(r,1/3)),i++),r++}();var c=[],f=s.SHA256=o.extend({_doReset:function(){this._hash=new i.init(a.slice(0))},_doProcessBlock:function(e,t){for(var n=this._hash.words,r=n[0],i=n[1],o=n[2],s=n[3],a=n[4],f=n[5],l=n[6],d=n[7],h=0;h<64;h++){if(h<16)c[h]=0|e[t+h];else{var p=c[h-15],v=(p<<25|p>>>7)^(p<<14|p>>>18)^p>>>3,g=c[h-2],m=(g<<15|g>>>17)^(g<<13|g>>>19)^g>>>10;c[h]=v+c[h-7]+m+c[h-16]}var b=r&i^r&o^i&o,y=(r<<30|r>>>2)^(r<<19|r>>>13)^(r<<10|r>>>22),w=d+((a<<26|a>>>6)^(a<<21|a>>>11)^(a<<7|a>>>25))+(a&f^~a&l)+u[h]+c[h];d=l,l=f,f=a,a=s+w|0,s=o,o=i,i=r,r=w+(y+b)|0}n[0]=n[0]+r|0,n[1]=n[1]+i|0,n[2]=n[2]+o|0,n[3]=n[3]+s|0,n[4]=n[4]+a|0,n[5]=n[5]+f|0,n[6]=n[6]+l|0,n[7]=n[7]+d|0},_doFinalize:function(){var t=this._data,n=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return n[i>>>5]|=128<<24-i%32,n[14+(i+64>>>9<<4)]=e.floor(r/4294967296),n[15+(i+64>>>9<<4)]=r,t.sigBytes=4*n.length,this._process(),this._hash},clone:function(){var e=o.clone.call(this);return e._hash=this._hash.clone(),e}});t.SHA256=o._createHelper(f),t.HmacSHA256=o._createHmacHelper(f)}(Math),r.SHA256)},function(e,t,n){"use strict";n.d(t,"a",(function(){return c}));var r=n(44),i=function(){return(i=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},o=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},s=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(o(arguments[t]));return e},a=new r.a("Hub"),u="undefined"!=typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("amplify_default"):"@@amplify_default";var c=new(function(){function e(e){this.listeners=[],this.patterns=[],this.protectedChannels=["core","auth","api","analytics","interactions","pubsub","storage","xr"],this.name=e}return e.prototype.remove=function(e,t){if(e instanceof RegExp){var n=this.patterns.find((function(t){return t.pattern.source===e.source}));if(!n)return void a.warn("No listeners for "+e);this.patterns=s(this.patterns.filter((function(e){return e!==n})))}else{var r=this.listeners[e];if(!r)return void a.warn("No listeners for "+e);this.listeners[e]=s(r.filter((function(e){return e.callback!==t})))}},e.prototype.dispatch=function(e,t,n,r){(void 0===n&&(n=""),this.protectedChannels.indexOf(e)>-1)&&(r===u||a.warn("WARNING: "+e+" is protected and dispatching on it can have unintended consequences"));var o={channel:e,payload:i({},t),source:n,patternInfo:[]};try{this._toListeners(o)}catch(e){a.error(e)}},e.prototype.listen=function(e,t,n){var r,i=this;if(void 0===n&&(n="noname"),function(e){return void 0!==e.onHubCapsule}(t))a.warn("WARNING onHubCapsule is Deprecated. Please pass in a callback."),r=t.onHubCapsule.bind(t);else{if("function"!=typeof t)throw new Error("No callback supplied to Hub");r=t}if(e instanceof RegExp)this.patterns.push({pattern:e,callback:r});else{var o=this.listeners[e];o||(o=[],this.listeners[e]=o),o.push({name:n,callback:r})}return function(){i.remove(e,r)}},e.prototype._toListeners=function(e){var t=e.channel,n=e.payload,r=this.listeners[t];if(r&&r.forEach((function(r){a.debug("Dispatching to "+t+" with ",n);try{r.callback(e)}catch(e){a.error(e)}})),this.patterns.length>0){if(!n.message)return void a.warn("Cannot perform pattern matching without a message key");var s=n.message;this.patterns.forEach((function(t){var n=s.match(t.pattern);if(n){var r=o(n).slice(1),u=i(i({},e),{patternInfo:r});try{t.callback(u)}catch(e){a.error(e)}}}))}},e}())("__default__")},function(e,t,n){"use strict";n.d(t,"a",(function(){return Lt}));var r=n(44),i=n(86),o=n(33),s=n(73),a=n(514),u=n(50),c=n(19),f=n(1),l=function(e,t){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function d(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}l(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var h=function(){return(h=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function p(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function v(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}Object.create;function g(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s}var m,b,y,w,_,S,E,M,A,I,k,O,x,C,T,P,N,R,L,j,D,U,B,F,z,q,K,H,V,G,W,$,Y,J,Z,X,Q,ee,te,ne,re,ie,oe,se,ae,ue,ce,fe,le,de,he,pe,ve,ge,me,be,ye;Object.create;!function(e){e.AUTHENTICATED_ROLE="AuthenticatedRole",e.DENY="Deny"}(m||(m={})),(b||(b={})).filterSensitiveLog=function(e){return h({},e)},(y||(y={})).filterSensitiveLog=function(e){return h({},e)},(w||(w={})).filterSensitiveLog=function(e){return h({},e)},(_||(_={})).filterSensitiveLog=function(e){return h({},e)},(S||(S={})).filterSensitiveLog=function(e){return h({},e)},(E||(E={})).filterSensitiveLog=function(e){return h({},e)},(M||(M={})).filterSensitiveLog=function(e){return h({},e)},(A||(A={})).filterSensitiveLog=function(e){return h({},e)},(I||(I={})).filterSensitiveLog=function(e){return h({},e)},(k||(k={})).filterSensitiveLog=function(e){return h({},e)},function(e){e.ACCESS_DENIED="AccessDenied",e.INTERNAL_SERVER_ERROR="InternalServerError"}(O||(O={})),(x||(x={})).filterSensitiveLog=function(e){return h({},e)},(C||(C={})).filterSensitiveLog=function(e){return h({},e)},(T||(T={})).filterSensitiveLog=function(e){return h({},e)},(P||(P={})).filterSensitiveLog=function(e){return h({},e)},(N||(N={})).filterSensitiveLog=function(e){return h({},e)},(R||(R={})).filterSensitiveLog=function(e){return h({},e)},(L||(L={})).filterSensitiveLog=function(e){return h({},e)},(j||(j={})).filterSensitiveLog=function(e){return h({},e)},(D||(D={})).filterSensitiveLog=function(e){return h({},e)},(U||(U={})).filterSensitiveLog=function(e){return h({},e)},(B||(B={})).filterSensitiveLog=function(e){return h({},e)},(F||(F={})).filterSensitiveLog=function(e){return h({},e)},(z||(z={})).filterSensitiveLog=function(e){return h({},e)},(q||(q={})).filterSensitiveLog=function(e){return h({},e)},(K||(K={})).filterSensitiveLog=function(e){return h({},e)},function(e){e.CONTAINS="Contains",e.EQUALS="Equals",e.NOT_EQUAL="NotEqual",e.STARTS_WITH="StartsWith"}(H||(H={})),(V||(V={})).filterSensitiveLog=function(e){return h({},e)},(G||(G={})).filterSensitiveLog=function(e){return h({},e)},function(e){e.RULES="Rules",e.TOKEN="Token"}(W||(W={})),($||($={})).filterSensitiveLog=function(e){return h({},e)},(Y||(Y={})).filterSensitiveLog=function(e){return h({},e)},(J||(J={})).filterSensitiveLog=function(e){return h({},e)},(Z||(Z={})).filterSensitiveLog=function(e){return h({},e)},(X||(X={})).filterSensitiveLog=function(e){return h({},e)},(Q||(Q={})).filterSensitiveLog=function(e){return h({},e)},(ee||(ee={})).filterSensitiveLog=function(e){return h({},e)},(te||(te={})).filterSensitiveLog=function(e){return h({},e)},(ne||(ne={})).filterSensitiveLog=function(e){return h({},e)},(re||(re={})).filterSensitiveLog=function(e){return h({},e)},(ie||(ie={})).filterSensitiveLog=function(e){return h({},e)},(oe||(oe={})).filterSensitiveLog=function(e){return h({},e)},(se||(se={})).filterSensitiveLog=function(e){return h({},e)},(ae||(ae={})).filterSensitiveLog=function(e){return h({},e)},(ue||(ue={})).filterSensitiveLog=function(e){return h({},e)},(ce||(ce={})).filterSensitiveLog=function(e){return h({},e)},(fe||(fe={})).filterSensitiveLog=function(e){return h({},e)},(le||(le={})).filterSensitiveLog=function(e){return h({},e)},(de||(de={})).filterSensitiveLog=function(e){return h({},e)},(he||(he={})).filterSensitiveLog=function(e){return h({},e)},(pe||(pe={})).filterSensitiveLog=function(e){return h({},e)},(ve||(ve={})).filterSensitiveLog=function(e){return h({},e)},(ge||(ge={})).filterSensitiveLog=function(e){return h({},e)},(me||(me={})).filterSensitiveLog=function(e){return h({},e)},(be||(be={})).filterSensitiveLog=function(e){return h({},e)},(ye||(ye={})).filterSensitiveLog=function(e){return h({},e)};var we=n(2),_e=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d,p,g,m,b,y;return v(this,(function(v){switch(v.label){case 0:return r=[h({},e)],y={},[4,Je(e.body,t)];case 1:switch(n=h.apply(void 0,r.concat([(y.body=v.sent(),y)])),o="UnknownError",s=n.body.__type.split("#"),o=void 0===s[1]?s[0]:s[1],o){case"ExternalServiceException":case"com.amazonaws.cognitoidentity#ExternalServiceException":return[3,2];case"InternalErrorException":case"com.amazonaws.cognitoidentity#InternalErrorException":return[3,4];case"InvalidIdentityPoolConfigurationException":case"com.amazonaws.cognitoidentity#InvalidIdentityPoolConfigurationException":return[3,6];case"InvalidParameterException":case"com.amazonaws.cognitoidentity#InvalidParameterException":return[3,8];case"NotAuthorizedException":case"com.amazonaws.cognitoidentity#NotAuthorizedException":return[3,10];case"ResourceConflictException":case"com.amazonaws.cognitoidentity#ResourceConflictException":return[3,12];case"ResourceNotFoundException":case"com.amazonaws.cognitoidentity#ResourceNotFoundException":return[3,14];case"TooManyRequestsException":case"com.amazonaws.cognitoidentity#TooManyRequestsException":return[3,16]}return[3,18];case 2:return a=[{}],[4,Ee(n,t)];case 3:return i=h.apply(void 0,[h.apply(void 0,a.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 4:return u=[{}],[4,Me(n,t)];case 5:return i=h.apply(void 0,[h.apply(void 0,u.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 6:return c=[{}],[4,Ae(n,t)];case 7:return i=h.apply(void 0,[h.apply(void 0,c.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 8:return f=[{}],[4,Ie(n,t)];case 9:return i=h.apply(void 0,[h.apply(void 0,f.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 10:return l=[{}],[4,Oe(n,t)];case 11:return i=h.apply(void 0,[h.apply(void 0,l.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 12:return d=[{}],[4,xe(n,t)];case 13:return i=h.apply(void 0,[h.apply(void 0,d.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 14:return p=[{}],[4,Ce(n,t)];case 15:return i=h.apply(void 0,[h.apply(void 0,p.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 16:return g=[{}],[4,Te(n,t)];case 17:return i=h.apply(void 0,[h.apply(void 0,g.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 18:m=n.body,o=m.code||m.Code||o,i=h(h({},m),{name:""+o,message:m.message||m.Message||o,$fault:"client",$metadata:We(e)}),v.label=19;case 19:return b=i.message||i.Message||o,i.message=b,delete i.Message,[2,Promise.reject(Object.assign(new Error(b),i))]}}))}))},Se=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d,p,g,m,b,y;return v(this,(function(v){switch(v.label){case 0:return r=[h({},e)],y={},[4,Je(e.body,t)];case 1:switch(n=h.apply(void 0,r.concat([(y.body=v.sent(),y)])),o="UnknownError",s=n.body.__type.split("#"),o=void 0===s[1]?s[0]:s[1],o){case"ExternalServiceException":case"com.amazonaws.cognitoidentity#ExternalServiceException":return[3,2];case"InternalErrorException":case"com.amazonaws.cognitoidentity#InternalErrorException":return[3,4];case"InvalidParameterException":case"com.amazonaws.cognitoidentity#InvalidParameterException":return[3,6];case"LimitExceededException":case"com.amazonaws.cognitoidentity#LimitExceededException":return[3,8];case"NotAuthorizedException":case"com.amazonaws.cognitoidentity#NotAuthorizedException":return[3,10];case"ResourceConflictException":case"com.amazonaws.cognitoidentity#ResourceConflictException":return[3,12];case"ResourceNotFoundException":case"com.amazonaws.cognitoidentity#ResourceNotFoundException":return[3,14];case"TooManyRequestsException":case"com.amazonaws.cognitoidentity#TooManyRequestsException":return[3,16]}return[3,18];case 2:return a=[{}],[4,Ee(n,t)];case 3:return i=h.apply(void 0,[h.apply(void 0,a.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 4:return u=[{}],[4,Me(n,t)];case 5:return i=h.apply(void 0,[h.apply(void 0,u.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 6:return c=[{}],[4,Ie(n,t)];case 7:return i=h.apply(void 0,[h.apply(void 0,c.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 8:return f=[{}],[4,ke(n,t)];case 9:return i=h.apply(void 0,[h.apply(void 0,f.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 10:return l=[{}],[4,Oe(n,t)];case 11:return i=h.apply(void 0,[h.apply(void 0,l.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 12:return d=[{}],[4,xe(n,t)];case 13:return i=h.apply(void 0,[h.apply(void 0,d.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 14:return p=[{}],[4,Ce(n,t)];case 15:return i=h.apply(void 0,[h.apply(void 0,p.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 16:return g=[{}],[4,Te(n,t)];case 17:return i=h.apply(void 0,[h.apply(void 0,g.concat([v.sent()])),{name:o,$metadata:We(e)}]),[3,19];case 18:m=n.body,o=m.code||m.Code||o,i=h(h({},m),{name:""+o,message:m.message||m.Message||o,$fault:"client",$metadata:We(e)}),v.label=19;case 19:return b=i.message||i.Message||o,i.message=b,delete i.Message,[2,Promise.reject(Object.assign(new Error(b),i))]}}))}))},Ee=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=je(n,t),[2,h({name:"ExternalServiceException",$fault:"client",$metadata:We(e)},r)]}))}))},Me=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=Be(n,t),[2,h({name:"InternalErrorException",$fault:"server",$metadata:We(e)},r)]}))}))},Ae=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=Fe(n,t),[2,h({name:"InvalidIdentityPoolConfigurationException",$fault:"client",$metadata:We(e)},r)]}))}))},Ie=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=ze(n,t),[2,h({name:"InvalidParameterException",$fault:"client",$metadata:We(e)},r)]}))}))},ke=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=qe(n,t),[2,h({name:"LimitExceededException",$fault:"client",$metadata:We(e)},r)]}))}))},Oe=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=Ke(n,t),[2,h({name:"NotAuthorizedException",$fault:"client",$metadata:We(e)},r)]}))}))},xe=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=He(n,t),[2,h({name:"ResourceConflictException",$fault:"client",$metadata:We(e)},r)]}))}))},Ce=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=Ve(n,t),[2,h({name:"ResourceNotFoundException",$fault:"client",$metadata:We(e)},r)]}))}))},Te=function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n=e.body,r=Ge(n,t),[2,h({name:"TooManyRequestsException",$fault:"client",$metadata:We(e)},r)]}))}))},Pe=function(e,t){return h(h(h({},void 0!==e.CustomRoleArn&&{CustomRoleArn:e.CustomRoleArn}),void 0!==e.IdentityId&&{IdentityId:e.IdentityId}),void 0!==e.Logins&&{Logins:Re(e.Logins,t)})},Ne=function(e,t){return h(h(h({},void 0!==e.AccountId&&{AccountId:e.AccountId}),void 0!==e.IdentityPoolId&&{IdentityPoolId:e.IdentityPoolId}),void 0!==e.Logins&&{Logins:Re(e.Logins,t)})},Re=function(e,t){return Object.entries(e).reduce((function(e,t){var n,r=g(t,2),i=r[0],o=r[1];return h(h({},e),((n={})[i]=o,n))}),{})},Le=function(e,t){return{AccessKeyId:void 0!==e.AccessKeyId&&null!==e.AccessKeyId?e.AccessKeyId:void 0,Expiration:void 0!==e.Expiration&&null!==e.Expiration?new Date(Math.round(1e3*e.Expiration)):void 0,SecretKey:void 0!==e.SecretKey&&null!==e.SecretKey?e.SecretKey:void 0,SessionToken:void 0!==e.SessionToken&&null!==e.SessionToken?e.SessionToken:void 0}},je=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},De=function(e,t){return{Credentials:void 0!==e.Credentials&&null!==e.Credentials?Le(e.Credentials):void 0,IdentityId:void 0!==e.IdentityId&&null!==e.IdentityId?e.IdentityId:void 0}},Ue=function(e,t){return{IdentityId:void 0!==e.IdentityId&&null!==e.IdentityId?e.IdentityId:void 0}},Be=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},Fe=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},ze=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},qe=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},Ke=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},He=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},Ve=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},Ge=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},We=function(e){return{httpStatusCode:e.statusCode,httpHeaders:e.headers,requestId:e.headers["x-amzn-requestid"]}},$e=function(e,t){return void 0===e&&(e=new Uint8Array),e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array)},Ye=function(e,t,n,r,i){return p(void 0,void 0,void 0,(function(){var o,s,a,u,c,f;return v(this,(function(l){switch(l.label){case 0:return[4,e.endpoint()];case 1:return o=l.sent(),s=o.hostname,a=o.protocol,u=void 0===a?"https":a,c=o.port,f={protocol:u,hostname:s,port:c,method:"POST",path:n,headers:t},void 0!==r&&(f.hostname=r),void 0!==i&&(f.body=i),[2,new we.a(f)]}}))}))},Je=function(e,t){return function(e,t){return $e(e,t).then((function(e){return t.utf8Encoder(e)}))}(e,t).then((function(e){return e.length?JSON.parse(e):{}}))},Ze=n(10),Xe=n(0),Qe=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return d(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Ze.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"CognitoIdentityClient",commandName:"GetCredentialsForIdentityCommand",inputFilterSensitiveLog:D.filterSensitiveLog,outputFilterSensitiveLog:B.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"CognitoIdentityClient",commandName:"GetCredentialsForIdentityCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n={"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AWSCognitoIdentityService.GetCredentialsForIdentity"},r=JSON.stringify(Pe(e,t)),[2,Ye(t,n,"/",void 0,r)]}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return p(void 0,void 0,void 0,(function(){var n,r,i;return v(this,(function(o){switch(o.label){case 0:return e.statusCode>=300?[2,_e(e,t)]:[4,Je(e.body,t)];case 1:return n=o.sent(),{},r=De(n,t),i=h({$metadata:We(e)},r),[2,Promise.resolve(i)]}}))}))}(e,t)},t}(Xe.b),et=function(e){function t(t,n){void 0===n&&(n=!0);var r=e.call(this,t)||this;return r.tryNextLink=n,r}return Object(f.__extends)(t,e),t}(Error);function tt(e){return Promise.all(Object.keys(e).reduce((function(t,n){var r=e[n];return"string"==typeof r?t.push([n,r]):t.push(r().then((function(e){return[n,e]}))),t}),[])).then((function(e){return e.reduce((function(e,t){var n=Object(f.__read)(t,2),r=n[0],i=n[1];return e[r]=i,e}),{})}))}function nt(e){var t=this;return function(){return Object(f.__awaiter)(t,void 0,void 0,(function(){var t,n,r,i,o,s,a,u,c,l,d,h,p;return Object(f.__generator)(this,(function(f){switch(f.label){case 0:return l=(c=e.client).send,d=Qe.bind,p={CustomRoleArn:e.customRoleArn,IdentityId:e.identityId},e.logins?[4,tt(e.logins)]:[3,2];case 1:return h=f.sent(),[3,3];case 2:h=void 0,f.label=3;case 3:return[4,l.apply(c,[new(d.apply(Qe,[void 0,(p.Logins=h,p)]))])];case 4:return t=f.sent().Credentials,n=void 0===t?function(){throw new et("Response from Amazon Cognito contained no credentials")}():t,r=n.AccessKeyId,i=void 0===r?function(){throw new et("Response from Amazon Cognito contained no access key ID")}():r,o=n.Expiration,s=n.SecretKey,a=void 0===s?function(){throw new et("Response from Amazon Cognito contained no secret key")}():s,u=n.SessionToken,[2,{identityId:e.identityId,accessKeyId:i,secretAccessKey:a,sessionToken:u,expiration:o}]}}))}))}}var rt=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return d(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Ze.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"CognitoIdentityClient",commandName:"GetIdCommand",inputFilterSensitiveLog:z.filterSensitiveLog,outputFilterSensitiveLog:q.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"CognitoIdentityClient",commandName:"GetIdCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return p(void 0,void 0,void 0,(function(){var n,r;return v(this,(function(i){return n={"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AWSCognitoIdentityService.GetId"},r=JSON.stringify(Ne(e,t)),[2,Ye(t,n,"/",void 0,r)]}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return p(void 0,void 0,void 0,(function(){var n,r,i;return v(this,(function(o){switch(o.label){case 0:return e.statusCode>=300?[2,Se(e,t)]:[4,Je(e.body,t)];case 1:return n=o.sent(),{},r=Ue(n,t),i=h({$metadata:We(e)},r),[2,Promise.resolve(i)]}}))}))}(e,t)},t}(Xe.b),it=function(){function e(e){void 0===e&&(e="aws:cognito-identity-ids"),this.dbName=e}return e.prototype.getItem=function(e){return this.withObjectStore("readonly",(function(t){var n=t.get(e);return new Promise((function(e){n.onerror=function(){return e(null)},n.onsuccess=function(){return e(n.result?n.result.value:null)}}))})).catch((function(){return null}))},e.prototype.removeItem=function(e){return this.withObjectStore("readwrite",(function(t){var n=t.delete(e);return new Promise((function(e,t){n.onerror=function(){return t(n.error)},n.onsuccess=function(){return e()}}))}))},e.prototype.setItem=function(e,t){return this.withObjectStore("readwrite",(function(n){var r=n.put({id:e,value:t});return new Promise((function(e,t){r.onerror=function(){return t(r.error)},r.onsuccess=function(){return e()}}))}))},e.prototype.getDb=function(){var e=self.indexedDB.open(this.dbName,1);return new Promise((function(t,n){e.onsuccess=function(){t(e.result)},e.onerror=function(){n(e.error)},e.onblocked=function(){n(new Error("Unable to access DB"))},e.onupgradeneeded=function(){var t=e.result;t.onerror=function(){n(new Error("Failed to create object store"))},t.createObjectStore("IdentityIds",{keyPath:"id"})}}))},e.prototype.withObjectStore=function(e,t){return this.getDb().then((function(n){var r=n.transaction("IdentityIds",e);return r.oncomplete=function(){return n.close()},new Promise((function(e,n){r.onerror=function(){return n(r.error)},e(t(r.objectStore("IdentityIds")))})).catch((function(e){throw n.close(),e}))}))},e}(),ot=new(function(){function e(e){void 0===e&&(e={}),this.store=e}return e.prototype.getItem=function(e){return e in this.store?this.store[e]:null},e.prototype.removeItem=function(e){delete this.store[e]},e.prototype.setItem=function(e,t){this.store[e]=t},e}());function st(e){var t=this,n=e.accountId,r=e.cache,i=void 0===r?"object"==typeof self&&self.indexedDB?new it:"object"==typeof window&&window.localStorage?window.localStorage:ot:r,o=e.client,s=e.customRoleArn,a=e.identityPoolId,u=e.logins,c=e.userIdentifier,l=void 0===c?u&&0!==Object.keys(u).length?void 0:"ANONYMOUS":c,d=l?"aws:cognito-identity-credentials:"+a+":"+l:void 0,h=function(){return Object(f.__awaiter)(t,void 0,void 0,(function(){var e,t,r,c,l,p,v,g,m;return Object(f.__generator)(this,(function(f){switch(f.label){case 0:return(t=d)?[4,i.getItem(d)]:[3,2];case 1:t=f.sent(),f.label=2;case 2:return(e=t)?[3,7]:(p=(l=o).send,v=rt.bind,m={AccountId:n,IdentityPoolId:a},u?[4,tt(u)]:[3,4]);case 3:return g=f.sent(),[3,5];case 4:g=void 0,f.label=5;case 5:return[4,p.apply(l,[new(v.apply(rt,[void 0,(m.Logins=g,m)]))])];case 6:r=f.sent().IdentityId,c=void 0===r?function(){throw new et("Response from Amazon Cognito contained no identity ID")}():r,e=c,d&&Promise.resolve(i.setItem(d,e)).catch((function(){})),f.label=7;case 7:return[2,(h=nt({client:o,customRoleArn:s,logins:u,identityId:e}))()]}}))}))};return function(){return h().catch((function(e){return Object(f.__awaiter)(t,void 0,void 0,(function(){return Object(f.__generator)(this,(function(t){throw d&&Promise.resolve(i.removeItem(d)).catch((function(){})),e}))}))}))}}var at=n(147),ut=n(38),ct=n(18),ft=n(24),lt=n(11),dt=n(39),ht=n(17),pt=n(40),vt=n(41),gt=n(15),mt="cognito-identity.{region}.amazonaws.com",bt=new Set(["ap-east-1","ap-northeast-1","ap-northeast-2","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-north-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-west-1","us-west-2"]),yt=new Set(["cn-north-1","cn-northwest-1"]),wt=new Set(["us-iso-east-1"]),_t=new Set(["us-isob-east-1"]),St=new Set(["us-gov-east-1","us-gov-west-1"]),Et=h(h({},{apiVersion:"2014-06-30",disableHostPrefix:!1,logger:{},regionInfoProvider:function(e,t){var n=void 0;switch(e){case"ap-northeast-1":n={hostname:"cognito-identity.ap-northeast-1.amazonaws.com",partition:"aws"};break;case"ap-northeast-2":n={hostname:"cognito-identity.ap-northeast-2.amazonaws.com",partition:"aws"};break;case"ap-south-1":n={hostname:"cognito-identity.ap-south-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-1":n={hostname:"cognito-identity.ap-southeast-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-2":n={hostname:"cognito-identity.ap-southeast-2.amazonaws.com",partition:"aws"};break;case"ca-central-1":n={hostname:"cognito-identity.ca-central-1.amazonaws.com",partition:"aws"};break;case"cn-north-1":n={hostname:"cognito-identity.cn-north-1.amazonaws.com.cn",partition:"aws-cn"};break;case"eu-central-1":n={hostname:"cognito-identity.eu-central-1.amazonaws.com",partition:"aws"};break;case"eu-west-1":n={hostname:"cognito-identity.eu-west-1.amazonaws.com",partition:"aws"};break;case"eu-west-2":n={hostname:"cognito-identity.eu-west-2.amazonaws.com",partition:"aws"};break;case"us-east-1":n={hostname:"cognito-identity.us-east-1.amazonaws.com",partition:"aws"};break;case"us-east-2":n={hostname:"cognito-identity.us-east-2.amazonaws.com",partition:"aws"};break;case"us-west-2":n={hostname:"cognito-identity.us-west-2.amazonaws.com",partition:"aws"};break;default:bt.has(e)&&(n={hostname:mt.replace("{region}",e),partition:"aws"}),yt.has(e)&&(n={hostname:"cognito-identity.{region}.amazonaws.com.cn".replace("{region}",e),partition:"aws-cn"}),wt.has(e)&&(n={hostname:"cognito-identity.{region}.c2s.ic.gov".replace("{region}",e),partition:"aws-iso"}),_t.has(e)&&(n={hostname:"cognito-identity.{region}.sc2s.sgov.gov".replace("{region}",e),partition:"aws-iso-b"}),St.has(e)&&(n={hostname:"cognito-identity.{region}.amazonaws.com".replace("{region}",e),partition:"aws-us-gov"}),void 0===n&&(n={hostname:mt.replace("{region}",e),partition:"aws"})}return Promise.resolve(n)},signingName:"cognito-identity"}),{runtime:"browser",base64Decoder:ht.a,base64Encoder:ht.b,bodyLengthChecker:pt.a,credentialDefaultProvider:function(){},defaultUserAgent:Object(vt.a)(at.name,at.version),maxAttempts:lt.a,region:Object(ft.a)("Region is missing"),requestHandler:new ct.a,sha256:ut.Sha256,streamCollector:ct.b,urlParser:dt.a,utf8Decoder:gt.a,utf8Encoder:gt.b}),Mt=n(22),At=n(37),It=n(21),kt=n(43),Ot=n(25),xt=n(23),Ct=function(e){function t(t){var n=this,r=h(h({},Et),t),i=Object(Mt.b)(r),o=Object(Mt.a)(i),s=Object(Ot.b)(o),a=Object(lt.c)(s),u=Object(xt.b)(a),c=Object(It.b)(u);return(n=e.call(this,c)||this).config=c,n.middlewareStack.use(Object(lt.b)(n.config)),n.middlewareStack.use(Object(xt.a)(n.config)),n.middlewareStack.use(Object(At.a)(n.config)),n.middlewareStack.use(Object(It.a)(n.config)),n.middlewareStack.use(Object(kt.a)(n.config)),n}return d(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this)},t}(Xe.a),Tt=function(){return(Tt=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},Pt=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},Nt=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},Rt=new r.a("Credentials"),Lt=new(function(){function e(e){this._gettingCredPromise=null,this._refreshHandlers={},this.Auth=void 0,this.configure(e),this._refreshHandlers.google=s.b.refreshGoogleToken,this._refreshHandlers.facebook=s.a.refreshFacebookToken}return e.prototype.getModuleName=function(){return"Credentials"},e.prototype.getCredSource=function(){return this._credentials_source},e.prototype.configure=function(e){if(!e)return this._config||{};this._config=Object.assign({},this._config,e);var t=this._config.refreshHandlers;return t&&(this._refreshHandlers=Tt(Tt({},this._refreshHandlers),t)),this._storage=this._config.storage,this._storage||(this._storage=(new i.a).getStorage()),this._storageSync=Promise.resolve(),"function"==typeof this._storage.sync&&(this._storageSync=this._storage.sync()),this._config},e.prototype.get=function(){return Rt.debug("getting credentials"),this._pickupCredentials()},e.prototype._pickupCredentials=function(){return Rt.debug("picking up credentials"),this._gettingCredPromise&&this._gettingCredPromise.isPending()?Rt.debug("getting old cred promise"):(Rt.debug("getting new cred promise"),this._gettingCredPromise=Object(o.d)(this._keepAlive())),this._gettingCredPromise},e.prototype._keepAlive=function(){return Pt(this,void 0,void 0,(function(){var e,t,n,r,i,o,s;return Nt(this,(function(a){switch(a.label){case 0:if(Rt.debug("checking if credentials exists and not expired"),(e=this._credentials)&&!this._isExpired(e)&&!this._isPastTTL())return Rt.debug("credentials not changed and not expired, directly return"),[2,Promise.resolve(e)];if(Rt.debug("need to get a new credential or refresh the existing one"),t=this.Auth,!(n=void 0===t?c.a.Auth:t)||"function"!=typeof n.currentUserCredentials)return[2,Promise.reject("No Auth module registered in Amplify")];if(this._isExpired(e)||!this._isPastTTL())return[3,6];Rt.debug("ttl has passed but token is not yet expired"),a.label=1;case 1:return a.trys.push([1,5,,6]),[4,n.currentUserPoolUser()];case 2:return r=a.sent(),[4,n.currentSession()];case 3:return i=a.sent(),o=i.refreshToken,[4,new Promise((function(e,t){r.refreshSession(o,(function(n,r){return n?t(n):e(r)}))}))];case 4:return a.sent(),[3,6];case 5:return s=a.sent(),Rt.debug("Error attempting to refreshing the session",s),[3,6];case 6:return[2,n.currentUserCredentials()]}}))}))},e.prototype.refreshFederatedToken=function(e){Rt.debug("Getting federated credentials");var t=e.provider,n=e.user,r=e.token,i=e.identity_id,o=e.expires_at;o=1970===new Date(o).getFullYear()?1e3*o:o;return Rt.debug("checking if federated jwt token expired"),o>(new Date).getTime()?(Rt.debug("token not expired"),this._setCredentialsFromFederation({provider:t,token:r,user:n,identity_id:i,expires_at:o})):this._refreshHandlers[t]&&"function"==typeof this._refreshHandlers[t]?(Rt.debug("getting refreshed jwt token from federation provider"),this._providerRefreshWithRetry({refreshHandler:this._refreshHandlers[t],provider:t,user:n})):(Rt.debug("no refresh handler for provider:",t),this.clear(),Promise.reject("no refresh handler for provider"))},e.prototype._providerRefreshWithRetry=function(e){var t=this,n=e.refreshHandler,r=e.provider,i=e.user;return Object(a.b)(n,[],1e4).then((function(e){return Rt.debug("refresh federated token sucessfully",e),t._setCredentialsFromFederation({provider:r,token:e.token,user:i,identity_id:e.identity_id,expires_at:e.expires_at})})).catch((function(e){return"string"==typeof e&&0===e.toLowerCase().lastIndexOf("network error",e.length)||t.clear(),Rt.debug("refresh federated token failed",e),Promise.reject("refreshing federation token failed: "+e)}))},e.prototype._isExpired=function(e){if(!e)return Rt.debug("no credentials for expiration check"),!0;Rt.debug("are these credentials expired?",e);var t=Date.now();return e.expiration.getTime()<=t},e.prototype._isPastTTL=function(){return this._nextCredentialsRefresh<=Date.now()},e.prototype._setCredentialsForGuest=function(){return Pt(this,void 0,void 0,(function(){var e,t,n,r,i,o,s,a=this;return Nt(this,(function(c){switch(c.label){case 0:if(Rt.debug("setting credentials for guest"),e=this._config,t=e.identityPoolId,n=e.region,e.mandatorySignIn)return[2,Promise.reject("cannot get guest credentials when mandatory signin enabled")];if(!t)return Rt.debug("No Cognito Identity pool provided for unauthenticated access"),[2,Promise.reject("No Cognito Identity pool provided for unauthenticated access")];if(!n)return Rt.debug("region is not configured for getting the credentials"),[2,Promise.reject("region is not configured for getting the credentials")];r=void 0,c.label=1;case 1:return c.trys.push([1,3,,4]),[4,this._storageSync];case 2:return c.sent(),r=this._storage.getItem("CognitoIdentityId-"+t),this._identityId=r,[3,4];case 3:return i=c.sent(),Rt.debug("Failed to get the cached identityId",i),[3,4];case 4:return o=new Ct({region:n,customUserAgent:Object(u.b)()}),s=void 0,s=r?nt({identityId:r,client:o})():function(){return Pt(a,void 0,void 0,(function(){var e;return Nt(this,(function(n){switch(n.label){case 0:return[4,o.send(new rt({IdentityPoolId:t}))];case 1:return e=n.sent().IdentityId,this._identityId=e,[2,nt({client:o,identityId:e})()]}}))}))}().catch((function(e){return Pt(a,void 0,void 0,(function(){return Nt(this,(function(t){throw e}))}))})),[2,this._loadCredentials(s,"guest",!1,null).then((function(e){return e})).catch((function(e){return Pt(a,void 0,void 0,(function(){var n=this;return Nt(this,(function(i){return"ResourceNotFoundException"===e.name&&e.message==="Identity '"+r+"' not found."?(Rt.debug("Failed to load guest credentials"),this._storage.removeItem("CognitoIdentityId-"+t),s=function(){return Pt(n,void 0,void 0,(function(){var e;return Nt(this,(function(n){switch(n.label){case 0:return[4,o.send(new rt({IdentityPoolId:t}))];case 1:return e=n.sent().IdentityId,this._identityId=e,[2,nt({client:o,identityId:e})()]}}))}))}().catch((function(e){return Pt(n,void 0,void 0,(function(){return Nt(this,(function(t){throw e}))}))})),[2,this._loadCredentials(s,"guest",!1,null)]):[2,e]}))}))}))]}}))}))},e.prototype._setCredentialsFromFederation=function(e){var t=e.provider,n=e.token,r=e.identity_id,i={google:"accounts.google.com",facebook:"graph.facebook.com",amazon:"www.amazon.com",developer:"cognito-identity.amazonaws.com"}[t]||t;if(!i)return Promise.reject("You must specify a federated provider");var o={};o[i]=n;var s=this._config,a=s.identityPoolId,c=s.region;if(!a)return Rt.debug("No Cognito Federated Identity pool provided"),Promise.reject("No Cognito Federated Identity pool provided");if(!c)return Rt.debug("region is not configured for getting the credentials"),Promise.reject("region is not configured for getting the credentials");var f=new Ct({region:c,customUserAgent:Object(u.b)()}),l=void 0;r?l=nt({identityId:r,logins:o,client:f})():l=st({logins:o,identityPoolId:a,client:f})();return this._loadCredentials(l,"federated",!0,e)},e.prototype._setCredentialsFromSession=function(e){var t=this;Rt.debug("set credentials from session");var n=e.getIdToken().getJwtToken(),r=this._config,i=r.region,o=r.userPoolId,s=r.identityPoolId;if(!s)return Rt.debug("No Cognito Federated Identity pool provided"),Promise.reject("No Cognito Federated Identity pool provided");if(!i)return Rt.debug("region is not configured for getting the credentials"),Promise.reject("region is not configured for getting the credentials");var a={};a["cognito-idp."+i+".amazonaws.com/"+o]=n;var c=new Ct({region:i,customUserAgent:Object(u.b)()}),f=Pt(t,void 0,void 0,(function(){var e;return Nt(this,(function(t){switch(t.label){case 0:return[4,c.send(new rt({IdentityPoolId:s,Logins:a}))];case 1:return e=t.sent().IdentityId,this._identityId=e,[2,nt({client:c,logins:a,identityId:e})()]}}))})).catch((function(e){return Pt(t,void 0,void 0,(function(){return Nt(this,(function(t){throw e}))}))}));return this._loadCredentials(f,"userPool",!0,null)},e.prototype._loadCredentials=function(e,t,n,r){var i=this,o=this,s=this._config.identityPoolId;return new Promise((function(a,u){e.then((function(e){return Pt(i,void 0,void 0,(function(){var i,u,c,f,l,d;return Nt(this,(function(h){switch(h.label){case 0:if(Rt.debug("Load credentials successfully",e),this._identityId&&!e.identityId&&(e.identityId=this._identityId),o._credentials=e,o._credentials.authenticated=n,o._credentials_source=t,o._nextCredentialsRefresh=(new Date).getTime()+3e6,"federated"===t){i=Object.assign({id:this._credentials.identityId},r.user),u=r.provider,c=r.token,f=r.expires_at,l=r.identity_id;try{this._storage.setItem("aws-amplify-federatedInfo",JSON.stringify({provider:u,token:c,user:i,expires_at:f,identity_id:l}))}catch(e){Rt.debug("Failed to put federated info into auth storage",e)}}if("guest"!==t)return[3,4];h.label=1;case 1:return h.trys.push([1,3,,4]),[4,this._storageSync];case 2:return h.sent(),this._storage.setItem("CognitoIdentityId-"+s,e.identityId),[3,4];case 3:return d=h.sent(),Rt.debug("Failed to cache identityId",d),[3,4];case 4:return a(o._credentials),[2]}}))}))})).catch((function(t){if(t)return Rt.debug("Failed to load credentials",e),Rt.debug("Error loading credentials",t),void u(t)}))}))},e.prototype.set=function(e,t){return"session"===t?this._setCredentialsFromSession(e):"federation"===t?this._setCredentialsFromFederation(e):"guest"===t?this._setCredentialsForGuest():(Rt.debug("no source specified for setting credentials"),Promise.reject("invalid source"))},e.prototype.clear=function(){return Pt(this,void 0,void 0,(function(){return Nt(this,(function(e){return this._credentials=null,this._credentials_source=null,Rt.debug("removing aws-amplify-federatedInfo from storage"),this._storage.removeItem("aws-amplify-federatedInfo"),[2]}))}))},e.prototype.shear=function(e){return{accessKeyId:e.accessKeyId,sessionToken:e.sessionToken,secretAccessKey:e.secretAccessKey,identityId:e.identityId,authenticated:e.authenticated}},e}())(null);c.a.register(Lt)},function(e,t,n){var r,i,o;e.exports=(o=n(32),i=(r=o).lib.WordArray,r.enc.Base64={stringify:function(e){var t=e.words,n=e.sigBytes,r=this._map;e.clamp();for(var i=[],o=0;o<n;o+=3)for(var s=(t[o>>>2]>>>24-o%4*8&255)<<16|(t[o+1>>>2]>>>24-(o+1)%4*8&255)<<8|t[o+2>>>2]>>>24-(o+2)%4*8&255,a=0;a<4&&o+.75*a<n;a++)i.push(r.charAt(s>>>6*(3-a)&63));var u=r.charAt(64);if(u)for(;i.length%4;)i.push(u);return i.join("")},parse:function(e){var t=e.length,n=this._map,r=this._reverseMap;if(!r){r=this._reverseMap=[];for(var o=0;o<n.length;o++)r[n.charCodeAt(o)]=o}var s=n.charAt(64);if(s){var a=e.indexOf(s);-1!==a&&(t=a)}return function(e,t,n){for(var r=[],o=0,s=0;s<t;s++)if(s%4){var a=n[e.charCodeAt(s-1)]<<s%4*2,u=n[e.charCodeAt(s)]>>>6-s%4*2;r[o>>>2]|=(a|u)<<24-o%4*8,o++}return i.create(r,o)}(e,t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="},o.enc.Base64)},function(e,t,n){"use strict";function r(e,t){for(var n,r=/\r\n|[\n\r]/g,i=1,o=t+1;(n=r.exec(e.body))&&n.index<t;)i+=1,o=t+1-(n.index+n[0].length);return{line:i,column:o}}function i(e,t){var n=e.locationOffset.column-1,r=o(n)+e.body,i=t.line-1,s=e.locationOffset.line-1,a=t.line+s,u=1===t.line?n:0,c=t.column+u,f=r.split(/\r\n|[\n\r]/g);return"".concat(e.name," (").concat(a,":").concat(c,")\n")+function(e){var t=e.filter((function(e){e[0];return void 0!==e[1]})),n=0,r=!0,i=!1,s=void 0;try{for(var a,u=t[Symbol.iterator]();!(r=(a=u.next()).done);r=!0){var c=a.value[0];n=Math.max(n,c.length)}}catch(e){i=!0,s=e}finally{try{r||null==u.return||u.return()}finally{if(i)throw s}}return t.map((function(e){var t,r=e[0],i=e[1];return o(n-(t=r).length)+t+i})).join("\n")}([["".concat(a-1,": "),f[i-1]],["".concat(a,": "),f[i]],["",o(c-1)+"^"],["".concat(a+1,": "),f[i+1]]])}function o(e){return Array(e+1).join(" ")}function s(e,t,n,i,o,a,u){var c=Array.isArray(t)?0!==t.length?t:void 0:t?[t]:void 0,f=n;if(!f&&c){var l=c[0];f=l&&l.loc&&l.loc.source}var d,h=i;!h&&c&&(h=c.reduce((function(e,t){return t.loc&&e.push(t.loc.start),e}),[])),h&&0===h.length&&(h=void 0),i&&n?d=i.map((function(e){return r(n,e)})):c&&(d=c.reduce((function(e,t){return t.loc&&e.push(r(t.loc.source,t.loc.start)),e}),[]));var p=u||a&&a.extensions;Object.defineProperties(this,{message:{value:e,enumerable:!0,writable:!0},locations:{value:d||void 0,enumerable:Boolean(d)},path:{value:o||void 0,enumerable:Boolean(o)},nodes:{value:c||void 0},source:{value:f||void 0},positions:{value:h||void 0},originalError:{value:a},extensions:{value:p||void 0,enumerable:Boolean(p)}}),a&&a.stack?Object.defineProperty(this,"stack",{value:a.stack,writable:!0,configurable:!0}):Error.captureStackTrace?Error.captureStackTrace(this,s):Object.defineProperty(this,"stack",{value:Error().stack,writable:!0,configurable:!0})}n.d(t,"a",(function(){return s})),s.prototype=Object.create(Error.prototype,{constructor:{value:s},name:{value:"GraphQLError"},toString:{value:function(){return function(e){var t=[];if(e.nodes){var n=!0,o=!1,s=void 0;try{for(var a,u=e.nodes[Symbol.iterator]();!(n=(a=u.next()).done);n=!0){var c=a.value;c.loc&&t.push(i(c.loc.source,r(c.loc.source,c.loc.start)))}}catch(e){o=!0,s=e}finally{try{n||null==u.return||u.return()}finally{if(o)throw s}}}else if(e.source&&e.locations){var f=e.source,l=!0,d=!1,h=void 0;try{for(var p,v=e.locations[Symbol.iterator]();!(l=(p=v.next()).done);l=!0){var g=p.value;t.push(i(f,g))}}catch(e){d=!0,h=e}finally{try{l||null==v.return||v.return()}finally{if(d)throw h}}}return 0===t.length?e.message:[e.message].concat(t).join("\n\n")+"\n"}(this)}}})},function(e,t,n){"use strict";(function(t){void 0===t||!t.version||0===t.version.indexOf("v0.")||0===t.version.indexOf("v1.")&&0!==t.version.indexOf("v1.8.")?e.exports={nextTick:function(e,n,r,i){if("function"!=typeof e)throw new TypeError('"callback" argument must be a function');var o,s,a=arguments.length;switch(a){case 0:case 1:return t.nextTick(e);case 2:return t.nextTick((function(){e.call(null,n)}));case 3:return t.nextTick((function(){e.call(null,n,r)}));case 4:return t.nextTick((function(){e.call(null,n,r,i)}));default:for(o=new Array(a-1),s=0;s<o.length;)o[s++]=arguments[s];return t.nextTick((function(){e.apply(null,o)}))}}}:e.exports=t}).call(this,n(20))},function(e,t,n){var r=n(8).Buffer;function i(e){r.isBuffer(e)||(e=r.from(e));for(var t=e.length/4|0,n=new Array(t),i=0;i<t;i++)n[i]=e.readUInt32BE(4*i);return n}function o(e){for(;0<e.length;e++)e[0]=0}function s(e,t,n,r,i){for(var o,s,a,u,c=n[0],f=n[1],l=n[2],d=n[3],h=e[0]^t[0],p=e[1]^t[1],v=e[2]^t[2],g=e[3]^t[3],m=4,b=1;b<i;b++)o=c[h>>>24]^f[p>>>16&255]^l[v>>>8&255]^d[255&g]^t[m++],s=c[p>>>24]^f[v>>>16&255]^l[g>>>8&255]^d[255&h]^t[m++],a=c[v>>>24]^f[g>>>16&255]^l[h>>>8&255]^d[255&p]^t[m++],u=c[g>>>24]^f[h>>>16&255]^l[p>>>8&255]^d[255&v]^t[m++],h=o,p=s,v=a,g=u;return o=(r[h>>>24]<<24|r[p>>>16&255]<<16|r[v>>>8&255]<<8|r[255&g])^t[m++],s=(r[p>>>24]<<24|r[v>>>16&255]<<16|r[g>>>8&255]<<8|r[255&h])^t[m++],a=(r[v>>>24]<<24|r[g>>>16&255]<<16|r[h>>>8&255]<<8|r[255&p])^t[m++],u=(r[g>>>24]<<24|r[h>>>16&255]<<16|r[p>>>8&255]<<8|r[255&v])^t[m++],[o>>>=0,s>>>=0,a>>>=0,u>>>=0]}var a=[0,1,2,4,8,16,32,64,128,27,54],u=function(){for(var e=new Array(256),t=0;t<256;t++)e[t]=t<128?t<<1:t<<1^283;for(var n=[],r=[],i=[[],[],[],[]],o=[[],[],[],[]],s=0,a=0,u=0;u<256;++u){var c=a^a<<1^a<<2^a<<3^a<<4;c=c>>>8^255&c^99,n[s]=c,r[c]=s;var f=e[s],l=e[f],d=e[l],h=257*e[c]^16843008*c;i[0][s]=h<<24|h>>>8,i[1][s]=h<<16|h>>>16,i[2][s]=h<<8|h>>>24,i[3][s]=h,h=16843009*d^65537*l^257*f^16843008*s,o[0][c]=h<<24|h>>>8,o[1][c]=h<<16|h>>>16,o[2][c]=h<<8|h>>>24,o[3][c]=h,0===s?s=a=1:(s=f^e[e[e[d^f]]],a^=e[e[a]])}return{SBOX:n,INV_SBOX:r,SUB_MIX:i,INV_SUB_MIX:o}}();function c(e){this._key=i(e),this._reset()}c.blockSize=16,c.keySize=32,c.prototype.blockSize=c.blockSize,c.prototype.keySize=c.keySize,c.prototype._reset=function(){for(var e=this._key,t=e.length,n=t+6,r=4*(n+1),i=[],o=0;o<t;o++)i[o]=e[o];for(o=t;o<r;o++){var s=i[o-1];o%t==0?(s=s<<8|s>>>24,s=u.SBOX[s>>>24]<<24|u.SBOX[s>>>16&255]<<16|u.SBOX[s>>>8&255]<<8|u.SBOX[255&s],s^=a[o/t|0]<<24):t>6&&o%t==4&&(s=u.SBOX[s>>>24]<<24|u.SBOX[s>>>16&255]<<16|u.SBOX[s>>>8&255]<<8|u.SBOX[255&s]),i[o]=i[o-t]^s}for(var c=[],f=0;f<r;f++){var l=r-f,d=i[l-(f%4?0:4)];c[f]=f<4||l<=4?d:u.INV_SUB_MIX[0][u.SBOX[d>>>24]]^u.INV_SUB_MIX[1][u.SBOX[d>>>16&255]]^u.INV_SUB_MIX[2][u.SBOX[d>>>8&255]]^u.INV_SUB_MIX[3][u.SBOX[255&d]]}this._nRounds=n,this._keySchedule=i,this._invKeySchedule=c},c.prototype.encryptBlockRaw=function(e){return s(e=i(e),this._keySchedule,u.SUB_MIX,u.SBOX,this._nRounds)},c.prototype.encryptBlock=function(e){var t=this.encryptBlockRaw(e),n=r.allocUnsafe(16);return n.writeUInt32BE(t[0],0),n.writeUInt32BE(t[1],4),n.writeUInt32BE(t[2],8),n.writeUInt32BE(t[3],12),n},c.prototype.decryptBlock=function(e){var t=(e=i(e))[1];e[1]=e[3],e[3]=t;var n=s(e,this._invKeySchedule,u.INV_SUB_MIX,u.INV_SBOX,this._nRounds),o=r.allocUnsafe(16);return o.writeUInt32BE(n[0],0),o.writeUInt32BE(n[3],4),o.writeUInt32BE(n[2],8),o.writeUInt32BE(n[1],12),o},c.prototype.scrub=function(){o(this._keySchedule),o(this._invKeySchedule),o(this._key)},e.exports.AES=c},function(e,t,n){var r=n(8).Buffer,i=n(113);e.exports=function(e,t,n,o){if(r.isBuffer(e)||(e=r.from(e,"binary")),t&&(r.isBuffer(t)||(t=r.from(t,"binary")),8!==t.length))throw new RangeError("salt should be Buffer with 8 byte length");for(var s=n/8,a=r.alloc(s),u=r.alloc(o||0),c=r.alloc(0);s>0||o>0;){var f=new i;f.update(c),f.update(e),t&&f.update(t),c=f.digest();var l=0;if(s>0){var d=a.length-s;l=Math.min(s,c.length),c.copy(a,d,0,l),s-=l}if(l<c.length&&o>0){var h=u.length-o,p=Math.min(o,c.length-l);c.copy(u,h,l,l+p),o-=p}}return c.fill(0),{key:a,iv:u}}},function(e,t,n){"use strict";var r=n(29),i=n(47),o=i.getNAF,s=i.getJSF,a=i.assert;function u(e,t){this.type=e,this.p=new r(t.p,16),this.red=t.prime?r.red(t.prime):r.mont(this.p),this.zero=new r(0).toRed(this.red),this.one=new r(1).toRed(this.red),this.two=new r(2).toRed(this.red),this.n=t.n&&new r(t.n,16),this.g=t.g&&this.pointFromJSON(t.g,t.gRed),this._wnafT1=new Array(4),this._wnafT2=new Array(4),this._wnafT3=new Array(4),this._wnafT4=new Array(4),this._bitLength=this.n?this.n.bitLength():0;var n=this.n&&this.p.div(this.n);!n||n.cmpn(100)>0?this.redN=null:(this._maxwellTrick=!0,this.redN=this.n.toRed(this.red))}function c(e,t){this.curve=e,this.type=t,this.precomputed=null}e.exports=u,u.prototype.point=function(){throw new Error("Not implemented")},u.prototype.validate=function(){throw new Error("Not implemented")},u.prototype._fixedNafMul=function(e,t){a(e.precomputed);var n=e._getDoubles(),r=o(t,1,this._bitLength),i=(1<<n.step+1)-(n.step%2==0?2:1);i/=3;var s,u,c=[];for(s=0;s<r.length;s+=n.step){u=0;for(var f=s+n.step-1;f>=s;f--)u=(u<<1)+r[f];c.push(u)}for(var l=this.jpoint(null,null,null),d=this.jpoint(null,null,null),h=i;h>0;h--){for(s=0;s<c.length;s++)(u=c[s])===h?d=d.mixedAdd(n.points[s]):u===-h&&(d=d.mixedAdd(n.points[s].neg()));l=l.add(d)}return l.toP()},u.prototype._wnafMul=function(e,t){var n=4,r=e._getNAFPoints(n);n=r.wnd;for(var i=r.points,s=o(t,n,this._bitLength),u=this.jpoint(null,null,null),c=s.length-1;c>=0;c--){for(var f=0;c>=0&&0===s[c];c--)f++;if(c>=0&&f++,u=u.dblp(f),c<0)break;var l=s[c];a(0!==l),u="affine"===e.type?l>0?u.mixedAdd(i[l-1>>1]):u.mixedAdd(i[-l-1>>1].neg()):l>0?u.add(i[l-1>>1]):u.add(i[-l-1>>1].neg())}return"affine"===e.type?u.toP():u},u.prototype._wnafMulAdd=function(e,t,n,r,i){var a,u,c,f=this._wnafT1,l=this._wnafT2,d=this._wnafT3,h=0;for(a=0;a<r;a++){var p=(c=t[a])._getNAFPoints(e);f[a]=p.wnd,l[a]=p.points}for(a=r-1;a>=1;a-=2){var v=a-1,g=a;if(1===f[v]&&1===f[g]){var m=[t[v],null,null,t[g]];0===t[v].y.cmp(t[g].y)?(m[1]=t[v].add(t[g]),m[2]=t[v].toJ().mixedAdd(t[g].neg())):0===t[v].y.cmp(t[g].y.redNeg())?(m[1]=t[v].toJ().mixedAdd(t[g]),m[2]=t[v].add(t[g].neg())):(m[1]=t[v].toJ().mixedAdd(t[g]),m[2]=t[v].toJ().mixedAdd(t[g].neg()));var b=[-3,-1,-5,-7,0,7,5,1,3],y=s(n[v],n[g]);for(h=Math.max(y[0].length,h),d[v]=new Array(h),d[g]=new Array(h),u=0;u<h;u++){var w=0|y[0][u],_=0|y[1][u];d[v][u]=b[3*(w+1)+(_+1)],d[g][u]=0,l[v]=m}}else d[v]=o(n[v],f[v],this._bitLength),d[g]=o(n[g],f[g],this._bitLength),h=Math.max(d[v].length,h),h=Math.max(d[g].length,h)}var S=this.jpoint(null,null,null),E=this._wnafT4;for(a=h;a>=0;a--){for(var M=0;a>=0;){var A=!0;for(u=0;u<r;u++)E[u]=0|d[u][a],0!==E[u]&&(A=!1);if(!A)break;M++,a--}if(a>=0&&M++,S=S.dblp(M),a<0)break;for(u=0;u<r;u++){var I=E[u];0!==I&&(I>0?c=l[u][I-1>>1]:I<0&&(c=l[u][-I-1>>1].neg()),S="affine"===c.type?S.mixedAdd(c):S.add(c))}}for(a=0;a<r;a++)l[a]=null;return i?S:S.toP()},u.BasePoint=c,c.prototype.eq=function(){throw new Error("Not implemented")},c.prototype.validate=function(){return this.curve.validate(this)},u.prototype.decodePoint=function(e,t){e=i.toArray(e,t);var n=this.p.byteLength();if((4===e[0]||6===e[0]||7===e[0])&&e.length-1==2*n)return 6===e[0]?a(e[e.length-1]%2==0):7===e[0]&&a(e[e.length-1]%2==1),this.point(e.slice(1,1+n),e.slice(1+n,1+2*n));if((2===e[0]||3===e[0])&&e.length-1===n)return this.pointFromX(e.slice(1,1+n),3===e[0]);throw new Error("Unknown point format")},c.prototype.encodeCompressed=function(e){return this.encode(e,!0)},c.prototype._encode=function(e){var t=this.curve.p.byteLength(),n=this.getX().toArray("be",t);return e?[this.getY().isEven()?2:3].concat(n):[4].concat(n,this.getY().toArray("be",t))},c.prototype.encode=function(e,t){return i.encode(this._encode(t),e)},c.prototype.precompute=function(e){if(this.precomputed)return this;var t={doubles:null,naf:null,beta:null};return t.naf=this._getNAFPoints(8),t.doubles=this._getDoubles(4,e),t.beta=this._getBeta(),this.precomputed=t,this},c.prototype._hasDoubles=function(e){if(!this.precomputed)return!1;var t=this.precomputed.doubles;return!!t&&t.points.length>=Math.ceil((e.bitLength()+1)/t.step)},c.prototype._getDoubles=function(e,t){if(this.precomputed&&this.precomputed.doubles)return this.precomputed.doubles;for(var n=[this],r=this,i=0;i<t;i+=e){for(var o=0;o<e;o++)r=r.dbl();n.push(r)}return{step:e,points:n}},c.prototype._getNAFPoints=function(e){if(this.precomputed&&this.precomputed.naf)return this.precomputed.naf;for(var t=[this],n=(1<<e)-1,r=1===n?null:this.dbl(),i=1;i<n;i++)t[i]=t[i-1].add(r);return{wnd:e,points:t}},c.prototype._getBeta=function(){return null},c.prototype.dblp=function(e){for(var t=this,n=0;n<e;n++)t=t.dbl();return t}},function(e,t,n){var r=n(350),i=n(357),o=n(358),s=n(122),a=n(178),u=n(8).Buffer;function c(e){var t;"object"!=typeof e||u.isBuffer(e)||(t=e.passphrase,e=e.key),"string"==typeof e&&(e=u.from(e));var n,c,f=o(e,t),l=f.tag,d=f.data;switch(l){case"CERTIFICATE":c=r.certificate.decode(d,"der").tbsCertificate.subjectPublicKeyInfo;case"PUBLIC KEY":switch(c||(c=r.PublicKey.decode(d,"der")),n=c.algorithm.algorithm.join(".")){case"1.2.840.113549.1.1.1":return r.RSAPublicKey.decode(c.subjectPublicKey.data,"der");case"1.2.840.10045.2.1":return c.subjectPrivateKey=c.subjectPublicKey,{type:"ec",data:c};case"1.2.840.10040.4.1":return c.algorithm.params.pub_key=r.DSAparam.decode(c.subjectPublicKey.data,"der"),{type:"dsa",data:c.algorithm.params};default:throw new Error("unknown key id "+n)}case"ENCRYPTED PRIVATE KEY":d=function(e,t){var n=e.algorithm.decrypt.kde.kdeparams.salt,r=parseInt(e.algorithm.decrypt.kde.kdeparams.iters.toString(),10),o=i[e.algorithm.decrypt.cipher.algo.join(".")],c=e.algorithm.decrypt.cipher.iv,f=e.subjectPrivateKey,l=parseInt(o.split("-")[1],10)/8,d=a.pbkdf2Sync(t,n,r,l,"sha1"),h=s.createDecipheriv(o,d,c),p=[];return p.push(h.update(f)),p.push(h.final()),u.concat(p)}(d=r.EncryptedPrivateKey.decode(d,"der"),t);case"PRIVATE KEY":switch(n=(c=r.PrivateKey.decode(d,"der")).algorithm.algorithm.join(".")){case"1.2.840.113549.1.1.1":return r.RSAPrivateKey.decode(c.subjectPrivateKey,"der");case"1.2.840.10045.2.1":return{curve:c.algorithm.curve,privateKey:r.ECPrivateKey.decode(c.subjectPrivateKey,"der").privateKey};case"1.2.840.10040.4.1":return c.algorithm.params.priv_key=r.DSAparam.decode(c.subjectPrivateKey,"der"),{type:"dsa",params:c.algorithm.params};default:throw new Error("unknown key id "+n)}case"RSA PUBLIC KEY":return r.RSAPublicKey.decode(d,"der");case"RSA PRIVATE KEY":return r.RSAPrivateKey.decode(d,"der");case"DSA PRIVATE KEY":return{type:"dsa",params:r.DSAPrivateKey.decode(d,"der")};case"EC PRIVATE KEY":return{curve:(d=r.ECPrivateKey.decode(d,"der")).parameters.value,privateKey:d.privateKey};default:throw new Error("unknown key type "+l)}}e.exports=c,c.signature=r.signature},function(e,t,n){var r=n(53).Symbol;e.exports=r},function(e,t,n){var r=n(72)(Object,"create");e.exports=r},function(e,t,n){var r=n(406),i=n(407),o=n(408),s=n(409),a=n(410);function u(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}u.prototype.clear=r,u.prototype.delete=i,u.prototype.get=o,u.prototype.has=s,u.prototype.set=a,e.exports=u},function(e,t,n){var r=n(227);e.exports=function(e,t){for(var n=e.length;n--;)if(r(e[n][0],t))return n;return-1}},function(e,t,n){var r=n(412);e.exports=function(e,t){var n=e.__data__;return r(t)?n["string"==typeof t?"string":"hash"]:n.map}},function(e,t,n){"use strict";const r=n(54),i=n(54).buildOptions,o=n(460);"<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)".replace(/NAME/g,r.nameRegexp);!Number.parseInt&&window.parseInt&&(Number.parseInt=window.parseInt),!Number.parseFloat&&window.parseFloat&&(Number.parseFloat=window.parseFloat);const s={attributeNamePrefix:"@_",attrNodeName:!1,textNodeName:"#text",ignoreAttributes:!0,ignoreNameSpace:!1,allowBooleanAttributes:!1,parseNodeValue:!0,parseAttributeValue:!1,arrayMode:!1,trimValues:!0,cdataTagName:!1,cdataPositionChar:"\\c",tagValueProcessor:function(e,t){return e},attrValueProcessor:function(e,t){return e},stopNodes:[]};t.defaultOptions=s;const a=["attributeNamePrefix","attrNodeName","textNodeName","ignoreAttributes","ignoreNameSpace","allowBooleanAttributes","parseNodeValue","parseAttributeValue","arrayMode","trimValues","cdataTagName","cdataPositionChar","tagValueProcessor","attrValueProcessor","parseTrueNumberOnly","stopNodes"];function u(e,t,n){return t&&(n.trimValues&&(t=t.trim()),t=f(t=n.tagValueProcessor(t,e),n.parseNodeValue,n.parseTrueNumberOnly)),t}function c(e,t){if(t.ignoreNameSpace){const t=e.split(":"),n="/"===e.charAt(0)?"/":"";if("xmlns"===t[0])return"";2===t.length&&(e=n+t[1])}return e}function f(e,t,n){if(t&&"string"==typeof e){let t;return""===e.trim()||isNaN(e)?t="true"===e||"false"!==e&&e:(-1!==e.indexOf("0x")?t=Number.parseInt(e,16):-1!==e.indexOf(".")?(t=Number.parseFloat(e),e=e.replace(/\.?0+$/,"")):t=Number.parseInt(e,10),n&&(t=String(t)===e?t:e)),t}return r.isExist(e)?e:""}t.props=a;const l=new RegExp("([^\\s=]+)\\s*(=\\s*(['\"])(.*?)\\3)?","g");function d(e,t){if(!t.ignoreAttributes&&"string"==typeof e){e=e.replace(/\r?\n/g," ");const n=r.getAllMatches(e,l),i=n.length,o={};for(let e=0;e<i;e++){const r=c(n[e][1],t);r.length&&(void 0!==n[e][4]?(t.trimValues&&(n[e][4]=n[e][4].trim()),n[e][4]=t.attrValueProcessor(n[e][4],r),o[t.attributeNamePrefix+r]=f(n[e][4],t.parseAttributeValue,t.parseTrueNumberOnly)):t.allowBooleanAttributes&&(o[t.attributeNamePrefix+r]=!0))}if(!Object.keys(o).length)return;if(t.attrNodeName){const e={};return e[t.attrNodeName]=o,e}return o}}function h(e,t){let n,r="";for(let i=t;i<e.length;i++){let t=e[i];if(n)t===n&&(n="");else if('"'===t||"'"===t)n=t;else{if(">"===t)return{data:r,index:i};"\t"===t&&(t=" ")}r+=t}}function p(e,t,n,r){const i=e.indexOf(t,n);if(-1===i)throw new Error(r);return i+t.length-1}t.getTraversalObj=function(e,t){e=e.replace(/\r\n?/g,"\n"),t=i(t,s,a);const n=new o("!xml");let c=n,f="";for(let n=0;n<e.length;n++){if("<"===e[n])if("/"===e[n+1]){const i=p(e,">",n,"Closing Tag is not closed.");let o=e.substring(n+2,i).trim();if(t.ignoreNameSpace){const e=o.indexOf(":");-1!==e&&(o=o.substr(e+1))}c&&(c.val?c.val=r.getValue(c.val)+""+u(o,f,t):c.val=u(o,f,t)),t.stopNodes.length&&t.stopNodes.includes(c.tagname)&&(c.child=[],null==c.attrsMap&&(c.attrsMap={}),c.val=e.substr(c.startIndex+1,n-c.startIndex-1)),c=c.parent,f="",n=i}else if("?"===e[n+1])n=p(e,"?>",n,"Pi Tag is not closed.");else if("!--"===e.substr(n+1,3))n=p(e,"--\x3e",n,"Comment is not closed.");else if("!D"===e.substr(n+1,2)){const t=p(e,">",n,"DOCTYPE is not closed.");n=e.substring(n,t).indexOf("[")>=0?e.indexOf("]>",n)+1:t}else if("!["===e.substr(n+1,2)){const i=p(e,"]]>",n,"CDATA is not closed.")-2,s=e.substring(n+9,i);if(f&&(c.val=r.getValue(c.val)+""+u(c.tagname,f,t),f=""),t.cdataTagName){const e=new o(t.cdataTagName,c,s);c.addChild(e),c.val=r.getValue(c.val)+t.cdataPositionChar,s&&(e.val=s)}else c.val=(c.val||"")+(s||"");n=i+2}else{const i=h(e,n+1);let s=i.data;const a=i.index,l=s.indexOf(" ");let p=s;if(-1!==l&&(p=s.substr(0,l).replace(/\s\s*$/,""),s=s.substr(l+1)),t.ignoreNameSpace){const e=p.indexOf(":");-1!==e&&(p=p.substr(e+1))}if(c&&f&&"!xml"!==c.tagname&&(c.val=r.getValue(c.val)+""+u(c.tagname,f,t)),s.length>0&&s.lastIndexOf("/")===s.length-1){"/"===p[p.length-1]?(p=p.substr(0,p.length-1),s=p):s=s.substr(0,s.length-1);const e=new o(p,c,"");p!==s&&(e.attrsMap=d(s,t)),c.addChild(e)}else{const e=new o(p,c);t.stopNodes.length&&t.stopNodes.includes(e.tagname)&&(e.startIndex=a),p!==s&&(e.attrsMap=d(s,t)),c.addChild(e),c=e}f="",n=a}else f+=e[n]}return n}},function(e,t,n){"use strict";n.d(t,"a",(function(){return i})),n.d(t,"b",(function(){return o})),n.d(t,"c",(function(){return s}));var r="undefined"!=typeof Symbol&&"function"==typeof Symbol.for,i=r?Symbol.for("INTERNAL_AWS_APPSYNC_PUBSUB_PROVIDER"):"@@INTERNAL_AWS_APPSYNC_PUBSUB_PROVIDER",o=r?Symbol.for("INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER"):"@@INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER",s="x-amz-user-agent"},function(e,t,n){"use strict";n.d(t,"a",(function(){return _}));var r=n(44),i=n(148),o=n(28),s=n(16),a=n(77);function u(e){return(u="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)}var c=function(){return(c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},f=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},l=new r.a("Signer"),d=function(e,t){var n=new i.Sha256(e);return n.update(t),n.digestSync()},h=function(e){var t=e||"",n=new i.Sha256;return n.update(t),Object(o.b)(n.digestSync())},p=function(e){return Object.keys(e).map((function(e){return e.toLowerCase()})).sort().join(";")},v=function(e){var t,n,r=Object(s.parse)(e.url);return[e.method||"/",encodeURIComponent(r.pathname).replace(/%2F/gi,"/"),(n=r.query,n&&0!==n.length?n.split("&").map((function(e){var t=e.split("=");if(1===t.length)return e;var n=t[1].replace(/[!'()*]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}));return t[0]+"="+n})).sort((function(e,t){var n=e.split("=")[0],r=t.split("=")[0];return n===r?e<t?-1:1:n<r?-1:1})).join("&"):""),(t=e.headers,t&&0!==Object.keys(t).length?Object.keys(t).map((function(e){return{key:e.toLowerCase(),value:t[e]?t[e].trim().replace(/\s+/g," "):""}})).sort((function(e,t){return e.key<t.key?-1:1})).map((function(e){return e.key+":"+e.value})).join("\n")+"\n":""),p(e.headers),h(e.data)].join("\n")},g=function(e){var t=(Object(s.parse)(e.url).host.match(/([^\.]+)\.(?:([^\.]*)\.)?amazonaws\.com$/)||[]).slice(1,3);return"es"===t[1]&&(t=t.reverse()),{service:e.service||t[0],region:e.region||t[1]}},m=function(e,t,n){return[e,t,n,"aws4_request"].join("/")},b=function(e,t,n,r){return[e,n,r,h(t)].join("\n")},y=function(e,t,n){l.debug(n);var r=d("AWS4"+e,t),i=d(r,n.region),o=d(i,n.service);return d(o,"aws4_request")},w=function(e,t){return Object(o.b)(d(e,t))},_=function(){function e(){}return e.sign=function(e,t,n){void 0===n&&(n=null),e.headers=e.headers||{};var r=a.a.getDateWithClockOffset().toISOString().replace(/[:\-]|\.\d{3}/g,""),i=r.substr(0,8),o=Object(s.parse)(e.url);e.headers.host=o.host,e.headers["x-amz-date"]=r,t.session_token&&(e.headers["X-Amz-Security-Token"]=t.session_token);var u=v(e);l.debug(u);var c=n||g(e),f=m(i,c.region,c.service),d=b("AWS4-HMAC-SHA256",u,r,f),h=y(t.secret_key,i,c),_=w(h,d),S=function(e,t,n,r,i){return[e+" Credential="+t+"/"+n,"SignedHeaders="+r,"Signature="+i].join(", ")}("AWS4-HMAC-SHA256",t.access_key,f,p(e.headers),_);return e.headers.Authorization=S,e},e.signUrl=function(e,t,n,r){var i="object"===u(e)?e.url:e,o="object"===u(e)?e.method:"GET",l="object"===u(e)?e.body:void 0,d=a.a.getDateWithClockOffset().toISOString().replace(/[:\-]|\.\d{3}/g,""),h=d.substr(0,8),p=Object(s.parse)(i,!0,!0),_=(p.search,f(p,["search"])),S={host:_.host},E=n||g({url:Object(s.format)(_)}),M=E.region,A=E.service,I=m(h,M,A),k=t.session_token&&"iotdevicegateway"!==A,O=c(c(c({"X-Amz-Algorithm":"AWS4-HMAC-SHA256","X-Amz-Credential":[t.access_key,I].join("/"),"X-Amz-Date":d.substr(0,16)},k?{"X-Amz-Security-Token":""+t.session_token}:{}),r?{"X-Amz-Expires":""+r}:{}),{"X-Amz-SignedHeaders":Object.keys(S).join(",")}),x=v({method:o,url:Object(s.format)(c(c({},_),{query:c(c({},_.query),O)})),headers:S,data:l}),C=b("AWS4-HMAC-SHA256",x,d,I),T=y(t.secret_key,h,{region:M,service:A}),P=w(T,C),N=c({"X-Amz-Signature":P},t.session_token&&{"X-Amz-Security-Token":t.session_token});return Object(s.format)({protocol:_.protocol,slashes:!0,hostname:_.hostname,port:_.port,pathname:_.pathname,query:c(c(c({},_.query),O),N)})},e}()},function(e,t,n){"use strict";n.d(t,"a",(function(){return se}));var r=n(14),i=n(33),o=n(44),s=n(103),a=n(19),u=n(256),c=n(27),f=function(){return(f=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},l=new o.a("AbstractPubSubProvider"),d=function(){function e(e){void 0===e&&(e={}),this._config=e}return e.prototype.configure=function(e){return void 0===e&&(e={}),this._config=f(f({},e),this._config),l.debug("configure "+this.getProviderName(),this._config),this.options},e.prototype.getCategory=function(){return"PubSub"},Object.defineProperty(e.prototype,"options",{get:function(){return f({},this._config)},enumerable:!0,configurable:!0}),e}();function h(e){return(h="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)}var p,v=(p=function(e,t){return(p=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}p(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),g=function(){return(g=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},m=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},b=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},y=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},w=new o.a("MqttOverWSProvider");var _,S,E,M=function(){function e(){this.promises=new Map}return e.prototype.get=function(e,t){return m(this,void 0,void 0,(function(){var n;return b(this,(function(r){return(n=this.promises.get(e))||(n=t(e),this.promises.set(e,n)),[2,n]}))}))},Object.defineProperty(e.prototype,"allClients",{get:function(){return Array.from(this.promises.keys())},enumerable:!0,configurable:!0}),e.prototype.remove=function(e){this.promises.delete(e)},e}(),A="undefined"!=typeof Symbol?Symbol("topic"):"@@topic",I=function(e){function t(t){void 0===t&&(t={});var n=e.call(this,g(g({},t),{clientId:t.clientId||Object(c.v4)()}))||this;return n._clientsQueue=new M,n._topicObservers=new Map,n._clientIdObservers=new Map,n}return v(t,e),Object.defineProperty(t.prototype,"clientId",{get:function(){return this.options.clientId},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"endpoint",{get:function(){return this.options.aws_pubsub_endpoint},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"clientsQueue",{get:function(){return this._clientsQueue},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"isSSLEnabled",{get:function(){return!this.options.aws_appsync_dangerously_connect_to_http_endpoint_for_testing},enumerable:!0,configurable:!0}),t.prototype.getTopicForValue=function(e){return"object"===h(e)&&e[A]},t.prototype.getProviderName=function(){return"MqttOverWSProvider"},t.prototype.onDisconnect=function(e){var t=this,n=e.clientId,r=e.errorCode,i=y(e,["clientId","errorCode"]);if(0!==r){w.warn(n,JSON.stringify(g({errorCode:r},i),null,2));var o=[],s=this._clientIdObservers.get(n);if(!s)return;s.forEach((function(e){e.error("Disconnected, error code: "+r),t._topicObservers.forEach((function(t,n){t.delete(e),0===t.size&&o.push(n)}))})),this._clientIdObservers.delete(n),o.forEach((function(e){t._topicObservers.delete(e)}))}},t.prototype.newClient=function(e){var t=e.url,n=e.clientId;return m(this,void 0,void 0,(function(){var e,r=this;return b(this,(function(i){switch(i.label){case 0:return w.debug("Creating new MQTT client",n),(e=new u.Client(t,n)).onMessageArrived=function(e){var t=e.destinationName,n=e.payloadString;r._onMessage(t,n)},e.onConnectionLost=function(e){var t=e.errorCode,i=y(e,["errorCode"]);r.onDisconnect(g({clientId:n,errorCode:t},i))},[4,new Promise((function(t,n){e.connect({useSSL:r.isSSLEnabled,mqttVersion:3,onSuccess:function(){return t(e)},onFailure:n})}))];case 1:return i.sent(),[2,e]}}))}))},t.prototype.connect=function(e,t){return void 0===t&&(t={}),m(this,void 0,void 0,(function(){var n=this;return b(this,(function(r){switch(r.label){case 0:return[4,this.clientsQueue.get(e,(function(e){return n.newClient(g(g({},t),{clientId:e}))}))];case 1:return[2,r.sent()]}}))}))},t.prototype.disconnect=function(e){return m(this,void 0,void 0,(function(){var t;return b(this,(function(n){switch(n.label){case 0:return[4,this.clientsQueue.get(e,(function(){return null}))];case 1:return(t=n.sent())&&t.isConnected()&&t.disconnect(),this.clientsQueue.remove(e),[2]}}))}))},t.prototype.publish=function(e,t){return m(this,void 0,void 0,(function(){var n,r,i,o;return b(this,(function(s){switch(s.label){case 0:return n=[].concat(e),r=JSON.stringify(t),[4,this.endpoint];case 1:return i=s.sent(),[4,this.connect(this.clientId,{url:i})];case 2:return o=s.sent(),w.debug("Publishing to topic(s)",n.join(","),r),n.forEach((function(e){return o.send(e,r)})),[2]}}))}))},t.prototype._onMessage=function(e,t){try{var n=[];this._topicObservers.forEach((function(t,r){(function(e,t){for(var n=e.split("/"),r=n.length,i=t.split("/"),o=0;o<r;++o){var s=n[o],a=i[o];if("#"===s)return i.length>=r;if("+"!==s&&s!==a)return!1}return r===i.length})(r,e)&&n.push(t)}));var r=JSON.parse(t);"object"===h(r)&&(r[A]=e),n.forEach((function(e){e.forEach((function(e){return e.next(r)}))}))}catch(e){w.warn("Error handling message",e,t)}},t.prototype.subscribe=function(e,t){var n=this;void 0===t&&(t={});var i=[].concat(e);return w.debug("Subscribing to topic(s)",i.join(",")),new r.a((function(e){var r;i.forEach((function(t){var r=n._topicObservers.get(t);r||(r=new Set,n._topicObservers.set(t,r)),r.add(e)}));var o=t.clientId,s=void 0===o?n.clientId:o,a=n._clientIdObservers.get(s);return a||(a=new Set),a.add(e),n._clientIdObservers.set(s,a),m(n,void 0,void 0,(function(){var n,o,a,u;return b(this,(function(c){switch(c.label){case 0:return void 0!==(n=t.url)?[3,2]:[4,this.endpoint];case 1:return a=c.sent(),[3,3];case 2:a=n,c.label=3;case 3:o=a,c.label=4;case 4:return c.trys.push([4,6,,7]),[4,this.connect(s,{url:o})];case 5:return r=c.sent(),i.forEach((function(e){r.subscribe(e)})),[3,7];case 6:return u=c.sent(),e.error(u),[3,7];case 7:return[2]}}))})),function(){return w.debug("Unsubscribing from topic(s)",i.join(",")),r&&(n._clientIdObservers.get(s).delete(e),0===n._clientIdObservers.get(s).size&&(n.disconnect(s),n._clientIdObservers.delete(s)),i.forEach((function(t){var i=n._topicObservers.get(t)||new Set;i.delete(e),0===i.size&&(n._topicObservers.delete(t),r.isConnected()&&r.unsubscribe(t))}))),null}}))},t}(d),k=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),O=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},x=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},C=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},T=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},P=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(T(arguments[t]));return e},N=new o.a("AWSAppSyncProvider"),R=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t._topicClient=new Map,t._topicAlias=new Map,t}return k(t,e),Object.defineProperty(t.prototype,"endpoint",{get:function(){throw new Error("Not supported")},enumerable:!0,configurable:!0}),t.prototype.getProviderName=function(){return"AWSAppSyncProvider"},t.prototype.publish=function(e,t,n){return O(this,void 0,void 0,(function(){return x(this,(function(e){throw new Error("Operation not supported")}))}))},t.prototype._cleanUp=function(e){var t=this;Array.from(this._topicClient.entries()).filter((function(t){return T(t,2)[1].clientId===e})).map((function(e){return T(e,1)[0]})).forEach((function(e){return t._cleanUpForTopic(e)}))},t.prototype._cleanUpForTopic=function(e){this._topicClient.delete(e),this._topicAlias.delete(e)},t.prototype.onDisconnect=function(e){var t=this,n=e.clientId,r=e.errorCode,i=C(e,["clientId","errorCode"]);0!==r&&(Array.from(this._topicClient.entries()).filter((function(e){return T(e,2)[1].clientId===n})).map((function(e){return T(e,1)[0]})).forEach((function(e){t._topicObservers.has(e)&&(t._topicObservers.get(e).forEach((function(e){e.closed||e.error(i)})),t._topicObservers.delete(e))})),this._cleanUp(n))},t.prototype.disconnect=function(t){return O(this,void 0,void 0,(function(){return x(this,(function(n){switch(n.label){case 0:return[4,this.clientsQueue.get(t,(function(){return null}))];case 1:return n.sent(),[4,e.prototype.disconnect.call(this,t)];case 2:return n.sent(),this._cleanUp(t),[2]}}))}))},t.prototype.subscribe=function(e,t){var n=this;void 0===t&&(t={});var i=new r.a((function(r){var i=[].concat(e);return N.debug("Subscribing to topic(s)",i.join(",")),O(n,void 0,void 0,(function(){var e,n,o,s,a,u=this;return x(this,(function(c){switch(c.label){case 0:return i.forEach((function(e){u._topicObservers.has(e)||u._topicObservers.set(e,new Set),u._topicObservers.get(e).add(r)})),e=t.mqttConnections,n=void 0===e?[]:e,o=t.newSubscriptions,s=Object.entries(o).map((function(e){var t=T(e,2),n=t[0];return[t[1].topic,n]})),this._topicAlias=new Map(P(Array.from(this._topicAlias.entries()),s)),a=Object.entries(i.reduce((function(e,t){var r=n.find((function(e){return e.topics.indexOf(t)>-1}));if(r){var i=r.client,o=r.url;e[i]||(e[i]={url:o,topics:new Set}),e[i].topics.add(t)}return e}),{})),[4,Promise.all(a.map((function(e){var t=T(e,2),n=t[0],i=t[1],o=i.url,s=i.topics;return O(u,void 0,void 0,(function(){var e,t,i=this;return x(this,(function(a){switch(a.label){case 0:e=null,a.label=1;case 1:return a.trys.push([1,3,,4]),[4,this.connect(n,{clientId:n,url:o})];case 2:return e=a.sent(),[3,4];case 3:return t=a.sent(),r.error({message:"Failed to connect",error:t}),r.complete(),[2,void 0];case 4:return s.forEach((function(t){e.isConnected()&&(e.subscribe(t),i._topicClient.set(t,e))})),[2,e]}}))}))})))];case 1:return c.sent(),[2]}}))})),function(){N.debug("Unsubscribing from topic(s)",i.join(",")),i.forEach((function(e){var t=n._topicClient.get(e);t&&t.isConnected()&&(t.unsubscribe(e),n._topicClient.delete(e),Array.from(n._topicClient.values()).some((function(e){return e===t}))||n.disconnect(t.clientId)),n._topicObservers.delete(e)}))}}));return r.a.from(i).map((function(e){var t=n.getTopicForValue(e),r=n._topicAlias.get(t);return e.data=Object.entries(e.data).reduce((function(e,t){var n=T(t,2),i=n[0],o=n[1];return e[r||i]=o,e}),{}),e}))},t}(I),L=n(91),j=n(16),D=n(6),U=n(88),B=n(5),F=n(514),z=n(89),q=n(104),K=n(26),H=n(42),V=n(34),G=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),W=function(){return(W=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},$=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},Y=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},J=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},Z=new o.a("AWSAppSyncRealTimeProvider"),X="undefined"!=typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("amplify_default"):"@@amplify_default",Q=[400,401,403];!function(e){e.GQL_CONNECTION_INIT="connection_init",e.GQL_CONNECTION_ERROR="connection_error",e.GQL_CONNECTION_ACK="connection_ack",e.GQL_START="start",e.GQL_START_ACK="start_ack",e.GQL_DATA="data",e.GQL_CONNECTION_KEEP_ALIVE="ka",e.GQL_STOP="stop",e.GQL_COMPLETE="complete",e.GQL_ERROR="error"}(_||(_={})),function(e){e[e.PENDING=0]="PENDING",e[e.CONNECTED=1]="CONNECTED",e[e.FAILED=2]="FAILED"}(S||(S={})),function(e){e[e.CLOSED=0]="CLOSED",e[e.READY=1]="READY",e[e.CONNECTING=2]="CONNECTING"}(E||(E={}));var ee={accept:"application/json, text/javascript","content-encoding":"amz-1.0","content-type":"application/json; charset=UTF-8"},te=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.socketStatus=E.CLOSED,t.keepAliveTimeout=3e5,t.subscriptionObserverMap=new Map,t.promiseArray=[],t}return G(t,e),t.prototype.getProviderName=function(){return"AWSAppSyncRealTimeProvider"},t.prototype.newClient=function(){throw new Error("Not used here")},t.prototype.publish=function(e,t,n){return $(this,void 0,void 0,(function(){return Y(this,(function(e){throw new Error("Operation not supported")}))}))},t.prototype.subscribe=function(e,t){var n=this,i=t.appSyncGraphqlEndpoint;return new r.a((function(e){if(i){var r=Object(c.v4)();return n._startSubscriptionWithAWSAppSyncRealTime({options:t,observer:e,subscriptionId:r}),function(){return $(n,void 0,void 0,(function(){var e,t;return Y(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,3,4]),[4,this._waitForSubscriptionToBeConnected(r)];case 1:if(n.sent(),!(e=(this.subscriptionObserverMap.get(r)||{}).subscriptionState))return[2];if(e!==S.CONNECTED)throw new Error("Subscription never connected");return this._sendUnsubscriptionMessage(r),[3,4];case 2:return t=n.sent(),Z.debug("Error while unsubscribing "+t),[3,4];case 3:return this._removeSubscriptionObserver(r),[7];case 4:return[2]}}))}))}}e.error({errors:[W({},new L.a("Subscribe only available for AWS AppSync endpoint"))]}),e.complete()}))},Object.defineProperty(t.prototype,"isSSLEnabled",{get:function(){return!this.options.aws_appsync_dangerously_connect_to_http_endpoint_for_testing},enumerable:!0,configurable:!0}),t.prototype._startSubscriptionWithAWSAppSyncRealTime=function(e){var t=e.options,n=e.observer,r=e.subscriptionId;return $(this,void 0,void 0,(function(){var e,i,o,a,u,c,f,l,d,h,p,v,g,m,b,y,w,E,M,A,I,k,O,x,C,T,P=this;return Y(this,(function(N){switch(N.label){case 0:return e=t.appSyncGraphqlEndpoint,i=t.authenticationType,o=t.query,a=t.variables,u=t.apiKey,c=t.region,f=t.graphql_headers,l=void 0===f?function(){return{}}:f,d=t.additionalHeaders,h=void 0===d?{}:d,p=S.PENDING,v={query:o,variables:a},this.subscriptionObserverMap.set(r,{observer:n,query:o,variables:a,subscriptionState:p,startAckTimeoutId:null}),g=JSON.stringify(v),b=[{}],[4,this._awsRealTimeHeaderBasedAuth({apiKey:u,appSyncGraphqlEndpoint:e,authenticationType:i,payload:g,canonicalUri:"",region:c})];case 1:return y=[W.apply(void 0,b.concat([N.sent()]))],[4,l()];case 2:m=W.apply(void 0,[W.apply(void 0,[W.apply(void 0,y.concat([N.sent()])),h]),(T={},T[s.c]=B.a.userAgent,T)]),w={id:r,payload:{data:g,extensions:{authorization:W({},m)}},type:_.GQL_START},E=JSON.stringify(w),N.label=3;case 3:return N.trys.push([3,5,,6]),[4,this._initializeWebSocketConnection({apiKey:u,appSyncGraphqlEndpoint:e,authenticationType:i,region:c})];case 4:return N.sent(),[3,6];case 5:return M=N.sent(),Z.debug({err:M}),A=M.message,I=void 0===A?"":A,n.error({errors:[W({},new L.a("Connection failed: "+I))]}),n.complete(),"function"==typeof(k=(this.subscriptionObserverMap.get(r)||{}).subscriptionFailedCallback)&&k(),[2];case 6:return O=this.subscriptionObserverMap.get(r),x=O.subscriptionFailedCallback,C=O.subscriptionReadyCallback,this.subscriptionObserverMap.set(r,{observer:n,subscriptionState:p,variables:a,query:o,subscriptionReadyCallback:C,subscriptionFailedCallback:x,startAckTimeoutId:setTimeout((function(){P._timeoutStartSubscriptionAck.call(P,r)}),15e3)}),this.awsRealTimeSocket&&this.awsRealTimeSocket.send(E),[2]}}))}))},t.prototype._waitForSubscriptionToBeConnected=function(e){return $(this,void 0,void 0,(function(){var t=this;return Y(this,(function(n){return this.subscriptionObserverMap.get(e).subscriptionState===S.PENDING?[2,new Promise((function(n,r){var i=t.subscriptionObserverMap.get(e),o=i.observer,s=i.subscriptionState,a=i.variables,u=i.query;t.subscriptionObserverMap.set(e,{observer:o,subscriptionState:s,variables:a,query:u,subscriptionReadyCallback:n,subscriptionFailedCallback:r})}))]:[2]}))}))},t.prototype._sendUnsubscriptionMessage=function(e){try{if(this.awsRealTimeSocket&&this.awsRealTimeSocket.readyState===WebSocket.OPEN&&this.socketStatus===E.READY){var t={id:e,type:_.GQL_STOP},n=JSON.stringify(t);this.awsRealTimeSocket.send(n)}}catch(e){Z.debug({err:e})}},t.prototype._removeSubscriptionObserver=function(e){this.subscriptionObserverMap.delete(e),setTimeout(this._closeSocketIfRequired.bind(this),1e3)},t.prototype._closeSocketIfRequired=function(){if(!(this.subscriptionObserverMap.size>0))if(this.awsRealTimeSocket)if(this.awsRealTimeSocket.bufferedAmount>0)setTimeout(this._closeSocketIfRequired.bind(this),1e3);else{Z.debug("closing WebSocket..."),clearTimeout(this.keepAliveTimeoutId);var e=this.awsRealTimeSocket;e.onclose=void 0,e.onerror=void 0,e.close(1e3),this.awsRealTimeSocket=null,this.socketStatus=E.CLOSED}else this.socketStatus=E.CLOSED},t.prototype._handleIncomingSubscriptionMessage=function(e){Z.debug("subscription message from AWS AppSync RealTime: "+e.data);var t=JSON.parse(e.data),n=t.id,r=void 0===n?"":n,i=t.payload,o=t.type,s=this.subscriptionObserverMap.get(r)||{},a=s.observer,u=void 0===a?null:a,c=s.query,f=void 0===c?"":c,l=s.variables,d=void 0===l?{}:l,h=s.startAckTimeoutId,p=s.subscriptionReadyCallback,v=s.subscriptionFailedCallback;if(Z.debug({id:r,observer:u,query:f,variables:d}),o===_.GQL_DATA&&i&&i.data)u?u.next(i):Z.debug("observer not found for id: "+r);else if(o!==_.GQL_START_ACK){if(o===_.GQL_CONNECTION_KEEP_ALIVE)return clearTimeout(this.keepAliveTimeoutId),void(this.keepAliveTimeoutId=setTimeout(this._errorDisconnect.bind(this,V.a.TIMEOUT_DISCONNECT),this.keepAliveTimeout));if(o===_.GQL_ERROR){g=S.FAILED;this.subscriptionObserverMap.set(r,{observer:u,query:f,variables:d,startAckTimeoutId:h,subscriptionReadyCallback:p,subscriptionFailedCallback:v,subscriptionState:g}),u.error({errors:[W({},new L.a("Connection failed: "+JSON.stringify(i)))]}),clearTimeout(h),u.complete(),"function"==typeof v&&v()}}else{Z.debug("subscription ready for "+JSON.stringify({query:f,variables:d})),"function"==typeof p&&p(),clearTimeout(h),function(e,t,n){U.a.dispatch("api",{event:e,data:t,message:n},"PubSub",X)}(V.a.SUBSCRIPTION_ACK,{query:f,variables:d},"Connection established for subscription");var g=S.CONNECTED;this.subscriptionObserverMap.set(r,{observer:u,query:f,variables:d,startAckTimeoutId:null,subscriptionState:g,subscriptionReadyCallback:p,subscriptionFailedCallback:v})}},t.prototype._errorDisconnect=function(e){Z.debug("Disconnect error: "+e),this.subscriptionObserverMap.forEach((function(t){var n=t.observer;n&&!n.closed&&n.error({errors:[W({},new L.a(e))]})})),this.subscriptionObserverMap.clear(),this.awsRealTimeSocket&&this.awsRealTimeSocket.close(),this.socketStatus=E.CLOSED},t.prototype._timeoutStartSubscriptionAck=function(e){var t=this.subscriptionObserverMap.get(e)||{},n=t.observer,r=t.query,i=t.variables;n&&(this.subscriptionObserverMap.set(e,{observer:n,query:r,variables:i,subscriptionState:S.FAILED}),n&&!n.closed&&(n.error({errors:[W({},new L.a("Subscription timeout "+JSON.stringify({query:r,variables:i})))]}),n.complete()),Z.debug("timeoutStartSubscription",JSON.stringify({query:r,variables:i})))},t.prototype._initializeWebSocketConnection=function(e){var t=this,n=e.appSyncGraphqlEndpoint,r=e.authenticationType,i=e.apiKey,o=e.region;if(this.socketStatus!==E.READY)return new Promise((function(e,s){return $(t,void 0,void 0,(function(){var t,a,u,c,f,l,d,h,p,v;return Y(this,(function(g){switch(g.label){case 0:if(this.promiseArray.push({res:e,rej:s}),this.socketStatus!==E.CLOSED)return[3,5];g.label=1;case 1:return g.trys.push([1,4,,5]),this.socketStatus=E.CONNECTING,t=this.isSSLEnabled?"wss://":"ws://",a=n.replace("https://",t).replace("http://",t).replace("appsync-api","appsync-realtime-api").replace("gogi-beta","grt-beta"),u="{}",l=(f=JSON).stringify,[4,this._awsRealTimeHeaderBasedAuth({authenticationType:r,payload:u,canonicalUri:"/connect",apiKey:i,appSyncGraphqlEndpoint:n,region:o})];case 2:return c=l.apply(f,[g.sent()]),d=D.Buffer.from(c).toString("base64"),h=D.Buffer.from(u).toString("base64"),p=a+"?header="+d+"&payload="+h,[4,this._initializeRetryableHandshake({awsRealTimeUrl:p})];case 3:return g.sent(),this.promiseArray.forEach((function(e){var t=e.res;Z.debug("Notifying connection successful"),t()})),this.socketStatus=E.READY,this.promiseArray=[],[3,5];case 4:return v=g.sent(),this.promiseArray.forEach((function(e){return(0,e.rej)(v)})),this.promiseArray=[],this.awsRealTimeSocket&&this.awsRealTimeSocket.readyState===WebSocket.OPEN&&this.awsRealTimeSocket.close(3001),this.awsRealTimeSocket=null,this.socketStatus=E.CLOSED,[3,5];case 5:return[2]}}))}))}))},t.prototype._initializeRetryableHandshake=function(e){var t=e.awsRealTimeUrl;return $(this,void 0,void 0,(function(){return Y(this,(function(e){switch(e.label){case 0:return Z.debug("Initializaling retryable Handshake"),[4,Object(F.b)(this._initializeHandshake.bind(this),[{awsRealTimeUrl:t}],5e3)];case 1:return e.sent(),[2]}}))}))},t.prototype._initializeHandshake=function(e){var t=e.awsRealTimeUrl;return $(this,void 0,void 0,(function(){var e,n,r,i=this;return Y(this,(function(o){switch(o.label){case 0:Z.debug("Initializing handshake "+t),o.label=1;case 1:return o.trys.push([1,4,,5]),[4,new Promise((function(e,n){var r=new WebSocket(t,"graphql-ws");r.onerror=function(){Z.debug("WebSocket connection error")},r.onclose=function(){n(new Error("Connection handshake error"))},r.onopen=function(){return i.awsRealTimeSocket=r,e()}}))];case 2:return o.sent(),[4,new Promise((function(e,t){var n=!1;i.awsRealTimeSocket.onerror=function(e){Z.debug("WebSocket error "+JSON.stringify(e))},i.awsRealTimeSocket.onclose=function(e){Z.debug("WebSocket closed "+e.reason),t(new Error(JSON.stringify(e)))},i.awsRealTimeSocket.onmessage=function(r){Z.debug("subscription message from AWS AppSyncRealTime: "+r.data+" ");var o=JSON.parse(r.data),s=o.type,a=o.payload,u=(void 0===a?{}:a).connectionTimeoutMs,c=void 0===u?3e5:u;if(s===_.GQL_CONNECTION_ACK)return n=!0,i.keepAliveTimeout=c,i.awsRealTimeSocket.onmessage=i._handleIncomingSubscriptionMessage.bind(i),i.awsRealTimeSocket.onerror=function(e){Z.debug(e),i._errorDisconnect(V.a.CONNECTION_CLOSED)},i.awsRealTimeSocket.onclose=function(e){Z.debug("WebSocket closed "+e.reason),i._errorDisconnect(V.a.CONNECTION_CLOSED)},void e("Cool, connected to AWS AppSyncRealTime");if(s===_.GQL_CONNECTION_ERROR){var f=o.payload,l=(void 0===f?{}:f).errors,d=J(void 0===l?[]:l,1)[0],h=void 0===d?{}:d,p=h.errorType,v=void 0===p?"":p,g=h.errorCode;t({errorType:v,errorCode:void 0===g?0:g})}};var r={type:_.GQL_CONNECTION_INIT};i.awsRealTimeSocket.send(JSON.stringify(r)),setTimeout(function(){n||t(new Error("Connection timeout: ack from AWSRealTime was not received on 15000 ms"))}.bind(i),15e3)}))];case 3:return o.sent(),[3,5];case 4:throw e=o.sent(),n=e.errorType,r=e.errorCode,Q.includes(r)?new F.a(n):n?new Error(n):e;case 5:return[2]}}))}))},t.prototype._awsRealTimeHeaderBasedAuth=function(e){var t=e.authenticationType,n=e.payload,r=e.canonicalUri,i=e.appSyncGraphqlEndpoint,o=e.apiKey,s=e.region;return $(this,void 0,void 0,(function(){var e,a,u;return Y(this,(function(c){switch(c.label){case 0:return e={API_KEY:this._awsRealTimeApiKeyHeader.bind(this),AWS_IAM:this._awsRealTimeIAMHeader.bind(this),OPENID_CONNECT:this._awsRealTimeOPENIDHeader.bind(this),AMAZON_COGNITO_USER_POOLS:this._awsRealTimeCUPHeader.bind(this)},"function"!=typeof(a=e[t])?(Z.debug("Authentication type "+t+" not supported"),[2,""]):(u=j.parse(i).host,[4,a({payload:n,canonicalUri:r,appSyncGraphqlEndpoint:i,apiKey:o,region:s,host:u})]);case 1:return[2,c.sent()]}}))}))},t.prototype._awsRealTimeCUPHeader=function(e){var t=e.host;return $(this,void 0,void 0,(function(){return Y(this,(function(e){switch(e.label){case 0:return[4,H.a.currentSession()];case 1:return[2,{Authorization:e.sent().getAccessToken().getJwtToken(),host:t}]}}))}))},t.prototype._awsRealTimeOPENIDHeader=function(e){var t=e.host;return $(this,void 0,void 0,(function(){var e,n,r;return Y(this,(function(i){switch(i.label){case 0:return[4,K.a.getItem("federatedInfo")];case 1:return(n=i.sent())?(e=n.token,[3,4]):[3,2];case 2:return[4,H.a.currentAuthenticatedUser()];case 3:(r=i.sent())&&(e=r.token),i.label=4;case 4:if(!e)throw new Error("No federated jwt");return[2,{Authorization:e,host:t}]}}))}))},t.prototype._awsRealTimeApiKeyHeader=function(e){var t=e.apiKey,n=e.host;return $(this,void 0,void 0,(function(){var e,r;return Y(this,(function(i){return e=new Date,r=e.toISOString().replace(/[:\-]|\.\d{3}/g,""),[2,{host:n,"x-amz-date":r,"x-api-key":t}]}))}))},t.prototype._awsRealTimeIAMHeader=function(e){var t=e.payload,n=e.canonicalUri,r=e.appSyncGraphqlEndpoint,i=e.region;return $(this,void 0,void 0,(function(){var e,o,s;return Y(this,(function(a){switch(a.label){case 0:return e={region:i,service:"appsync"},[4,this._ensureCredentials()];case 1:if(!a.sent())throw new Error("No credentials");return[4,z.a.get().then((function(e){return{secret_key:e.secretAccessKey,access_key:e.accessKeyId,session_token:e.sessionToken}}))];case 2:return o=a.sent(),s={url:""+r+n,data:t,method:"POST",headers:W({},ee)},[2,q.a.sign(s,o,e).headers]}}))}))},t.prototype._ensureCredentials=function(){return z.a.get().then((function(e){if(!e)return!1;var t=z.a.shear(e);return Z.debug("set credentials for AWSAppSyncRealTimeProvider",t),!0})).catch((function(e){return Z.warn("ensure credentials error",e),!1}))},t}(d),ne=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},re=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},ie=Object(i.b)().isNode,oe=new o.a("PubSub"),se=new(function(){function e(e){this._options=e,oe.debug("PubSub Options",this._options),this._pluggables=[],this.subscribe=this.subscribe.bind(this)}return Object.defineProperty(e.prototype,"awsAppSyncProvider",{get:function(){return this._awsAppSyncProvider||(this._awsAppSyncProvider=new R(this._options)),this._awsAppSyncProvider},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"awsAppSyncRealTimeProvider",{get:function(){return this._awsAppSyncRealTimeProvider||(this._awsAppSyncRealTimeProvider=new te(this._options)),this._awsAppSyncRealTimeProvider},enumerable:!0,configurable:!0}),e.prototype.getModuleName=function(){return"PubSub"},e.prototype.configure=function(e){var t=this,n=e?e.PubSub||e:{};return oe.debug("configure PubSub",{opt:n}),this._options=Object.assign({},this._options,n),this._pluggables.map((function(e){return e.configure(t._options)})),this._options},e.prototype.addPluggable=function(e){return ne(this,void 0,void 0,(function(){return re(this,(function(t){return e&&"PubSub"===e.getCategory()?(this._pluggables.push(e),[2,e.configure(this._options)]):[2]}))}))},e.prototype.getProviderByName=function(e){return e===s.a?this.awsAppSyncProvider:e===s.b?this.awsAppSyncRealTimeProvider:this._pluggables.find((function(t){return t.getProviderName()===e}))},e.prototype.getProviders=function(e){void 0===e&&(e={});var t=e.provider;if(!t)return this._pluggables;var n=this.getProviderByName(t);if(!n)throw new Error("Could not find provider named "+t);return[n]},e.prototype.publish=function(e,t,n){return ne(this,void 0,void 0,(function(){return re(this,(function(r){return[2,Promise.all(this.getProviders(n).map((function(r){return r.publish(e,t,n)})))]}))}))},e.prototype.subscribe=function(e,t){if(ie&&this._options&&this._options.ssr)throw new Error("Subscriptions are not supported for Server-Side Rendering (SSR)");oe.debug("subscribe options",t);var n=this.getProviders(t);return new r.a((function(r){var i=n.map((function(n){return{provider:n,observable:n.subscribe(e,t)}})).map((function(e){var t=e.provider;return e.observable.subscribe({start:console.error,next:function(e){return r.next({provider:t,value:e})},error:function(e){return r.error({provider:t,error:e})}})}));return function(){return i.forEach((function(e){return e.unsubscribe()}))}}))},e}())(null);a.a.register(se)},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(1);function i(e){var t,n,i={};if(e=e.replace(/^\?/,""))try{for(var o=Object(r.__values)(e.split("&")),s=o.next();!s.done;s=o.next()){var a=s.value,u=Object(r.__read)(a.split("="),2),c=u[0],f=u[1],l=void 0===f?null:f;c=decodeURIComponent(c),l&&(l=decodeURIComponent(l)),c in i?Array.isArray(i[c])?i[c].push(l):i[c]=[i[c],l]:i[c]=l}}catch(e){t={error:e}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(t)throw t.error}}return i}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Crc32=t.crc32=void 0;var r=n(1);t.crc32=function(e){return(new i).update(e).digest()};var i=function(){function e(){this.checksum=4294967295}return e.prototype.update=function(e){var t,n;try{for(var i=r.__values(e),s=i.next();!s.done;s=i.next()){var a=s.value;this.checksum=this.checksum>>>8^o[255&(this.checksum^a)]}}catch(e){t={error:e}}finally{try{s&&!s.done&&(n=i.return)&&n.call(i)}finally{if(t)throw t.error}}return this},e.prototype.digest=function(){return(4294967295^this.checksum)>>>0},e}();t.Crc32=i;var o=Uint32Array.from([0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117])},function(e,t,n){var r=n(431);e.exports=function(e,t){return r(e,t)}},function(e,t,n){var r=n(483),i=n(484),o=i;o.v1=r,o.v4=i,e.exports=o},function(e,t,n){"use strict";n.d(t,"a",(function(){return M}));var r=n(107),i=n(1),o=n(28),s=function(){function e(e){if(this.bytes=e,8!==e.byteLength)throw new Error("Int64 buffers must be exactly 8 bytes")}return e.fromNumber=function(t){if(t>0x8000000000000000||t<-0x8000000000000000)throw new Error(t+" is too large (or, if negative, too small) to represent as an Int64");for(var n=new Uint8Array(8),r=7,i=Math.abs(Math.round(t));r>-1&&i>0;r--,i/=256)n[r]=i;return t<0&&a(n),new e(n)},e.prototype.valueOf=function(){var e=this.bytes.slice(0),t=128&e[0];return t&&a(e),parseInt(Object(o.b)(e),16)*(t?-1:1)},e.prototype.toString=function(){return String(this.valueOf())},e}();function a(e){for(var t=0;t<8;t++)e[t]^=255;for(t=7;t>-1&&(e[t]++,0===e[t]);t--);}var u,c=function(){function e(e,t){this.toUtf8=e,this.fromUtf8=t}return e.prototype.format=function(e){var t,n,r,o,s=[];try{for(var a=Object(i.__values)(Object.keys(e)),u=a.next();!u.done;u=a.next()){var c=u.value,f=this.fromUtf8(c);s.push(Uint8Array.from([f.byteLength]),f,this.formatHeaderValue(e[c]))}}catch(e){t={error:e}}finally{try{u&&!u.done&&(n=a.return)&&n.call(a)}finally{if(t)throw t.error}}var l=new Uint8Array(s.reduce((function(e,t){return e+t.byteLength}),0)),d=0;try{for(var h=Object(i.__values)(s),p=h.next();!p.done;p=h.next()){var v=p.value;l.set(v,d),d+=v.byteLength}}catch(e){r={error:e}}finally{try{p&&!p.done&&(o=h.return)&&o.call(h)}finally{if(r)throw r.error}}return l},e.prototype.formatHeaderValue=function(e){switch(e.type){case"boolean":return Uint8Array.from([e.value?0:1]);case"byte":return Uint8Array.from([2,e.value]);case"short":var t=new DataView(new ArrayBuffer(3));return t.setUint8(0,3),t.setInt16(1,e.value,!1),new Uint8Array(t.buffer);case"integer":var n=new DataView(new ArrayBuffer(5));return n.setUint8(0,4),n.setInt32(1,e.value,!1),new Uint8Array(n.buffer);case"long":var r=new Uint8Array(9);return r[0]=5,r.set(e.value.bytes,1),r;case"binary":var i=new DataView(new ArrayBuffer(3+e.value.byteLength));i.setUint8(0,6),i.setUint16(1,e.value.byteLength,!1);var a=new Uint8Array(i.buffer);return a.set(e.value,3),a;case"string":var u=this.fromUtf8(e.value),c=new DataView(new ArrayBuffer(3+u.byteLength));c.setUint8(0,7),c.setUint16(1,u.byteLength,!1);var f=new Uint8Array(c.buffer);return f.set(u,3),f;case"timestamp":var l=new Uint8Array(9);return l[0]=8,l.set(s.fromNumber(e.value.valueOf()).bytes,1),l;case"uuid":if(!y.test(e.value))throw new Error("Invalid UUID received: "+e.value);var d=new Uint8Array(17);return d[0]=9,d.set(Object(o.a)(e.value.replace(/\-/g,"")),1),d}},e.prototype.parse=function(e){for(var t={},n=0;n<e.byteLength;){var r=e.getUint8(n++),i=this.toUtf8(new Uint8Array(e.buffer,e.byteOffset+n,r));switch(n+=r,e.getUint8(n++)){case 0:t[i]={type:f,value:!0};break;case 1:t[i]={type:f,value:!1};break;case 2:t[i]={type:l,value:e.getInt8(n++)};break;case 3:t[i]={type:d,value:e.getInt16(n,!1)},n+=2;break;case 4:t[i]={type:h,value:e.getInt32(n,!1)},n+=4;break;case 5:t[i]={type:p,value:new s(new Uint8Array(e.buffer,e.byteOffset+n,8))},n+=8;break;case 6:var a=e.getUint16(n,!1);n+=2,t[i]={type:v,value:new Uint8Array(e.buffer,e.byteOffset+n,a)},n+=a;break;case 7:var u=e.getUint16(n,!1);n+=2,t[i]={type:g,value:this.toUtf8(new Uint8Array(e.buffer,e.byteOffset+n,u))},n+=u;break;case 8:t[i]={type:m,value:new Date(new s(new Uint8Array(e.buffer,e.byteOffset+n,8)).valueOf())},n+=8;break;case 9:var c=new Uint8Array(e.buffer,e.byteOffset+n,16);n+=16,t[i]={type:b,value:Object(o.b)(c.subarray(0,4))+"-"+Object(o.b)(c.subarray(4,6))+"-"+Object(o.b)(c.subarray(6,8))+"-"+Object(o.b)(c.subarray(8,10))+"-"+Object(o.b)(c.subarray(10))};break;default:throw new Error("Unrecognized header type tag")}}return t},e}();!function(e){e[e.boolTrue=0]="boolTrue",e[e.boolFalse=1]="boolFalse",e[e.byte=2]="byte",e[e.short=3]="short",e[e.integer=4]="integer",e[e.long=5]="long",e[e.byteArray=6]="byteArray",e[e.string=7]="string",e[e.timestamp=8]="timestamp",e[e.uuid=9]="uuid"}(u||(u={}));var f="boolean",l="byte",d="short",h="integer",p="long",v="binary",g="string",m="timestamp",b="uuid",y=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;var w=function(){function e(e,t){this.headerMarshaller=new c(e,t)}return e.prototype.marshall=function(e){var t=e.headers,n=e.body,i=this.headerMarshaller.format(t),o=i.byteLength+n.byteLength+16,s=new Uint8Array(o),a=new DataView(s.buffer,s.byteOffset,s.byteLength),u=new r.Crc32;return a.setUint32(0,o,!1),a.setUint32(4,i.byteLength,!1),a.setUint32(8,u.update(s.subarray(0,8)).digest(),!1),s.set(i,12),s.set(n,i.byteLength+12),a.setUint32(o-4,u.update(s.subarray(8,o-4)).digest(),!1),s},e.prototype.unmarshall=function(e){var t=function(e){var t=e.byteLength,n=e.byteOffset,i=e.buffer;if(t<16)throw new Error("Provided message too short to accommodate event stream message overhead");var o=new DataView(i,n,t),s=o.getUint32(0,!1);if(t!==s)throw new Error("Reported message length does not match received message length");var a=o.getUint32(4,!1),u=o.getUint32(8,!1),c=o.getUint32(t-4,!1),f=(new r.Crc32).update(new Uint8Array(i,n,8));if(u!==f.digest())throw new Error("The prelude checksum specified in the message ("+u+") does not match the calculated CRC32 checksum ("+f.digest()+")");if(f.update(new Uint8Array(i,n+8,t-12)),c!==f.digest())throw new Error("The message checksum ("+f.digest()+") did not match the expected value of "+c);return{headers:new DataView(i,n+8+4,a),body:new Uint8Array(i,n+8+4+a,s-a-16)}}(e),n=t.headers,i=t.body;return{headers:this.headerMarshaller.parse(n),body:i}},e.prototype.formatHeaders=function(e){return this.headerMarshaller.format(e)},e}();var _=function(){function e(e){var t=e.utf8Encoder,n=e.utf8Decoder;this.eventMarshaller=new w(t,n),this.utfEncoder=t}return e.prototype.deserialize=function(e,t){var n,r,o,s,a,u,c;return function(e,t){var n;return(n={})[Symbol.asyncIterator]=function(){return Object(i.__asyncGenerator)(this,arguments,(function(){var n,r,o,s,a,u,c,f,l,d,h,p,v,g,m,b,y;return Object(i.__generator)(this,(function(w){switch(w.label){case 0:w.trys.push([0,12,13,18]),n=Object(i.__asyncValues)(e),w.label=1;case 1:return[4,Object(i.__await)(n.next())];case 2:if((r=w.sent()).done)return[3,11];if(o=r.value,s=t.eventMarshaller.unmarshall(o),"error"!==(a=s.headers[":message-type"].value))return[3,3];throw(u=new Error(s.headers[":error-message"].value||"UnknownError")).name=s.headers[":error-code"].value,u;case 3:return"exception"!==a?[3,5]:(c=s.headers[":exception-type"].value,(g={})[c]=s,f=g,[4,Object(i.__await)(t.deserializer(f))]);case 4:if((l=w.sent()).$unknown)throw(d=new Error(t.toUtf8(s.body))).name=c,d;throw l[c];case 5:return"event"!==a?[3,9]:((m={})[s.headers[":event-type"].value]=s,h=m,[4,Object(i.__await)(t.deserializer(h))]);case 6:return(p=w.sent()).$unknown?[3,10]:[4,Object(i.__await)(p)];case 7:return[4,w.sent()];case 8:return w.sent(),[3,10];case 9:throw Error("Unrecognizable event type: "+s.headers[":event-type"].value);case 10:return[3,1];case 11:return[3,18];case 12:return v=w.sent(),b={error:v},[3,18];case 13:return w.trys.push([13,,16,17]),r&&!r.done&&(y=n.return)?[4,Object(i.__await)(y.call(n))]:[3,15];case 14:w.sent(),w.label=15;case 15:return[3,17];case 16:if(b)throw b.error;return[7];case 17:return[7];case 18:return[2]}}))}))},n}((n=e,o=0,s=0,a=null,u=null,c=function(e){if("number"!=typeof e)throw new Error("Attempted to allocate an event message where size was not a number: "+e);o=e,s=4,a=new Uint8Array(e),new DataView(a.buffer).setUint32(0,e,!1)},(r={})[Symbol.asyncIterator]=function(){return Object(i.__asyncGenerator)(this,arguments,(function(){var e,t,r,f,l,d,h,p;return Object(i.__generator)(this,(function(v){switch(v.label){case 0:e=n[Symbol.asyncIterator](),v.label=1;case 1:return[4,Object(i.__await)(e.next())];case 2:return t=v.sent(),r=t.value,t.done?o?[3,4]:[4,Object(i.__await)(void 0)]:[3,10];case 3:return[2,v.sent()];case 4:return o!==s?[3,7]:[4,Object(i.__await)(a)];case 5:return[4,v.sent()];case 6:return v.sent(),[3,8];case 7:throw new Error("Truncated event message received.");case 8:return[4,Object(i.__await)(void 0)];case 9:return[2,v.sent()];case 10:f=r.length,l=0,v.label=11;case 11:if(!(l<f))return[3,15];if(!a){if(d=f-l,u||(u=new Uint8Array(4)),h=Math.min(4-s,d),u.set(r.slice(l,l+h),s),l+=h,(s+=h)<4)return[3,15];c(new DataView(u.buffer).getUint32(0,!1)),u=null}return p=Math.min(o-s,f-l),a.set(r.slice(l,l+p),s),s+=p,l+=p,o&&o===s?[4,Object(i.__await)(a)]:[3,14];case 12:return[4,v.sent()];case 13:v.sent(),a=null,o=0,s=0,v.label=14;case 14:return[3,11];case 15:return[3,1];case 16:return[2]}}))}))},r),{eventMarshaller:this.eventMarshaller,deserializer:t,toUtf8:this.utfEncoder})},e.prototype.serialize=function(e,t){var n,r=this;return(n={})[Symbol.asyncIterator]=function(){return Object(i.__asyncGenerator)(this,arguments,(function(){var n,o,s,a,u,c,f;return Object(i.__generator)(this,(function(l){switch(l.label){case 0:l.trys.push([0,7,8,13]),n=Object(i.__asyncValues)(e),l.label=1;case 1:return[4,Object(i.__await)(n.next())];case 2:return(o=l.sent()).done?[3,6]:(s=o.value,a=r.eventMarshaller.marshall(t(s)),[4,Object(i.__await)(a)]);case 3:return[4,l.sent()];case 4:l.sent(),l.label=5;case 5:return[3,1];case 6:return[3,13];case 7:return u=l.sent(),c={error:u},[3,13];case 8:return l.trys.push([8,,11,12]),o&&!o.done&&(f=n.return)?[4,Object(i.__await)(f.call(n))]:[3,10];case 9:l.sent(),l.label=10;case 10:return[3,12];case 11:if(c)throw c.error;return[7];case 12:return[7];case 13:return[4,Object(i.__await)(new Uint8Array(0))];case 14:return[4,l.sent()];case 15:return l.sent(),[2]}}))}))},n},e}(),S=function(){function e(e){var t=e.utf8Encoder,n=e.utf8Decoder;this.eventMarshaller=new w(t,n),this.universalMarshaller=new _({utf8Decoder:n,utf8Encoder:t})}return e.prototype.deserialize=function(e,t){var n,r,o=E(e)?(n=e,(r={})[Symbol.asyncIterator]=function(){return Object(i.__asyncGenerator)(this,arguments,(function(){var e,t,r,o;return Object(i.__generator)(this,(function(s){switch(s.label){case 0:e=n.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,Object(i.__await)(e.read())];case 3:return t=s.sent(),r=t.done,o=t.value,r?[4,Object(i.__await)(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,Object(i.__await)(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return e.releaseLock(),[7];case 10:return[2]}}))}))},r):e;return this.universalMarshaller.deserialize(o,t)},e.prototype.serialize=function(e,t){var n,r=this.universalMarshaller.serialize(e,t);return"function"==typeof ReadableStream?(n=r[Symbol.asyncIterator](),new ReadableStream({pull:function(e){return Object(i.__awaiter)(this,void 0,void 0,(function(){var t,r,o;return Object(i.__generator)(this,(function(i){switch(i.label){case 0:return[4,n.next()];case 1:return t=i.sent(),r=t.done,o=t.value,r?[2,e.close()]:(e.enqueue(o),[2])}}))}))}})):r},e}(),E=function(e){return"function"==typeof ReadableStream&&e instanceof ReadableStream},M=function(e){return new S(e)}},function(e,t,n){"use strict";n.d(t,"a",(function(){return M}));var r=n(1),i="X-Amz-Date".toLowerCase(),o=["authorization",i,"date"],s="X-Amz-Signature".toLowerCase(),a="X-Amz-Security-Token".toLowerCase(),u={authorization:!0,"cache-control":!0,connection:!0,expect:!0,from:!0,"keep-alive":!0,"max-forwards":!0,pragma:!0,referer:!0,te:!0,trailer:!0,"transfer-encoding":!0,upgrade:!0,"user-agent":!0,"x-amzn-trace-id":!0},c=/^proxy-/,f=/^sec-/,l="AWS4-HMAC-SHA256-PAYLOAD",d={},h=[];function p(e,t,n){return e+"/"+t+"/"+n+"/aws4_request"}var v=n(28);function g(e,t,n){var i,o,s=e.headers,a={};try{for(var l=Object(r.__values)(Object.keys(s).sort()),d=l.next();!d.done;d=l.next()){var h=d.value,p=h.toLowerCase();(p in u||(null==t?void 0:t.has(p))||c.test(p)||f.test(p))&&(!n||n&&!n.has(p))||(a[p]=s[h].trim().replace(/\s+/g," "))}}catch(e){i={error:e}}finally{try{d&&!d.done&&(o=l.return)&&o.call(l)}finally{if(i)throw i.error}}return a}var m=n(55);var b=n(251);function y(e,t){var n=e.headers,i=e.body;return Object(r.__awaiter)(this,void 0,void 0,(function(){var e,o,s,a,u,c,f;return Object(r.__generator)(this,(function(l){switch(l.label){case 0:try{for(e=Object(r.__values)(Object.keys(n)),o=e.next();!o.done;o=e.next())if("x-amz-content-sha256"===(s=o.value).toLowerCase())return[2,n[s]]}catch(e){c={error:e}}finally{try{o&&!o.done&&(f=e.return)&&f.call(e)}finally{if(c)throw c.error}}return null!=i?[3,1]:[2,"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"];case 1:return"string"==typeof i||ArrayBuffer.isView(i)||Object(b.a)(i)?((a=new t).update(i),u=v.b,[4,a.digest()]):[3,3];case 2:return[2,u.apply(void 0,[l.sent()])];case 3:return[2,"UNSIGNED-PAYLOAD"]}}))}))}function w(e){var t=e.headers,n=e.query,i=Object(r.__rest)(e,["headers","query"]);return Object(r.__assign)(Object(r.__assign)({},i),{headers:Object(r.__assign)({},t),query:n?_(n):void 0})}function _(e){return Object.keys(e).reduce((function(t,n){var i,o=e[n];return Object(r.__assign)(Object(r.__assign)({},t),((i={})[n]=Array.isArray(o)?Object(r.__spread)(o):o,i))}),{})}function S(e){var t,n;e="function"==typeof e.clone?e.clone():w(e);try{for(var i=Object(r.__values)(Object.keys(e.headers)),s=i.next();!s.done;s=i.next()){var a=s.value;o.indexOf(a.toLowerCase())>-1&&delete e.headers[a]}}catch(e){t={error:e}}finally{try{s&&!s.done&&(n=i.return)&&n.call(i)}finally{if(t)throw t.error}}return e}function E(e){return function(e){if("number"==typeof e)return new Date(1e3*e);if("string"==typeof e)return Number(e)?new Date(1e3*Number(e)):new Date(e);return e}(e).toISOString().replace(/\.\d{3}Z$/,"Z")}var M=function(){function e(e){var t=e.applyChecksum,n=e.credentials,r=e.region,i=e.service,o=e.sha256,s=e.uriEscapePath,a=void 0===s||s;this.service=i,this.sha256=o,this.uriEscapePath=a,this.applyChecksum="boolean"!=typeof t||t,this.regionProvider=k(r),this.credentialProvider=O(n)}return e.prototype.presign=function(e,t){return void 0===t&&(t={}),Object(r.__awaiter)(this,void 0,void 0,(function(){var n,i,o,s,a,u,c,f,l,d,h,v,m,b,_,E,M,k,O,x,C,T,P;return Object(r.__generator)(this,(function(N){switch(N.label){case 0:return n=t.signingDate,i=void 0===n?new Date:n,o=t.expiresIn,s=void 0===o?3600:o,a=t.unsignableHeaders,u=t.signableHeaders,c=t.signingRegion,f=t.signingService,[4,this.credentialProvider()];case 1:return l=N.sent(),null==c?[3,2]:(h=c,[3,4]);case 2:return[4,this.regionProvider()];case 3:h=N.sent(),N.label=4;case 4:return d=h,v=A(i),m=v.longDate,b=v.shortDate,s>604800?[2,Promise.reject("Signature version 4 presigned URLs must have an expiration date less than one week in the future")]:(_=p(b,d,null!=f?f:this.service),E=function(e){var t,n,i="function"==typeof e.clone?e.clone():w(e),o=i.headers,s=i.query,a=void 0===s?{}:s;try{for(var u=Object(r.__values)(Object.keys(o)),c=u.next();!c.done;c=u.next()){var f=c.value;"x-amz-"===f.toLowerCase().substr(0,6)&&(a[f]=o[f],delete o[f])}}catch(e){t={error:e}}finally{try{c&&!c.done&&(n=u.return)&&n.call(u)}finally{if(t)throw t.error}}return Object(r.__assign)(Object(r.__assign)({},e),{headers:o,query:a})}(S(e)),l.sessionToken&&(E.query["X-Amz-Security-Token"]=l.sessionToken),E.query["X-Amz-Algorithm"]="AWS4-HMAC-SHA256",E.query["X-Amz-Credential"]=l.accessKeyId+"/"+_,E.query["X-Amz-Date"]=m,E.query["X-Amz-Expires"]=s.toString(10),M=g(E,a,u),E.query["X-Amz-SignedHeaders"]=I(M),k=E.query,O="X-Amz-Signature",x=this.getSignature,C=[m,_,this.getSigningKey(l,d,b,f)],T=this.createCanonicalRequest,P=[E,M],[4,y(e,this.sha256)]);case 5:return[4,x.apply(this,C.concat([T.apply(this,P.concat([N.sent()]))]))];case 6:return k[O]=N.sent(),[2,E]}}))}))},e.prototype.sign=function(e,t){return Object(r.__awaiter)(this,void 0,void 0,(function(){return Object(r.__generator)(this,(function(n){return"string"==typeof e?[2,this.signString(e,t)]:e.headers&&e.payload?[2,this.signEvent(e,t)]:[2,this.signRequest(e,t)]}))}))},e.prototype.signEvent=function(e,t){var n=e.headers,i=e.payload,o=t.signingDate,s=void 0===o?new Date:o,a=t.priorSignature,u=t.signingRegion,c=t.signingService;return Object(r.__awaiter)(this,void 0,void 0,(function(){var e,t,o,f,d,h,g,m,b,w,_;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return null==u?[3,1]:(t=u,[3,3]);case 1:return[4,this.regionProvider()];case 2:t=r.sent(),r.label=3;case 3:return e=t,o=A(s),f=o.shortDate,d=o.longDate,h=p(f,e,null!=c?c:this.service),[4,y({headers:{},body:i},this.sha256)];case 4:return g=r.sent(),(m=new this.sha256).update(n),w=v.b,[4,m.digest()];case 5:return b=w.apply(void 0,[r.sent()]),_=[l,d,h,a,b,g].join("\n"),[2,this.signString(_,{signingDate:s,signingRegion:e,signingService:c})]}}))}))},e.prototype.signString=function(e,t){var n=void 0===t?{}:t,i=n.signingDate,o=void 0===i?new Date:i,s=n.signingRegion,a=n.signingService;return Object(r.__awaiter)(this,void 0,void 0,(function(){var t,n,i,u,c,f,l,d;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return[4,this.credentialProvider()];case 1:return t=r.sent(),null==s?[3,2]:(i=s,[3,4]);case 2:return[4,this.regionProvider()];case 3:i=r.sent(),r.label=4;case 4:return n=i,u=A(o).shortDate,l=(f=this.sha256).bind,[4,this.getSigningKey(t,n,u,a)];case 5:return(c=new(l.apply(f,[void 0,r.sent()]))).update(e),d=v.b,[4,c.digest()];case 6:return[2,d.apply(void 0,[r.sent()])]}}))}))},e.prototype.signRequest=function(e,t){var n=void 0===t?{}:t,o=n.signingDate,s=void 0===o?new Date:o,u=n.signableHeaders,c=n.unsignableHeaders,f=n.signingRegion,l=n.signingService;return Object(r.__awaiter)(this,void 0,void 0,(function(){var t,n,o,d,h,v,m,b,w,_,E;return Object(r.__generator)(this,(function(M){switch(M.label){case 0:return[4,this.credentialProvider()];case 1:return t=M.sent(),null==f?[3,2]:(o=f,[3,4]);case 2:return[4,this.regionProvider()];case 3:o=M.sent(),M.label=4;case 4:return n=o,d=S(e),h=A(s),v=h.longDate,m=h.shortDate,b=p(m,n,null!=l?l:this.service),d.headers[i]=v,t.sessionToken&&(d.headers[a]=t.sessionToken),[4,y(d,this.sha256)];case 5:return w=M.sent(),!function(e,t){var n,i;e=e.toLowerCase();try{for(var o=Object(r.__values)(Object.keys(t)),s=o.next();!s.done;s=o.next()){if(e===s.value.toLowerCase())return!0}}catch(e){n={error:e}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}return!1}("x-amz-content-sha256",d.headers)&&this.applyChecksum&&(d.headers["x-amz-content-sha256"]=w),_=g(d,c,u),[4,this.getSignature(v,b,this.getSigningKey(t,n,m,l),this.createCanonicalRequest(d,_,w))];case 6:return E=M.sent(),d.headers.authorization="AWS4-HMAC-SHA256 Credential="+t.accessKeyId+"/"+b+", SignedHeaders="+I(_)+", Signature="+E,[2,d]}}))}))},e.prototype.createCanonicalRequest=function(e,t,n){var i=Object.keys(t).sort();return e.method+"\n"+this.getCanonicalPath(e)+"\n"+function(e){var t,n,i=e.query,o=void 0===i?{}:i,a=[],u={},c=function(e){if(e.toLowerCase()===s)return"continue";a.push(e);var t=o[e];"string"==typeof t?u[e]=Object(m.a)(e)+"="+Object(m.a)(t):Array.isArray(t)&&(u[e]=t.slice(0).sort().reduce((function(t,n){return t.concat([Object(m.a)(e)+"="+Object(m.a)(n)])}),[]).join("&"))};try{for(var f=Object(r.__values)(Object.keys(o).sort()),l=f.next();!l.done;l=f.next()){c(l.value)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(n=f.return)&&n.call(f)}finally{if(t)throw t.error}}return a.map((function(e){return u[e]})).filter((function(e){return e})).join("&")}(e)+"\n"+i.map((function(e){return e+":"+t[e]})).join("\n")+"\n\n"+i.join(";")+"\n"+n},e.prototype.createStringToSign=function(e,t,n){return Object(r.__awaiter)(this,void 0,void 0,(function(){var i,o;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return(i=new this.sha256).update(n),[4,i.digest()];case 1:return o=r.sent(),[2,"AWS4-HMAC-SHA256\n"+e+"\n"+t+"\n"+Object(v.b)(o)]}}))}))},e.prototype.getCanonicalPath=function(e){var t=e.path;return this.uriEscapePath?"/"+encodeURIComponent(t.replace(/^\//,"")).replace(/%2F/g,"/"):t},e.prototype.getSignature=function(e,t,n,i){return Object(r.__awaiter)(this,void 0,void 0,(function(){var o,s,a,u,c;return Object(r.__generator)(this,(function(r){switch(r.label){case 0:return[4,this.createStringToSign(e,t,i)];case 1:return o=r.sent(),u=(a=this.sha256).bind,[4,n];case 2:return(s=new(u.apply(a,[void 0,r.sent()]))).update(o),c=v.b,[4,s.digest()];case 3:return[2,c.apply(void 0,[r.sent()])]}}))}))},e.prototype.getSigningKey=function(e,t,n,i){return function(e,t,n,i,o){var s=n+":"+i+":"+o+":"+t.accessKeyId+":"+t.sessionToken;if(s in d)return d[s];for(h.push(s);h.length>50;)delete d[h.shift()];return d[s]=new Promise((function(a,u){var c,f,l=Promise.resolve("AWS4"+t.secretAccessKey),h=function(t){(l=l.then((function(n){return r=t,(i=new e(n)).update(r),i.digest();var r,i}))).catch((function(){}))};try{for(var p=Object(r.__values)([n,i,o,"aws4_request"]),v=p.next();!v.done;v=p.next()){h(v.value)}}catch(e){c={error:e}}finally{try{v&&!v.done&&(f=p.return)&&f.call(p)}finally{if(c)throw c.error}}l.then(a,(function(e){delete d[s],u(e)}))}))}(this.sha256,e,n,t,i||this.service)},e}(),A=function(e){var t=E(e).replace(/[\-:]/g,"");return{longDate:t,shortDate:t.substr(0,8)}},I=function(e){return Object.keys(e).sort().join(";")},k=function(e){if("string"==typeof e){var t=Promise.resolve(e);return function(){return t}}return e},O=function(e){if("object"==typeof e){var t=Promise.resolve(e);return function(){return t}}return e}},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(1),i=function(e){return Object(r.__assign)(Object(r.__assign)({},e),{eventStreamMarshaller:e.eventStreamSerdeProvider(e)})}},function(e,t,n){"use strict";var r=n(7),i=n(162),o=n(8).Buffer,s=new Array(16);function a(){i.call(this,64),this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878}function u(e,t){return e<<t|e>>>32-t}function c(e,t,n,r,i,o,s){return u(e+(t&n|~t&r)+i+o|0,s)+t|0}function f(e,t,n,r,i,o,s){return u(e+(t&r|n&~r)+i+o|0,s)+t|0}function l(e,t,n,r,i,o,s){return u(e+(t^n^r)+i+o|0,s)+t|0}function d(e,t,n,r,i,o,s){return u(e+(n^(t|~r))+i+o|0,s)+t|0}r(a,i),a.prototype._update=function(){for(var e=s,t=0;t<16;++t)e[t]=this._block.readInt32LE(4*t);var n=this._a,r=this._b,i=this._c,o=this._d;n=c(n,r,i,o,e[0],3614090360,7),o=c(o,n,r,i,e[1],3905402710,12),i=c(i,o,n,r,e[2],606105819,17),r=c(r,i,o,n,e[3],3250441966,22),n=c(n,r,i,o,e[4],4118548399,7),o=c(o,n,r,i,e[5],1200080426,12),i=c(i,o,n,r,e[6],2821735955,17),r=c(r,i,o,n,e[7],4249261313,22),n=c(n,r,i,o,e[8],1770035416,7),o=c(o,n,r,i,e[9],2336552879,12),i=c(i,o,n,r,e[10],4294925233,17),r=c(r,i,o,n,e[11],2304563134,22),n=c(n,r,i,o,e[12],1804603682,7),o=c(o,n,r,i,e[13],4254626195,12),i=c(i,o,n,r,e[14],2792965006,17),n=f(n,r=c(r,i,o,n,e[15],1236535329,22),i,o,e[1],4129170786,5),o=f(o,n,r,i,e[6],3225465664,9),i=f(i,o,n,r,e[11],643717713,14),r=f(r,i,o,n,e[0],3921069994,20),n=f(n,r,i,o,e[5],3593408605,5),o=f(o,n,r,i,e[10],38016083,9),i=f(i,o,n,r,e[15],3634488961,14),r=f(r,i,o,n,e[4],3889429448,20),n=f(n,r,i,o,e[9],568446438,5),o=f(o,n,r,i,e[14],3275163606,9),i=f(i,o,n,r,e[3],4107603335,14),r=f(r,i,o,n,e[8],1163531501,20),n=f(n,r,i,o,e[13],2850285829,5),o=f(o,n,r,i,e[2],4243563512,9),i=f(i,o,n,r,e[7],1735328473,14),n=l(n,r=f(r,i,o,n,e[12],2368359562,20),i,o,e[5],4294588738,4),o=l(o,n,r,i,e[8],2272392833,11),i=l(i,o,n,r,e[11],1839030562,16),r=l(r,i,o,n,e[14],4259657740,23),n=l(n,r,i,o,e[1],2763975236,4),o=l(o,n,r,i,e[4],1272893353,11),i=l(i,o,n,r,e[7],4139469664,16),r=l(r,i,o,n,e[10],3200236656,23),n=l(n,r,i,o,e[13],681279174,4),o=l(o,n,r,i,e[0],3936430074,11),i=l(i,o,n,r,e[3],3572445317,16),r=l(r,i,o,n,e[6],76029189,23),n=l(n,r,i,o,e[9],3654602809,4),o=l(o,n,r,i,e[12],3873151461,11),i=l(i,o,n,r,e[15],530742520,16),n=d(n,r=l(r,i,o,n,e[2],3299628645,23),i,o,e[0],4096336452,6),o=d(o,n,r,i,e[7],1126891415,10),i=d(i,o,n,r,e[14],2878612391,15),r=d(r,i,o,n,e[5],4237533241,21),n=d(n,r,i,o,e[12],1700485571,6),o=d(o,n,r,i,e[3],2399980690,10),i=d(i,o,n,r,e[10],4293915773,15),r=d(r,i,o,n,e[1],2240044497,21),n=d(n,r,i,o,e[8],1873313359,6),o=d(o,n,r,i,e[15],4264355552,10),i=d(i,o,n,r,e[6],2734768916,15),r=d(r,i,o,n,e[13],1309151649,21),n=d(n,r,i,o,e[4],4149444226,6),o=d(o,n,r,i,e[11],3174756917,10),i=d(i,o,n,r,e[2],718787259,15),r=d(r,i,o,n,e[9],3951481745,21),this._a=this._a+n|0,this._b=this._b+r|0,this._c=this._c+i|0,this._d=this._d+o|0},a.prototype._digest=function(){this._block[this._blockOffset++]=128,this._blockOffset>56&&(this._block.fill(0,this._blockOffset,64),this._update(),this._blockOffset=0),this._block.fill(0,this._blockOffset,56),this._block.writeUInt32LE(this._length[0],56),this._block.writeUInt32LE(this._length[1],60),this._update();var e=o.allocUnsafe(16);return e.writeInt32LE(this._a,0),e.writeInt32LE(this._b,4),e.writeInt32LE(this._c,8),e.writeInt32LE(this._d,12),e},e.exports=a},function(e,t,n){(function(t){function n(e){try{if(!t.localStorage)return!1}catch(e){return!1}var n=t.localStorage[e];return null!=n&&"true"===String(n).toLowerCase()}e.exports=function(e,t){if(n("noDeprecation"))return e;var r=!1;return function(){if(!r){if(n("throwDeprecation"))throw new Error(t);n("traceDeprecation")?console.trace(t):console.warn(t),r=!0}return e.apply(this,arguments)}}}).call(this,n(31))},function(e,t,n){"use strict";var r=n(67).codes.ERR_STREAM_PREMATURE_CLOSE;function i(){}e.exports=function e(t,n,o){if("function"==typeof n)return e(t,null,n);n||(n={}),o=function(e){var t=!1;return function(){if(!t){t=!0;for(var n=arguments.length,r=new Array(n),i=0;i<n;i++)r[i]=arguments[i];e.apply(this,r)}}}(o||i);var s=n.readable||!1!==n.readable&&t.readable,a=n.writable||!1!==n.writable&&t.writable,u=function(){t.writable||f()},c=t._writableState&&t._writableState.finished,f=function(){a=!1,c=!0,s||o.call(t)},l=t._readableState&&t._readableState.endEmitted,d=function(){s=!1,l=!0,a||o.call(t)},h=function(e){o.call(t,e)},p=function(){var e;return s&&!l?(t._readableState&&t._readableState.ended||(e=new r),o.call(t,e)):a&&!c?(t._writableState&&t._writableState.ended||(e=new r),o.call(t,e)):void 0},v=function(){t.req.on("finish",f)};return!function(e){return e.setHeader&&"function"==typeof e.abort}(t)?a&&!t._writableState&&(t.on("end",u),t.on("close",u)):(t.on("complete",f),t.on("abort",p),t.req?v():t.on("request",v)),t.on("end",d),t.on("finish",f),!1!==n.error&&t.on("error",h),t.on("close",p),function(){t.removeListener("complete",f),t.removeListener("abort",p),t.removeListener("request",v),t.req&&t.req.removeListener("finish",f),t.removeListener("end",u),t.removeListener("close",u),t.removeListener("finish",f),t.removeListener("end",d),t.removeListener("error",h),t.removeListener("close",p)}}},function(e,t,n){"use strict";var r=n(6).Buffer,i=n(7),o=n(162),s=new Array(16),a=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13],u=[5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11],c=[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6],f=[8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11],l=[0,1518500249,1859775393,2400959708,2840853838],d=[1352829926,1548603684,1836072691,2053994217,0];function h(){o.call(this,64),this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520}function p(e,t){return e<<t|e>>>32-t}function v(e,t,n,r,i,o,s,a){return p(e+(t^n^r)+o+s|0,a)+i|0}function g(e,t,n,r,i,o,s,a){return p(e+(t&n|~t&r)+o+s|0,a)+i|0}function m(e,t,n,r,i,o,s,a){return p(e+((t|~n)^r)+o+s|0,a)+i|0}function b(e,t,n,r,i,o,s,a){return p(e+(t&r|n&~r)+o+s|0,a)+i|0}function y(e,t,n,r,i,o,s,a){return p(e+(t^(n|~r))+o+s|0,a)+i|0}i(h,o),h.prototype._update=function(){for(var e=s,t=0;t<16;++t)e[t]=this._block.readInt32LE(4*t);for(var n=0|this._a,r=0|this._b,i=0|this._c,o=0|this._d,h=0|this._e,w=0|this._a,_=0|this._b,S=0|this._c,E=0|this._d,M=0|this._e,A=0;A<80;A+=1){var I,k;A<16?(I=v(n,r,i,o,h,e[a[A]],l[0],c[A]),k=y(w,_,S,E,M,e[u[A]],d[0],f[A])):A<32?(I=g(n,r,i,o,h,e[a[A]],l[1],c[A]),k=b(w,_,S,E,M,e[u[A]],d[1],f[A])):A<48?(I=m(n,r,i,o,h,e[a[A]],l[2],c[A]),k=m(w,_,S,E,M,e[u[A]],d[2],f[A])):A<64?(I=b(n,r,i,o,h,e[a[A]],l[3],c[A]),k=g(w,_,S,E,M,e[u[A]],d[3],f[A])):(I=y(n,r,i,o,h,e[a[A]],l[4],c[A]),k=v(w,_,S,E,M,e[u[A]],d[4],f[A])),n=h,h=o,o=p(i,10),i=r,r=I,w=M,M=E,E=p(S,10),S=_,_=k}var O=this._b+i+E|0;this._b=this._c+o+M|0,this._c=this._d+h+w|0,this._d=this._e+n+_|0,this._e=this._a+r+S|0,this._a=O},h.prototype._digest=function(){this._block[this._blockOffset++]=128,this._blockOffset>56&&(this._block.fill(0,this._blockOffset,64),this._update(),this._blockOffset=0),this._block.fill(0,this._blockOffset,56),this._block.writeUInt32LE(this._length[0],56),this._block.writeUInt32LE(this._length[1],60),this._update();var e=r.alloc?r.alloc(20):new r(20);return e.writeInt32LE(this._a,0),e.writeInt32LE(this._b,4),e.writeInt32LE(this._c,8),e.writeInt32LE(this._d,12),e.writeInt32LE(this._e,16),e},e.exports=h},function(e,t,n){(t=e.exports=function(e){e=e.toLowerCase();var n=t[e];if(!n)throw new Error(e+" is not supported (we accept pull requests)");return new n}).sha=n(281),t.sha1=n(282),t.sha224=n(283),t.sha256=n(169),t.sha384=n(284),t.sha512=n(170)},function(e,t,n){(t=e.exports=n(171)).Stream=t,t.Readable=t,t.Writable=n(120),t.Duplex=n(60),t.Transform=n(174),t.PassThrough=n(291)},function(e,t,n){var r=n(6),i=r.Buffer;function o(e,t){for(var n in e)t[n]=e[n]}function s(e,t,n){return i(e,t,n)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=r:(o(r,t),t.Buffer=s),o(i,s),s.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return i(e,t,n)},s.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=i(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},s.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return i(e)},s.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t,n){"use strict";(function(t,r,i){var o=n(92);function s(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var r=e.entry;e.entry=null;for(;r;){var i=r.callback;t.pendingcb--,i(n),r=r.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=b;var a,u=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?r:o.nextTick;b.WritableState=m;var c=Object.create(n(80));c.inherits=n(7);var f={deprecate:n(114)},l=n(172),d=n(119).Buffer,h=i.Uint8Array||function(){};var p,v=n(173);function g(){}function m(e,t){a=a||n(60),e=e||{};var r=t instanceof a;this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var i=e.highWaterMark,c=e.writableHighWaterMark,f=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:r&&(c||0===c)?c:f,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var l=!1===e.decodeStrings;this.decodeStrings=!l,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,r=n.sync,i=n.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,r,i){--t.pendingcb,n?(o.nextTick(i,r),o.nextTick(M,e,t),e._writableState.errorEmitted=!0,e.emit("error",r)):(i(r),e._writableState.errorEmitted=!0,e.emit("error",r),M(e,t))}(e,n,r,t,i);else{var s=S(n);s||n.corked||n.bufferProcessing||!n.bufferedRequest||_(e,n),r?u(w,e,n,s,i):w(e,n,s,i)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new s(this)}function b(e){if(a=a||n(60),!(p.call(b,this)||this instanceof a))return new b(e);this._writableState=new m(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),l.call(this)}function y(e,t,n,r,i,o,s){t.writelen=r,t.writecb=s,t.writing=!0,t.sync=!0,n?e._writev(i,t.onwrite):e._write(i,o,t.onwrite),t.sync=!1}function w(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),M(e,t)}function _(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,i=new Array(r),o=t.corkedRequestsFree;o.entry=n;for(var a=0,u=!0;n;)i[a]=n,n.isBuf||(u=!1),n=n.next,a+=1;i.allBuffers=u,y(e,t,!0,t.length,i,"",o.finish),t.pendingcb++,t.lastBufferedRequest=null,o.next?(t.corkedRequestsFree=o.next,o.next=null):t.corkedRequestsFree=new s(t),t.bufferedRequestCount=0}else{for(;n;){var c=n.chunk,f=n.encoding,l=n.callback;if(y(e,t,!1,t.objectMode?1:c.length,c,f,l),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function S(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function E(e,t){e._final((function(n){t.pendingcb--,n&&e.emit("error",n),t.prefinished=!0,e.emit("prefinish"),M(e,t)}))}function M(e,t){var n=S(t);return n&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,o.nextTick(E,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),n}c.inherits(b,l),m.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(m.prototype,"buffer",{get:f.deprecate((function(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(p=Function.prototype[Symbol.hasInstance],Object.defineProperty(b,Symbol.hasInstance,{value:function(e){return!!p.call(this,e)||this===b&&(e&&e._writableState instanceof m)}})):p=function(e){return e instanceof this},b.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},b.prototype.write=function(e,t,n){var r,i=this._writableState,s=!1,a=!i.objectMode&&(r=e,d.isBuffer(r)||r instanceof h);return a&&!d.isBuffer(e)&&(e=function(e){return d.from(e)}(e)),"function"==typeof t&&(n=t,t=null),a?t="buffer":t||(t=i.defaultEncoding),"function"!=typeof n&&(n=g),i.ended?function(e,t){var n=new Error("write after end");e.emit("error",n),o.nextTick(t,n)}(this,n):(a||function(e,t,n,r){var i=!0,s=!1;return null===n?s=new TypeError("May not write null values to stream"):"string"==typeof n||void 0===n||t.objectMode||(s=new TypeError("Invalid non-string/buffer chunk")),s&&(e.emit("error",s),o.nextTick(r,s),i=!1),i}(this,i,e,n))&&(i.pendingcb++,s=function(e,t,n,r,i,o){if(!n){var s=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=d.from(t,n));return t}(t,r,i);r!==s&&(n=!0,i="buffer",r=s)}var a=t.objectMode?1:r.length;t.length+=a;var u=t.length<t.highWaterMark;u||(t.needDrain=!0);if(t.writing||t.corked){var c=t.lastBufferedRequest;t.lastBufferedRequest={chunk:r,encoding:i,isBuf:n,callback:o,next:null},c?c.next=t.lastBufferedRequest:t.bufferedRequest=t.lastBufferedRequest,t.bufferedRequestCount+=1}else y(e,t,!1,a,r,i,o);return u}(this,i,a,e,t,n)),s},b.prototype.cork=function(){this._writableState.corked++},b.prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--,e.writing||e.corked||e.finished||e.bufferProcessing||!e.bufferedRequest||_(this,e))},b.prototype.setDefaultEncoding=function(e){if("string"==typeof e&&(e=e.toLowerCase()),!(["hex","utf8","utf-8","ascii","binary","base64","ucs2","ucs-2","utf16le","utf-16le","raw"].indexOf((e+"").toLowerCase())>-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(b.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),b.prototype._write=function(e,t,n){n(new Error("_write() is not implemented"))},b.prototype._writev=null,b.prototype.end=function(e,t,n){var r=this._writableState;"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!=e&&this.write(e,t),r.corked&&(r.corked=1,this.uncork()),r.ending||r.finished||function(e,t,n){t.ending=!0,M(e,t),n&&(t.finished?o.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,r,n)},Object.defineProperty(b.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),b.prototype.destroy=v.destroy,b.prototype._undestroy=v.undestroy,b.prototype._destroy=function(e,t){this.end(),t(e)}}).call(this,n(20),n(289).setImmediate,n(31))},function(e,t,n){"use strict";var r=n(46);function i(e){this.options=e,this.type=this.options.type,this.blockSize=8,this._init(),this.buffer=new Array(this.blockSize),this.bufferOff=0}e.exports=i,i.prototype._init=function(){},i.prototype.update=function(e){return 0===e.length?[]:"decrypt"===this.type?this._updateDecrypt(e):this._updateEncrypt(e)},i.prototype._buffer=function(e,t){for(var n=Math.min(this.buffer.length-this.bufferOff,e.length-t),r=0;r<n;r++)this.buffer[this.bufferOff+r]=e[t+r];return this.bufferOff+=n,n},i.prototype._flushBuffer=function(e,t){return this._update(this.buffer,0,e,t),this.bufferOff=0,this.blockSize},i.prototype._updateEncrypt=function(e){var t=0,n=0,r=(this.bufferOff+e.length)/this.blockSize|0,i=new Array(r*this.blockSize);0!==this.bufferOff&&(t+=this._buffer(e,t),this.bufferOff===this.buffer.length&&(n+=this._flushBuffer(i,n)));for(var o=e.length-(e.length-t)%this.blockSize;t<o;t+=this.blockSize)this._update(e,t,i,n),n+=this.blockSize;for(;t<e.length;t++,this.bufferOff++)this.buffer[this.bufferOff]=e[t];return i},i.prototype._updateDecrypt=function(e){for(var t=0,n=0,r=Math.ceil((this.bufferOff+e.length)/this.blockSize)-1,i=new Array(r*this.blockSize);r>0;r--)t+=this._buffer(e,t),n+=this._flushBuffer(i,n);return t+=this._buffer(e,t),i},i.prototype.final=function(e){var t,n;return e&&(t=this.update(e)),n="encrypt"===this.type?this._finalEncrypt():this._finalDecrypt(),t?t.concat(n):n},i.prototype._pad=function(e,t){if(0===t)return!1;for(;t<e.length;)e[t++]=0;return!0},i.prototype._finalEncrypt=function(){if(!this._pad(this.buffer,this.bufferOff))return[];var e=new Array(this.blockSize);return this._update(this.buffer,0,e,0),e},i.prototype._unpad=function(e){return e},i.prototype._finalDecrypt=function(){r.equal(this.bufferOff,this.blockSize,"Not enough data to decrypt");var e=new Array(this.blockSize);return this._flushBuffer(e,0),this._unpad(e)}},function(e,t,n){var r=n(304),i=n(312),o=n(187);t.createCipher=t.Cipher=r.createCipher,t.createCipheriv=t.Cipheriv=r.createCipheriv,t.createDecipher=t.Decipher=i.createDecipher,t.createDecipheriv=t.Decipheriv=i.createDecipheriv,t.listCiphers=t.getCiphers=function(){return Object.keys(o)}},function(e,t,n){var r={ECB:n(305),CBC:n(306),CFB:n(307),CFB8:n(308),CFB1:n(309),OFB:n(310),CTR:n(185),GCM:n(185)},i=n(187);for(var o in i)i[o].module=r[i[o].mode];e.exports=i},function(e,t,n){var r;function i(e){this.rand=e}if(e.exports=function(e){return r||(r=new i(null)),r.generate(e)},e.exports.Rand=i,i.prototype.generate=function(e){return this._rand(e)},i.prototype._rand=function(e){if(this.rand.getBytes)return this.rand.getBytes(e);for(var t=new Uint8Array(e),n=0;n<t.length;n++)t[n]=this.rand.getByte();return t},"object"==typeof self)self.crypto&&self.crypto.getRandomValues?i.prototype._rand=function(e){var t=new Uint8Array(e);return self.crypto.getRandomValues(t),t}:self.msCrypto&&self.msCrypto.getRandomValues?i.prototype._rand=function(e){var t=new Uint8Array(e);return self.msCrypto.getRandomValues(t),t}:"object"==typeof window&&(i.prototype._rand=function(){throw new Error("Not implemented yet")});else try{var o=n(316);if("function"!=typeof o.randomBytes)throw new Error("Not supported");i.prototype._rand=function(e){return o.randomBytes(e)}}catch(e){}},function(e,t,n){"use strict";var r=n(70).codes.ERR_STREAM_PREMATURE_CLOSE;function i(){}e.exports=function e(t,n,o){if("function"==typeof n)return e(t,null,n);n||(n={}),o=function(e){var t=!1;return function(){if(!t){t=!0;for(var n=arguments.length,r=new Array(n),i=0;i<n;i++)r[i]=arguments[i];e.apply(this,r)}}}(o||i);var s=n.readable||!1!==n.readable&&t.readable,a=n.writable||!1!==n.writable&&t.writable,u=function(){t.writable||f()},c=t._writableState&&t._writableState.finished,f=function(){a=!1,c=!0,s||o.call(t)},l=t._readableState&&t._readableState.endEmitted,d=function(){s=!1,l=!0,a||o.call(t)},h=function(e){o.call(t,e)},p=function(){var e;return s&&!l?(t._readableState&&t._readableState.ended||(e=new r),o.call(t,e)):a&&!c?(t._writableState&&t._writableState.ended||(e=new r),o.call(t,e)):void 0},v=function(){t.req.on("finish",f)};return!function(e){return e.setHeader&&"function"==typeof e.abort}(t)?a&&!t._writableState&&(t.on("end",u),t.on("close",u)):(t.on("complete",f),t.on("abort",p),t.req?v():t.on("request",v)),t.on("end",d),t.on("finish",f),!1!==n.error&&t.on("error",h),t.on("close",p),function(){t.removeListener("complete",f),t.removeListener("abort",p),t.removeListener("request",v),t.req&&t.req.removeListener("finish",f),t.removeListener("end",u),t.removeListener("close",u),t.removeListener("finish",f),t.removeListener("end",d),t.removeListener("error",h),t.removeListener("close",p)}}},function(e,t,n){(function(t){var r=n(329),i=n(66);function o(e){var t,n=e.modulus.byteLength();do{t=new r(i(n))}while(t.cmp(e.modulus)>=0||!t.umod(e.prime1)||!t.umod(e.prime2));return t}function s(e,n){var i=function(e){var t=o(e);return{blinder:t.toRed(r.mont(e.modulus)).redPow(new r(e.publicExponent)).fromRed(),unblinder:t.invm(e.modulus)}}(n),s=n.modulus.byteLength(),a=new r(e).mul(i.blinder).umod(n.modulus),u=a.toRed(r.mont(n.prime1)),c=a.toRed(r.mont(n.prime2)),f=n.coefficient,l=n.prime1,d=n.prime2,h=u.redPow(n.exponent1).fromRed(),p=c.redPow(n.exponent2).fromRed(),v=h.isub(p).imul(f).umod(l).imul(d);return p.iadd(v).imul(i.unblinder).umod(n.modulus).toArrayLike(t,"be",s)}s.getr=o,e.exports=s}).call(this,n(6).Buffer)},function(e,t,n){"use strict";var r=t;r.version=n(331).version,r.utils=n(47),r.rand=n(124),r.curve=n(199),r.curves=n(128),r.ec=n(342),r.eddsa=n(346)},function(e,t,n){"use strict";var r,i=t,o=n(129),s=n(199),a=n(47).assert;function u(e){"short"===e.type?this.curve=new s.short(e):"edwards"===e.type?this.curve=new s.edwards(e):this.curve=new s.mont(e),this.g=this.curve.g,this.n=this.curve.n,this.hash=e.hash,a(this.g.validate(),"Invalid curve"),a(this.g.mul(this.n).isInfinity(),"Invalid curve, G*N != O")}function c(e,t){Object.defineProperty(i,e,{configurable:!0,enumerable:!0,get:function(){var n=new u(t);return Object.defineProperty(i,e,{configurable:!0,enumerable:!0,value:n}),n}})}i.PresetCurve=u,c("p192",{type:"short",prime:"p192",p:"ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff",a:"ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc",b:"64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1",n:"ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831",hash:o.sha256,gRed:!1,g:["188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012","07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811"]}),c("p224",{type:"short",prime:"p224",p:"ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001",a:"ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe",b:"b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4",n:"ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d",hash:o.sha256,gRed:!1,g:["b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21","bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34"]}),c("p256",{type:"short",prime:null,p:"ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff",a:"ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc",b:"5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b",n:"ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551",hash:o.sha256,gRed:!1,g:["6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296","4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5"]}),c("p384",{type:"short",prime:null,p:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ffffffff 00000000 00000000 ffffffff",a:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ffffffff 00000000 00000000 fffffffc",b:"b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef",n:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 f4372ddf 581a0db2 48b0a77a ecec196a ccc52973",hash:o.sha384,gRed:!1,g:["aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7","3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f"]}),c("p521",{type:"short",prime:null,p:"000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff",a:"000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffc",b:"00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b 99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd 3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00",n:"000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409",hash:o.sha512,gRed:!1,g:["000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66","00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 3fad0761 353c7086 a272c240 88be9476 9fd16650"]}),c("curve25519",{type:"mont",prime:"p25519",p:"7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed",a:"76d06",b:"1",n:"1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed",hash:o.sha256,gRed:!1,g:["9"]}),c("ed25519",{type:"edwards",prime:"p25519",p:"7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed",a:"-1",c:"1",d:"52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3",n:"1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed",hash:o.sha256,gRed:!1,g:["216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a","6666666666666666666666666666666666666666666666666666666666666658"]});try{r=n(341)}catch(e){r=void 0}c("secp256k1",{type:"short",prime:"k256",p:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f",a:"0",b:"7",n:"ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141",h:"1",hash:o.sha256,beta:"7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee",lambda:"5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72",basis:[{a:"3086d221a7d46bcde86c90e49284eb15",b:"-e4437ed6010e88286f547fa90abfe4c3"},{a:"114ca50f7a8e2f3f657c1108d9d44cfd8",b:"3086d221a7d46bcde86c90e49284eb15"}],gRed:!1,g:["79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",r]})},function(e,t,n){var r=t;r.utils=n(51),r.common=n(82),r.sha=n(335),r.ripemd=n(339),r.hmac=n(340),r.sha1=r.sha.sha1,r.sha256=r.sha.sha256,r.sha224=r.sha.sha224,r.sha384=r.sha.sha384,r.sha512=r.sha.sha512,r.ripemd160=r.ripemd.ripemd160},function(e,t,n){"use strict";(function(t){var r,i=n(6),o=i.Buffer,s={};for(r in i)i.hasOwnProperty(r)&&"SlowBuffer"!==r&&"Buffer"!==r&&(s[r]=i[r]);var a=s.Buffer={};for(r in o)o.hasOwnProperty(r)&&"allocUnsafe"!==r&&"allocUnsafeSlow"!==r&&(a[r]=o[r]);if(s.Buffer.prototype=o.prototype,a.from&&a.from!==Uint8Array.from||(a.from=function(e,t,n){if("number"==typeof e)throw new TypeError('The "value" argument must not be of type number. Received type '+typeof e);if(e&&void 0===e.length)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);return o(e,t,n)}),a.alloc||(a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError('The "size" argument must be of type number. Received type '+typeof e);if(e<0||e>=2*(1<<30))throw new RangeError('The value "'+e+'" is invalid for option "size"');var r=o(e);return t&&0!==t.length?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r}),!s.kStringMaxLength)try{s.kStringMaxLength=t.binding("buffer").kStringMaxLength}catch(e){}s.constants||(s.constants={MAX_LENGTH:s.kMaxLength},s.kStringMaxLength&&(s.constants.MAX_STRING_LENGTH=s.kStringMaxLength)),e.exports=s}).call(this,n(20))},function(e,t,n){"use strict";const r=n(132).Reporter,i=n(83).EncoderBuffer,o=n(83).DecoderBuffer,s=n(46),a=["seq","seqof","set","setof","objid","bool","gentime","utctime","null_","enum","int","objDesc","bitstr","bmpstr","charstr","genstr","graphstr","ia5str","iso646str","numstr","octstr","printstr","t61str","unistr","utf8str","videostr"],u=["key","obj","use","optional","explicit","implicit","def","choice","any","contains"].concat(a);function c(e,t,n){const r={};this._baseState=r,r.name=n,r.enc=e,r.parent=t||null,r.children=null,r.tag=null,r.args=null,r.reverseArgs=null,r.choice=null,r.optional=!1,r.any=!1,r.obj=!1,r.use=null,r.useDecoder=null,r.key=null,r.default=null,r.explicit=null,r.implicit=null,r.contains=null,r.parent||(r.children=[],this._wrap())}e.exports=c;const f=["enc","parent","children","tag","args","reverseArgs","choice","optional","any","obj","use","alteredUse","key","default","explicit","implicit","contains"];c.prototype.clone=function(){const e=this._baseState,t={};f.forEach((function(n){t[n]=e[n]}));const n=new this.constructor(t.parent);return n._baseState=t,n},c.prototype._wrap=function(){const e=this._baseState;u.forEach((function(t){this[t]=function(){const n=new this.constructor(this);return e.children.push(n),n[t].apply(n,arguments)}}),this)},c.prototype._init=function(e){const t=this._baseState;s(null===t.parent),e.call(this),t.children=t.children.filter((function(e){return e._baseState.parent===this}),this),s.equal(t.children.length,1,"Root node can have only one child")},c.prototype._useArgs=function(e){const t=this._baseState,n=e.filter((function(e){return e instanceof this.constructor}),this);e=e.filter((function(e){return!(e instanceof this.constructor)}),this),0!==n.length&&(s(null===t.children),t.children=n,n.forEach((function(e){e._baseState.parent=this}),this)),0!==e.length&&(s(null===t.args),t.args=e,t.reverseArgs=e.map((function(e){if("object"!=typeof e||e.constructor!==Object)return e;const t={};return Object.keys(e).forEach((function(n){n==(0|n)&&(n|=0);const r=e[n];t[r]=n})),t})))},["_peekTag","_decodeTag","_use","_decodeStr","_decodeObjid","_decodeTime","_decodeNull","_decodeInt","_decodeBool","_decodeList","_encodeComposite","_encodeStr","_encodeObjid","_encodeTime","_encodeNull","_encodeInt","_encodeBool"].forEach((function(e){c.prototype[e]=function(){const t=this._baseState;throw new Error(e+" not implemented for encoding: "+t.enc)}})),a.forEach((function(e){c.prototype[e]=function(){const t=this._baseState,n=Array.prototype.slice.call(arguments);return s(null===t.tag),t.tag=e,this._useArgs(n),this}})),c.prototype.use=function(e){s(e);const t=this._baseState;return s(null===t.use),t.use=e,this},c.prototype.optional=function(){return this._baseState.optional=!0,this},c.prototype.def=function(e){const t=this._baseState;return s(null===t.default),t.default=e,t.optional=!0,this},c.prototype.explicit=function(e){const t=this._baseState;return s(null===t.explicit&&null===t.implicit),t.explicit=e,this},c.prototype.implicit=function(e){const t=this._baseState;return s(null===t.explicit&&null===t.implicit),t.implicit=e,this},c.prototype.obj=function(){const e=this._baseState,t=Array.prototype.slice.call(arguments);return e.obj=!0,0!==t.length&&this._useArgs(t),this},c.prototype.key=function(e){const t=this._baseState;return s(null===t.key),t.key=e,this},c.prototype.any=function(){return this._baseState.any=!0,this},c.prototype.choice=function(e){const t=this._baseState;return s(null===t.choice),t.choice=e,this._useArgs(Object.keys(e).map((function(t){return e[t]}))),this},c.prototype.contains=function(e){const t=this._baseState;return s(null===t.use),t.contains=e,this},c.prototype._decode=function(e,t){const n=this._baseState;if(null===n.parent)return e.wrapResult(n.children[0]._decode(e,t));let r,i=n.default,s=!0,a=null;if(null!==n.key&&(a=e.enterKey(n.key)),n.optional){let r=null;if(null!==n.explicit?r=n.explicit:null!==n.implicit?r=n.implicit:null!==n.tag&&(r=n.tag),null!==r||n.any){if(s=this._peekTag(e,r,n.any),e.isError(s))return s}else{const r=e.save();try{null===n.choice?this._decodeGeneric(n.tag,e,t):this._decodeChoice(e,t),s=!0}catch(e){s=!1}e.restore(r)}}if(n.obj&&s&&(r=e.enterObject()),s){if(null!==n.explicit){const t=this._decodeTag(e,n.explicit);if(e.isError(t))return t;e=t}const r=e.offset;if(null===n.use&&null===n.choice){let t;n.any&&(t=e.save());const r=this._decodeTag(e,null!==n.implicit?n.implicit:n.tag,n.any);if(e.isError(r))return r;n.any?i=e.raw(t):e=r}if(t&&t.track&&null!==n.tag&&t.track(e.path(),r,e.length,"tagged"),t&&t.track&&null!==n.tag&&t.track(e.path(),e.offset,e.length,"content"),n.any||(i=null===n.choice?this._decodeGeneric(n.tag,e,t):this._decodeChoice(e,t)),e.isError(i))return i;if(n.any||null!==n.choice||null===n.children||n.children.forEach((function(n){n._decode(e,t)})),n.contains&&("octstr"===n.tag||"bitstr"===n.tag)){const r=new o(i);i=this._getUse(n.contains,e._reporterState.obj)._decode(r,t)}}return n.obj&&s&&(i=e.leaveObject(r)),null===n.key||null===i&&!0!==s?null!==a&&e.exitKey(a):e.leaveKey(a,n.key,i),i},c.prototype._decodeGeneric=function(e,t,n){const r=this._baseState;return"seq"===e||"set"===e?null:"seqof"===e||"setof"===e?this._decodeList(t,e,r.args[0],n):/str$/.test(e)?this._decodeStr(t,e,n):"objid"===e&&r.args?this._decodeObjid(t,r.args[0],r.args[1],n):"objid"===e?this._decodeObjid(t,null,null,n):"gentime"===e||"utctime"===e?this._decodeTime(t,e,n):"null_"===e?this._decodeNull(t,n):"bool"===e?this._decodeBool(t,n):"objDesc"===e?this._decodeStr(t,e,n):"int"===e||"enum"===e?this._decodeInt(t,r.args&&r.args[0],n):null!==r.use?this._getUse(r.use,t._reporterState.obj)._decode(t,n):t.error("unknown tag: "+e)},c.prototype._getUse=function(e,t){const n=this._baseState;return n.useDecoder=this._use(e,t),s(null===n.useDecoder._baseState.parent),n.useDecoder=n.useDecoder._baseState.children[0],n.implicit!==n.useDecoder._baseState.implicit&&(n.useDecoder=n.useDecoder.clone(),n.useDecoder._baseState.implicit=n.implicit),n.useDecoder},c.prototype._decodeChoice=function(e,t){const n=this._baseState;let r=null,i=!1;return Object.keys(n.choice).some((function(o){const s=e.save(),a=n.choice[o];try{const n=a._decode(e,t);if(e.isError(n))return!1;r={type:o,value:n},i=!0}catch(t){return e.restore(s),!1}return!0}),this),i?r:e.error("Choice not matched")},c.prototype._createEncoderBuffer=function(e){return new i(e,this.reporter)},c.prototype._encode=function(e,t,n){const r=this._baseState;if(null!==r.default&&r.default===e)return;const i=this._encodeValue(e,t,n);return void 0===i||this._skipDefault(i,t,n)?void 0:i},c.prototype._encodeValue=function(e,t,n){const i=this._baseState;if(null===i.parent)return i.children[0]._encode(e,t||new r);let o=null;if(this.reporter=t,i.optional&&void 0===e){if(null===i.default)return;e=i.default}let s=null,a=!1;if(i.any)o=this._createEncoderBuffer(e);else if(i.choice)o=this._encodeChoice(e,t);else if(i.contains)s=this._getUse(i.contains,n)._encode(e,t),a=!0;else if(i.children)s=i.children.map((function(n){if("null_"===n._baseState.tag)return n._encode(null,t,e);if(null===n._baseState.key)return t.error("Child should have a key");const r=t.enterKey(n._baseState.key);if("object"!=typeof e)return t.error("Child expected, but input is not object");const i=n._encode(e[n._baseState.key],t,e);return t.leaveKey(r),i}),this).filter((function(e){return e})),s=this._createEncoderBuffer(s);else if("seqof"===i.tag||"setof"===i.tag){if(!i.args||1!==i.args.length)return t.error("Too many args for : "+i.tag);if(!Array.isArray(e))return t.error("seqof/setof, but data is not Array");const n=this.clone();n._baseState.implicit=null,s=this._createEncoderBuffer(e.map((function(n){const r=this._baseState;return this._getUse(r.args[0],e)._encode(n,t)}),n))}else null!==i.use?o=this._getUse(i.use,n)._encode(e,t):(s=this._encodePrimitive(i.tag,e),a=!0);if(!i.any&&null===i.choice){const e=null!==i.implicit?i.implicit:i.tag,n=null===i.implicit?"universal":"context";null===e?null===i.use&&t.error("Tag could be omitted only for .use()"):null===i.use&&(o=this._encodeComposite(e,a,n,s))}return null!==i.explicit&&(o=this._encodeComposite(i.explicit,!1,"context",o)),o},c.prototype._encodeChoice=function(e,t){const n=this._baseState,r=n.choice[e.type];return r||s(!1,e.type+" not found in "+JSON.stringify(Object.keys(n.choice))),r._encode(e.value,t)},c.prototype._encodePrimitive=function(e,t){const n=this._baseState;if(/str$/.test(e))return this._encodeStr(t,e);if("objid"===e&&n.args)return this._encodeObjid(t,n.reverseArgs[0],n.args[1]);if("objid"===e)return this._encodeObjid(t,null,null);if("gentime"===e||"utctime"===e)return this._encodeTime(t,e);if("null_"===e)return this._encodeNull();if("int"===e||"enum"===e)return this._encodeInt(t,n.args&&n.reverseArgs[0]);if("bool"===e)return this._encodeBool(t);if("objDesc"===e)return this._encodeStr(t,e);throw new Error("Unsupported tag: "+e)},c.prototype._isNumstr=function(e){return/^[0-9 ]*$/.test(e)},c.prototype._isPrintstr=function(e){return/^[A-Za-z0-9 '()+,-./:=?]*$/.test(e)}},function(e,t,n){"use strict";const r=n(7);function i(e){this._reporterState={obj:null,path:[],options:e||{},errors:[]}}function o(e,t){this.path=e,this.rethrow(t)}t.Reporter=i,i.prototype.isError=function(e){return e instanceof o},i.prototype.save=function(){const e=this._reporterState;return{obj:e.obj,pathLen:e.path.length}},i.prototype.restore=function(e){const t=this._reporterState;t.obj=e.obj,t.path=t.path.slice(0,e.pathLen)},i.prototype.enterKey=function(e){return this._reporterState.path.push(e)},i.prototype.exitKey=function(e){const t=this._reporterState;t.path=t.path.slice(0,e-1)},i.prototype.leaveKey=function(e,t,n){const r=this._reporterState;this.exitKey(e),null!==r.obj&&(r.obj[t]=n)},i.prototype.path=function(){return this._reporterState.path.join("/")},i.prototype.enterObject=function(){const e=this._reporterState,t=e.obj;return e.obj={},t},i.prototype.leaveObject=function(e){const t=this._reporterState,n=t.obj;return t.obj=e,n},i.prototype.error=function(e){let t;const n=this._reporterState,r=e instanceof o;if(t=r?e:new o(n.path.map((function(e){return"["+JSON.stringify(e)+"]"})).join(""),e.message||e,e.stack),!n.options.partial)throw t;return r||n.errors.push(t),t},i.prototype.wrapResult=function(e){const t=this._reporterState;return t.options.partial?{result:this.isError(e)?null:e,errors:t.errors}:e},r(o,Error),o.prototype.rethrow=function(e){if(this.message=e+" at: "+(this.path||"(shallow)"),Error.captureStackTrace&&Error.captureStackTrace(this,o),!this.stack)try{throw new Error(this.message)}catch(e){this.stack=e.stack}return this}},function(e,t,n){"use strict";function r(e){const t={};return Object.keys(e).forEach((function(n){(0|n)==n&&(n|=0);const r=e[n];t[r]=n})),t}t.tagClass={0:"universal",1:"application",2:"context",3:"private"},t.tagClassByName=r(t.tagClass),t.tag={0:"end",1:"bool",2:"int",3:"bitstr",4:"octstr",5:"null_",6:"objid",7:"objDesc",8:"external",9:"real",10:"enum",11:"embed",12:"utf8str",13:"relativeOid",16:"seq",17:"set",18:"numstr",19:"printstr",20:"t61str",21:"videostr",22:"ia5str",23:"utctime",24:"gentime",25:"graphstr",26:"iso646str",27:"genstr",28:"unistr",29:"charstr",30:"bmpstr"},t.tagByName=r(t.tag)},function(e,t,n){"use strict";n.r(t),n.d(t,"locateWindow",(function(){return i}));var r={};function i(){return"undefined"!=typeof window?window:"undefined"!=typeof self?self:r}},function(e,t,n){var r=n(84),i=n(85);e.exports=function(e){return"symbol"==typeof e||i(e)&&"[object Symbol]"==r(e)}},function(e,t,n){var r=n(395),i=n(411),o=n(413),s=n(414),a=n(415);function u(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}u.prototype.clear=r,u.prototype.delete=i,u.prototype.get=o,u.prototype.has=s,u.prototype.set=a,e.exports=u},function(e,t,n){var r=n(72)(n(53),"Map");e.exports=r},function(e,t,n){(function(e){var r=n(53),i=n(427),o=t&&!t.nodeType&&t,s=o&&"object"==typeof e&&e&&!e.nodeType&&e,a=s&&s.exports===o?r.Buffer:void 0,u=(a?a.isBuffer:void 0)||i;e.exports=u}).call(this,n(57)(e))},function(e,t,n){var r=n(428),i=n(429),o=n(430),s=o&&o.isTypedArray,a=s?i(s):r;e.exports=a},function(e,t,n){"use strict";n.d(t,"a",(function(){return $s})),n.d(t,"b",(function(){return Ks}));var r=n(44),i=n(221),o=n(88),s=n(89),a=n(50),u=function(e,t){return(u=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function c(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}u(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var f=function(){return(f=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function l(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function d(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}Object.create;Object.create;var h,p,v,g,m,b,y,w,_,S,E,M,A,I,k,O,x,C,T,P,N,R,L,j,D,U,B,F,z,q,K,H,V,G,W,$,Y,J,Z,X,Q,ee,te,ne,re,ie,oe,se,ae,ue,ce,fe,le,de,he,pe,ve,ge,me,be,ye,we,_e,Se,Ee,Me,Ae,Ie,ke,Oe,xe,Ce,Te,Pe,Ne,Re,Le,je,De,Ue,Be,Fe,ze,qe,Ke,He,Ve,Ge,We,$e,Ye,Je,Ze,Xe,Qe,et,tt,nt,rt,it,ot,st,at,ut,ct,ft,lt,dt,ht,pt,vt,gt,mt,bt,yt,wt,_t,St,Et,Mt,At,It,kt,Ot,xt,Ct,Tt,Pt,Nt,Rt,Lt,jt,Dt,Ut,Bt,Ft,zt,qt,Kt,Ht,Vt,Gt,Wt,$t,Yt,Jt,Zt,Xt,Qt,en,tn,nn,rn,on,sn,an,un,cn,fn,ln,dn,hn,pn,vn,gn,mn,bn,yn,wn,_n,Sn,En,Mn,An,In,kn,On,xn,Cn,Tn,Pn,Nn,Rn,Ln,jn,Dn,Un,Bn,Fn,zn,qn,Kn,Hn,Vn,Gn,Wn,$n,Yn,Jn,Zn,Xn,Qn,er,tr,nr,rr,ir,or,sr,ar,ur,cr,fr,lr,dr,hr,pr,vr,gr,mr,br,yr,wr,_r,Sr,Er,Mr,Ar,Ir,kr,Or,xr,Cr,Tr,Pr,Nr,Rr,Lr,jr,Dr,Ur,Br,Fr,zr,qr,Kr,Hr,Vr,Gr,Wr,$r,Yr,Jr,Zr=n(0);(h||(h={})).filterSensitiveLog=function(e){return f({},e)},(p||(p={})).filterSensitiveLog=function(e){return f({},e)},(v||(v={})).filterSensitiveLog=function(e){return f({},e)},(g||(g={})).filterSensitiveLog=function(e){return f({},e)},(m||(m={})).filterSensitiveLog=function(e){return f({},e)},(b||(b={})).filterSensitiveLog=function(e){return f({},e)},(y||(y={})).filterSensitiveLog=function(e){return f({},e)},(w||(w={})).filterSensitiveLog=function(e){return f({},e)},(_||(_={})).filterSensitiveLog=function(e){return f({},e)},(S||(S={})).filterSensitiveLog=function(e){return f({},e)},(E||(E={})).filterSensitiveLog=function(e){return f(f({},e),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(M||(M={})).filterSensitiveLog=function(e){return f({},e)},(A||(A={})).filterSensitiveLog=function(e){return f({},e)},(I||(I={})).filterSensitiveLog=function(e){return f({},e)},(k||(k={})).filterSensitiveLog=function(e){return f({},e)},(O||(O={})).filterSensitiveLog=function(e){return f(f(f({},e),e.SSEKMSEncryptionContext&&{SSEKMSEncryptionContext:Zr.d}),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(x||(x={})).filterSensitiveLog=function(e){return f(f(f(f(f({},e),e.SSEKMSEncryptionContext&&{SSEKMSEncryptionContext:Zr.d}),e.SSECustomerKey&&{SSECustomerKey:Zr.d}),e.CopySourceSSECustomerKey&&{CopySourceSSECustomerKey:Zr.d}),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(C||(C={})).filterSensitiveLog=function(e){return f({},e)},(T||(T={})).filterSensitiveLog=function(e){return f({},e)},(P||(P={})).filterSensitiveLog=function(e){return f({},e)},(N||(N={})).filterSensitiveLog=function(e){return f({},e)},(R||(R={})).filterSensitiveLog=function(e){return f({},e)},(L||(L={})).filterSensitiveLog=function(e){return f({},e)},(j||(j={})).filterSensitiveLog=function(e){return f(f(f({},e),e.SSEKMSEncryptionContext&&{SSEKMSEncryptionContext:Zr.d}),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(D||(D={})).filterSensitiveLog=function(e){return f(f(f(f({},e),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d}),e.SSEKMSEncryptionContext&&{SSEKMSEncryptionContext:Zr.d}),e.SSECustomerKey&&{SSECustomerKey:Zr.d})},(U||(U={})).filterSensitiveLog=function(e){return f({},e)},(B||(B={})).filterSensitiveLog=function(e){return f({},e)},(F||(F={})).filterSensitiveLog=function(e){return f({},e)},(z||(z={})).filterSensitiveLog=function(e){return f({},e)},(q||(q={})).filterSensitiveLog=function(e){return f({},e)},(K||(K={})).filterSensitiveLog=function(e){return f({},e)},(H||(H={})).filterSensitiveLog=function(e){return f({},e)},(V||(V={})).filterSensitiveLog=function(e){return f({},e)},(G||(G={})).filterSensitiveLog=function(e){return f({},e)},(W||(W={})).filterSensitiveLog=function(e){return f({},e)},($||($={})).filterSensitiveLog=function(e){return f({},e)},(Y||(Y={})).filterSensitiveLog=function(e){return f({},e)},(J||(J={})).filterSensitiveLog=function(e){return f({},e)},(Z||(Z={})).filterSensitiveLog=function(e){return f({},e)},(X||(X={})).filterSensitiveLog=function(e){return f({},e)},(Q||(Q={})).filterSensitiveLog=function(e){return f({},e)},(ee||(ee={})).filterSensitiveLog=function(e){return f({},e)},(te||(te={})).filterSensitiveLog=function(e){return f({},e)},(ne||(ne={})).filterSensitiveLog=function(e){return f({},e)},(re||(re={})).filterSensitiveLog=function(e){return f({},e)},(ie||(ie={})).filterSensitiveLog=function(e){return f({},e)},(oe||(oe={})).filterSensitiveLog=function(e){return f({},e)},(se||(se={})).filterSensitiveLog=function(e){return f({},e)},(ae||(ae={})).filterSensitiveLog=function(e){return f({},e)},(ue||(ue={})).filterSensitiveLog=function(e){return f({},e)},(ce||(ce={})).filterSensitiveLog=function(e){return f({},e)},(fe||(fe={})).filterSensitiveLog=function(e){return f({},e)},(le||(le={})).filterSensitiveLog=function(e){return f({},e)},(de||(de={})).filterSensitiveLog=function(e){return f({},e)},(he||(he={})).filterSensitiveLog=function(e){return f({},e)},(pe||(pe={})).filterSensitiveLog=function(e){return f({},e)},(ve||(ve={})).filterSensitiveLog=function(e){return f({},e)},(ge||(ge={})).filterSensitiveLog=function(e){return f({},e)},(me||(me={})).filterSensitiveLog=function(e){return f({},e)},(be||(be={})).filterSensitiveLog=function(e){return f({},e)},(ye||(ye={})).filterSensitiveLog=function(e){return f({},e)},(we||(we={})).filterSensitiveLog=function(e){return f({},e)},(_e||(_e={})).filterSensitiveLog=function(e){return f({},e)},(Se||(Se={})).filterSensitiveLog=function(e){return f({},e)},(Ee||(Ee={})).filterSensitiveLog=function(e){return f({},e)},(Me||(Me={})).filterSensitiveLog=function(e){return f(f({},e),e.KMSMasterKeyID&&{KMSMasterKeyID:Zr.d})},(Ae||(Ae={})).filterSensitiveLog=function(e){return f(f({},e),e.ApplyServerSideEncryptionByDefault&&{ApplyServerSideEncryptionByDefault:Me.filterSensitiveLog(e.ApplyServerSideEncryptionByDefault)})},(Ie||(Ie={})).filterSensitiveLog=function(e){return f(f({},e),e.Rules&&{Rules:e.Rules.map((function(e){return Ae.filterSensitiveLog(e)}))})},(ke||(ke={})).filterSensitiveLog=function(e){return f(f({},e),e.ServerSideEncryptionConfiguration&&{ServerSideEncryptionConfiguration:Ie.filterSensitiveLog(e.ServerSideEncryptionConfiguration)})},(Oe||(Oe={})).filterSensitiveLog=function(e){return f({},e)},(xe||(xe={})).filterSensitiveLog=function(e){return f(f({},e),e.KeyId&&{KeyId:Zr.d})},(Ce||(Ce={})).filterSensitiveLog=function(e){return f({},e)},(Te||(Te={})).filterSensitiveLog=function(e){return f(f({},e),e.SSEKMS&&{SSEKMS:xe.filterSensitiveLog(e.SSEKMS)})},(Pe||(Pe={})).filterSensitiveLog=function(e){return f(f({},e),e.Encryption&&{Encryption:Te.filterSensitiveLog(e.Encryption)})},(Ne||(Ne={})).filterSensitiveLog=function(e){return f(f({},e),e.S3BucketDestination&&{S3BucketDestination:Pe.filterSensitiveLog(e.S3BucketDestination)})},(Re||(Re={})).filterSensitiveLog=function(e){return f({},e)},(Le||(Le={})).filterSensitiveLog=function(e){return f({},e)},(je||(je={})).filterSensitiveLog=function(e){return f(f({},e),e.Destination&&{Destination:Ne.filterSensitiveLog(e.Destination)})},(De||(De={})).filterSensitiveLog=function(e){return f(f({},e),e.InventoryConfiguration&&{InventoryConfiguration:je.filterSensitiveLog(e.InventoryConfiguration)})},(Ue||(Ue={})).filterSensitiveLog=function(e){return f({},e)},(Be||(Be={})).filterSensitiveLog=function(e){return f({},e)},(Fe||(Fe={})).filterSensitiveLog=function(e){return f({},e)},(ze||(ze={})).filterSensitiveLog=function(e){return f({},e)},(qe||(qe={})).filterSensitiveLog=function(e){return f({},e)},(Ke||(Ke={})).filterSensitiveLog=function(e){return f({},e)},(He||(He={})).filterSensitiveLog=function(e){return f({},e)},(Ve||(Ve={})).filterSensitiveLog=function(e){return f({},e)},(Ge||(Ge={})).filterSensitiveLog=function(e){return f({},e)},(We||(We={})).filterSensitiveLog=function(e){return f({},e)},($e||($e={})).filterSensitiveLog=function(e){return f({},e)},(Ye||(Ye={})).filterSensitiveLog=function(e){return f({},e)},(Je||(Je={})).filterSensitiveLog=function(e){return f({},e)},(Ze||(Ze={})).filterSensitiveLog=function(e){return f({},e)},(Xe||(Xe={})).filterSensitiveLog=function(e){return f({},e)},(Qe||(Qe={})).filterSensitiveLog=function(e){return f({},e)},(et||(et={})).filterSensitiveLog=function(e){return f({},e)},(tt||(tt={})).filterSensitiveLog=function(e){return f({},e)},(nt||(nt={})).filterSensitiveLog=function(e){return f({},e)},(rt||(rt={})).filterSensitiveLog=function(e){return f({},e)},(it||(it={})).filterSensitiveLog=function(e){return f({},e)},(ot||(ot={})).filterSensitiveLog=function(e){return f({},e)},(st||(st={})).filterSensitiveLog=function(e){return f({},e)},(at||(at={})).filterSensitiveLog=function(e){return f({},e)},(ut||(ut={})).filterSensitiveLog=function(e){return f({},e)},(ct||(ct={})).filterSensitiveLog=function(e){return f({},e)},(ft||(ft={})).filterSensitiveLog=function(e){return f({},e)},(lt||(lt={})).filterSensitiveLog=function(e){return f({},e)},(dt||(dt={})).filterSensitiveLog=function(e){return f({},e)},(ht||(ht={})).filterSensitiveLog=function(e){return f({},e)},(pt||(pt={})).filterSensitiveLog=function(e){return f({},e)},(vt||(vt={})).filterSensitiveLog=function(e){return f({},e)},(gt||(gt={})).filterSensitiveLog=function(e){return f({},e)},(mt||(mt={})).filterSensitiveLog=function(e){return f({},e)},(bt||(bt={})).filterSensitiveLog=function(e){return f({},e)},(yt||(yt={})).filterSensitiveLog=function(e){return f({},e)},(wt||(wt={})).filterSensitiveLog=function(e){return f({},e)},(_t||(_t={})).filterSensitiveLog=function(e){return f({},e)},(St||(St={})).filterSensitiveLog=function(e){return f({},e)},(Et||(Et={})).filterSensitiveLog=function(e){return f({},e)},(Mt||(Mt={})).filterSensitiveLog=function(e){return f({},e)},(At||(At={})).filterSensitiveLog=function(e){return f({},e)},(It||(It={})).filterSensitiveLog=function(e){return f({},e)},(kt||(kt={})).filterSensitiveLog=function(e){return f({},e)},(Ot||(Ot={})).filterSensitiveLog=function(e){return f({},e)},(xt||(xt={})).filterSensitiveLog=function(e){return f({},e)},(Ct||(Ct={})).filterSensitiveLog=function(e){return f({},e)},(Tt||(Tt={})).filterSensitiveLog=function(e){return f({},e)},(Pt||(Pt={})).filterSensitiveLog=function(e){return f({},e)},(Nt||(Nt={})).filterSensitiveLog=function(e){return f({},e)},(Rt||(Rt={})).filterSensitiveLog=function(e){return f({},e)},(Lt||(Lt={})).filterSensitiveLog=function(e){return f({},e)},(jt||(jt={})).filterSensitiveLog=function(e){return f({},e)},(Dt||(Dt={})).filterSensitiveLog=function(e){return f({},e)},(Ut||(Ut={})).filterSensitiveLog=function(e){return f({},e)},(Bt||(Bt={})).filterSensitiveLog=function(e){return f({},e)},(Ft||(Ft={})).filterSensitiveLog=function(e){return f({},e)},(zt||(zt={})).filterSensitiveLog=function(e){return f({},e)},(qt||(qt={})).filterSensitiveLog=function(e){return f({},e)},(Kt||(Kt={})).filterSensitiveLog=function(e){return f({},e)},(Ht||(Ht={})).filterSensitiveLog=function(e){return f({},e)},(Vt||(Vt={})).filterSensitiveLog=function(e){return f({},e)},(Gt||(Gt={})).filterSensitiveLog=function(e){return f({},e)},(Wt||(Wt={})).filterSensitiveLog=function(e){return f({},e)},($t||($t={})).filterSensitiveLog=function(e){return f({},e)},(Yt||(Yt={})).filterSensitiveLog=function(e){return f({},e)},(Jt||(Jt={})).filterSensitiveLog=function(e){return f({},e)},(Zt||(Zt={})).filterSensitiveLog=function(e){return f(f({},e),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(Xt||(Xt={})).filterSensitiveLog=function(e){return f(f({},e),e.SSECustomerKey&&{SSECustomerKey:Zr.d})},(Qt||(Qt={})).filterSensitiveLog=function(e){return f({},e)},(en||(en={})).filterSensitiveLog=function(e){return f({},e)},(tn||(tn={})).filterSensitiveLog=function(e){return f({},e)},(nn||(nn={})).filterSensitiveLog=function(e){return f({},e)},(rn||(rn={})).filterSensitiveLog=function(e){return f({},e)},(on||(on={})).filterSensitiveLog=function(e){return f({},e)},(sn||(sn={})).filterSensitiveLog=function(e){return f({},e)},(an||(an={})).filterSensitiveLog=function(e){return f({},e)},(un||(un={})).filterSensitiveLog=function(e){return f({},e)},(cn||(cn={})).filterSensitiveLog=function(e){return f({},e)},(fn||(fn={})).filterSensitiveLog=function(e){return f({},e)},(ln||(ln={})).filterSensitiveLog=function(e){return f({},e)},(dn||(dn={})).filterSensitiveLog=function(e){return f({},e)},(hn||(hn={})).filterSensitiveLog=function(e){return f({},e)},(pn||(pn={})).filterSensitiveLog=function(e){return f({},e)},(vn||(vn={})).filterSensitiveLog=function(e){return f({},e)},(gn||(gn={})).filterSensitiveLog=function(e){return f({},e)},(mn||(mn={})).filterSensitiveLog=function(e){return f({},e)},(bn||(bn={})).filterSensitiveLog=function(e){return f({},e)},(yn||(yn={})).filterSensitiveLog=function(e){return f({},e)},(wn||(wn={})).filterSensitiveLog=function(e){return f({},e)},(_n||(_n={})).filterSensitiveLog=function(e){return f({},e)},(Sn||(Sn={})).filterSensitiveLog=function(e){return f({},e)},(En||(En={})).filterSensitiveLog=function(e){return f(f({},e),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(Mn||(Mn={})).filterSensitiveLog=function(e){return f(f({},e),e.SSECustomerKey&&{SSECustomerKey:Zr.d})},(An||(An={})).filterSensitiveLog=function(e){return f({},e)},(In||(In={})).filterSensitiveLog=function(e){return f({},e)},(kn||(kn={})).filterSensitiveLog=function(e){return f(f({},e),e.InventoryConfigurationList&&{InventoryConfigurationList:e.InventoryConfigurationList.map((function(e){return je.filterSensitiveLog(e)}))})},(On||(On={})).filterSensitiveLog=function(e){return f({},e)},(xn||(xn={})).filterSensitiveLog=function(e){return f({},e)},(Cn||(Cn={})).filterSensitiveLog=function(e){return f({},e)},(Tn||(Tn={})).filterSensitiveLog=function(e){return f({},e)},(Pn||(Pn={})).filterSensitiveLog=function(e){return f({},e)},(Nn||(Nn={})).filterSensitiveLog=function(e){return f({},e)},(Rn||(Rn={})).filterSensitiveLog=function(e){return f({},e)},(Ln||(Ln={})).filterSensitiveLog=function(e){return f({},e)},(jn||(jn={})).filterSensitiveLog=function(e){return f({},e)},(Dn||(Dn={})).filterSensitiveLog=function(e){return f({},e)},(Un||(Un={})).filterSensitiveLog=function(e){return f({},e)},(Bn||(Bn={})).filterSensitiveLog=function(e){return f({},e)},(Fn||(Fn={})).filterSensitiveLog=function(e){return f({},e)},(zn||(zn={})).filterSensitiveLog=function(e){return f({},e)},(qn||(qn={})).filterSensitiveLog=function(e){return f({},e)},(Kn||(Kn={})).filterSensitiveLog=function(e){return f({},e)},(Hn||(Hn={})).filterSensitiveLog=function(e){return f({},e)},(Vn||(Vn={})).filterSensitiveLog=function(e){return f({},e)},(Gn||(Gn={})).filterSensitiveLog=function(e){return f({},e)},(Wn||(Wn={})).filterSensitiveLog=function(e){return f({},e)},($n||($n={})).filterSensitiveLog=function(e){return f({},e)},(Yn||(Yn={})).filterSensitiveLog=function(e){return f({},e)},(Jn||(Jn={})).filterSensitiveLog=function(e){return f({},e)},(Zn||(Zn={})).filterSensitiveLog=function(e){return f({},e)},(Xn||(Xn={})).filterSensitiveLog=function(e){return f({},e)},(Qn||(Qn={})).filterSensitiveLog=function(e){return f({},e)},(er||(er={})).filterSensitiveLog=function(e){return f({},e)},(tr||(tr={})).filterSensitiveLog=function(e){return f(f({},e),e.ServerSideEncryptionConfiguration&&{ServerSideEncryptionConfiguration:Ie.filterSensitiveLog(e.ServerSideEncryptionConfiguration)})},(nr||(nr={})).filterSensitiveLog=function(e){return f(f({},e),e.InventoryConfiguration&&{InventoryConfiguration:je.filterSensitiveLog(e.InventoryConfiguration)})},(rr||(rr={})).filterSensitiveLog=function(e){return f({},e)},(ir||(ir={})).filterSensitiveLog=function(e){return f({},e)},(or||(or={})).filterSensitiveLog=function(e){return f({},e)},(sr||(sr={})).filterSensitiveLog=function(e){return f({},e)},(ar||(ar={})).filterSensitiveLog=function(e){return f({},e)},(ur||(ur={})).filterSensitiveLog=function(e){return f({},e)},(cr||(cr={})).filterSensitiveLog=function(e){return f({},e)},(fr||(fr={})).filterSensitiveLog=function(e){return f({},e)},(lr||(lr={})).filterSensitiveLog=function(e){return f({},e)},(dr||(dr={})).filterSensitiveLog=function(e){return f({},e)},(hr||(hr={})).filterSensitiveLog=function(e){return f({},e)},(pr||(pr={})).filterSensitiveLog=function(e){return f({},e)},(vr||(vr={})).filterSensitiveLog=function(e){return f({},e)},(gr||(gr={})).filterSensitiveLog=function(e){return f({},e)},(mr||(mr={})).filterSensitiveLog=function(e){return f({},e)},(br||(br={})).filterSensitiveLog=function(e){return f({},e)},(yr||(yr={})).filterSensitiveLog=function(e){return f({},e)},(wr||(wr={})).filterSensitiveLog=function(e){return f(f(f({},e),e.SSEKMSEncryptionContext&&{SSEKMSEncryptionContext:Zr.d}),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(_r||(_r={})).filterSensitiveLog=function(e){return f(f(f(f({},e),e.SSECustomerKey&&{SSECustomerKey:Zr.d}),e.SSEKMSEncryptionContext&&{SSEKMSEncryptionContext:Zr.d}),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(Sr||(Sr={})).filterSensitiveLog=function(e){return f({},e)},(Er||(Er={})).filterSensitiveLog=function(e){return f({},e)},(Mr||(Mr={})).filterSensitiveLog=function(e){return f({},e)},(Ar||(Ar={})).filterSensitiveLog=function(e){return f({},e)},(Ir||(Ir={})).filterSensitiveLog=function(e){return f({},e)},(kr||(kr={})).filterSensitiveLog=function(e){return f({},e)},(Or||(Or={})).filterSensitiveLog=function(e){return f({},e)},(xr||(xr={})).filterSensitiveLog=function(e){return f({},e)},(Cr||(Cr={})).filterSensitiveLog=function(e){return f({},e)},(Tr||(Tr={})).filterSensitiveLog=function(e){return f({},e)},(Pr||(Pr={})).filterSensitiveLog=function(e){return f({},e)},(Nr||(Nr={})).filterSensitiveLog=function(e){return f({},e)},(Rr||(Rr={})).filterSensitiveLog=function(e){return f({},e)},(Lr||(Lr={})).filterSensitiveLog=function(e){return f({},e)},(jr||(jr={})).filterSensitiveLog=function(e){return f(f({},e),e.KMSKeyId&&{KMSKeyId:Zr.d})},(Dr||(Dr={})).filterSensitiveLog=function(e){return f({},e)},(Ur||(Ur={})).filterSensitiveLog=function(e){return f(f({},e),e.Encryption&&{Encryption:jr.filterSensitiveLog(e.Encryption)})},(Br||(Br={})).filterSensitiveLog=function(e){return f(f({},e),e.S3&&{S3:Ur.filterSensitiveLog(e.S3)})},function(e){e.IGNORE="IGNORE",e.NONE="NONE",e.USE="USE"}(Fr||(Fr={})),(zr||(zr={})).filterSensitiveLog=function(e){return f({},e)},function(e){e.DOCUMENT="DOCUMENT",e.LINES="LINES"}(qr||(qr={})),(Kr||(Kr={})).filterSensitiveLog=function(e){return f({},e)},(Hr||(Hr={})).filterSensitiveLog=function(e){return f({},e)},(Vr||(Vr={})).filterSensitiveLog=function(e){return f({},e)},function(e){e.ALWAYS="ALWAYS",e.ASNEEDED="ASNEEDED"}(Gr||(Gr={})),(Wr||(Wr={})).filterSensitiveLog=function(e){return f({},e)},($r||($r={})).filterSensitiveLog=function(e){return f({},e)},(Yr||(Yr={})).filterSensitiveLog=function(e){return f({},e)},(Jr||(Jr={})).filterSensitiveLog=function(e){return f({},e)};var Xr=n(2),Qr=n(1);var ei=function(){function e(e,t){void 0===t&&(t=[]),this.name=e,this.children=t,this.attributes={}}return e.prototype.withName=function(e){return this.name=e,this},e.prototype.addAttribute=function(e,t){return this.attributes[e]=t,this},e.prototype.addChildNode=function(e){return this.children.push(e),this},e.prototype.removeAttribute=function(e){return delete this.attributes[e],this},e.prototype.toString=function(){var e,t,n=Boolean(this.children.length),r="<"+this.name,i=this.attributes;try{for(var o=Object(Qr.__values)(Object.keys(i)),s=o.next();!s.done;s=o.next()){var a=s.value,u=i[a];null!=u&&(r+=" "+a+'="'+(""+u).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")+'"')}}catch(t){e={error:t}}finally{try{s&&!s.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}return r+(n?">"+this.children.map((function(e){return e.toString()})).join("")+"</"+this.name+">":"/>")},e}();var ti=function(){function e(e){this.value=e}return e.prototype.toString=function(){return(""+this.value).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")},e}(),ni=n(253),ri=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c;return d(this,(function(l){switch(l.label){case 0:return r=[f({},e)],c={},[4,Ai(e.body,t)];case 1:switch(n=f.apply(void 0,r.concat([(c.body=l.sent(),c)])),o="UnknownError",o=Ii(e,n.body),o){case"NoSuchUpload":case"com.amazonaws.s3#NoSuchUpload":return[3,2]}return[3,4];case 2:return s=[{}],[4,pi(n,t)];case 3:return i=f.apply(void 0,[f.apply(void 0,s.concat([l.sent()])),{name:o,$metadata:Si(e)}]),[3,5];case 4:a=n.body,o=a.code||a.Code||o,i=f(f({},a),{name:""+o,message:a.message||a.Message||o,$fault:"client",$metadata:Si(e)}),l.label=5;case 5:return u=i.message||i.Message||o,i.message=u,delete i.Message,[2,Promise.reject(Object.assign(new Error(u),i))]}}))}))},ii=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u;return d(this,(function(c){switch(c.label){case 0:return r=[f({},e)],u={},[4,Ai(e.body,t)];case 1:return n=f.apply(void 0,r.concat([(u.body=c.sent(),u)])),o="UnknownError",o=Ii(e,n.body),s=n.body,o=s.code||s.Code||o,i=f(f({},s),{name:""+o,message:s.message||s.Message||o,$fault:"client",$metadata:Si(e)}),a=i.message||i.Message||o,i.message=a,delete i.Message,[2,Promise.reject(Object.assign(new Error(a),i))]}}))}))},oi=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u;return d(this,(function(c){switch(c.label){case 0:return r=[f({},e)],u={},[4,Ai(e.body,t)];case 1:return n=f.apply(void 0,r.concat([(u.body=c.sent(),u)])),o="UnknownError",o=Ii(e,n.body),s=n.body,o=s.code||s.Code||o,i=f(f({},s),{name:""+o,message:s.message||s.Message||o,$fault:"client",$metadata:Si(e)}),a=i.message||i.Message||o,i.message=a,delete i.Message,[2,Promise.reject(Object.assign(new Error(a),i))]}}))}))},si=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u;return d(this,(function(c){switch(c.label){case 0:return r=[f({},e)],u={},[4,Ai(e.body,t)];case 1:return n=f.apply(void 0,r.concat([(u.body=c.sent(),u)])),o="UnknownError",o=Ii(e,n.body),s=n.body,o=s.code||s.Code||o,i=f(f({},s),{name:""+o,message:s.message||s.Message||o,$fault:"client",$metadata:Si(e)}),a=i.message||i.Message||o,i.message=a,delete i.Message,[2,Promise.reject(Object.assign(new Error(a),i))]}}))}))},ai=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c;return d(this,(function(l){switch(l.label){case 0:return r=[f({},e)],c={},[4,Ai(e.body,t)];case 1:switch(n=f.apply(void 0,r.concat([(c.body=l.sent(),c)])),o="UnknownError",o=Ii(e,n.body),o){case"NoSuchKey":case"com.amazonaws.s3#NoSuchKey":return[3,2]}return[3,4];case 2:return s=[{}],[4,hi(n,t)];case 3:return i=f.apply(void 0,[f.apply(void 0,s.concat([l.sent()])),{name:o,$metadata:Si(e)}]),[3,5];case 4:a=n.body,o=a.code||a.Code||o,i=f(f({},a),{name:""+o,message:a.message||a.Message||o,$fault:"client",$metadata:Si(e)}),l.label=5;case 5:return u=i.message||i.Message||o,i.message=u,delete i.Message,[2,Promise.reject(Object.assign(new Error(u),i))]}}))}))},ui=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c;return d(this,(function(l){switch(l.label){case 0:return r=[f({},e)],c={},[4,Ai(e.body,t)];case 1:switch(n=f.apply(void 0,r.concat([(c.body=l.sent(),c)])),o="UnknownError",o=Ii(e,n.body),o){case"NoSuchBucket":case"com.amazonaws.s3#NoSuchBucket":return[3,2]}return[3,4];case 2:return s=[{}],[4,di(n,t)];case 3:return i=f.apply(void 0,[f.apply(void 0,s.concat([l.sent()])),{name:o,$metadata:Si(e)}]),[3,5];case 4:a=n.body,o=a.code||a.Code||o,i=f(f({},a),{name:""+o,message:a.message||a.Message||o,$fault:"client",$metadata:Si(e)}),l.label=5;case 5:return u=i.message||i.Message||o,i.message=u,delete i.Message,[2,Promise.reject(Object.assign(new Error(u),i))]}}))}))},ci=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u;return d(this,(function(c){switch(c.label){case 0:return r=[f({},e)],u={},[4,Ai(e.body,t)];case 1:return n=f.apply(void 0,r.concat([(u.body=c.sent(),u)])),o="UnknownError",o=Ii(e,n.body),s=n.body,o=s.code||s.Code||o,i=f(f({},s),{name:""+o,message:s.message||s.Message||o,$fault:"client",$metadata:Si(e)}),a=i.message||i.Message||o,i.message=a,delete i.Message,[2,Promise.reject(Object.assign(new Error(a),i))]}}))}))},fi=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u;return d(this,(function(c){switch(c.label){case 0:return r=[f({},e)],u={},[4,Ai(e.body,t)];case 1:return n=f.apply(void 0,r.concat([(u.body=c.sent(),u)])),o="UnknownError",o=Ii(e,n.body),s=n.body,o=s.code||s.Code||o,i=f(f({},s),{name:""+o,message:s.message||s.Message||o,$fault:"client",$metadata:Si(e)}),a=i.message||i.Message||o,i.message=a,delete i.Message,[2,Promise.reject(Object.assign(new Error(a),i))]}}))}))},li=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u;return d(this,(function(c){switch(c.label){case 0:return r=[f({},e)],u={},[4,Ai(e.body,t)];case 1:return n=f.apply(void 0,r.concat([(u.body=c.sent(),u)])),o="UnknownError",o=Ii(e,n.body),s=n.body,o=s.code||s.Code||o,i=f(f({},s),{name:""+o,message:s.message||s.Message||o,$fault:"client",$metadata:Si(e)}),a=i.message||i.Message||o,i.message=a,delete i.Message,[2,Promise.reject(Object.assign(new Error(a),i))]}}))}))},di=function(e,t){return l(void 0,void 0,void 0,(function(){var t;return d(this,(function(n){return t={name:"NoSuchBucket",$fault:"client",$metadata:Si(e)},e.body,[2,t]}))}))},hi=function(e,t){return l(void 0,void 0,void 0,(function(){var t;return d(this,(function(n){return t={name:"NoSuchKey",$fault:"client",$metadata:Si(e)},e.body,[2,t]}))}))},pi=function(e,t){return l(void 0,void 0,void 0,(function(){var t;return d(this,(function(n){return t={name:"NoSuchUpload",$fault:"client",$metadata:Si(e)},e.body,[2,t]}))}))},vi=function(e,t){var n=new ei("CompletedMultipartUpload");void 0!==e.Parts&&gi(e.Parts,t).map((function(e){e=e.withName("Part"),n.addChildNode(e)}));return n},gi=function(e,t){return e.map((function(e){return function(e,t){var n=new ei("CompletedPart");if(void 0!==e.ETag){var r=new ei("ETag").addChildNode(new ti(e.ETag)).withName("ETag");n.addChildNode(r)}if(void 0!==e.PartNumber){r=new ei("PartNumber").addChildNode(new ti(String(e.PartNumber))).withName("PartNumber");n.addChildNode(r)}return n}(e).withName("member")}))},mi=function(e,t){return(e||[]).map((function(e){return function(e,t){var n={Prefix:void 0};return void 0!==e.Prefix&&(n.Prefix=e.Prefix),n}(e)}))},bi=function(e,t){var n={ID:void 0,DisplayName:void 0};return void 0!==e.ID&&(n.ID=e.ID),void 0!==e.DisplayName&&(n.DisplayName=e.DisplayName),n},yi=function(e,t){return(e||[]).map((function(e){return function(e,t){var n={Size:void 0,ETag:void 0,Owner:void 0,StorageClass:void 0,Key:void 0,LastModified:void 0};return void 0!==e.Size&&(n.Size=parseInt(e.Size)),void 0!==e.ETag&&(n.ETag=e.ETag),void 0!==e.Owner&&(n.Owner=wi(e.Owner,t)),void 0!==e.StorageClass&&(n.StorageClass=e.StorageClass),void 0!==e.Key&&(n.Key=e.Key),void 0!==e.LastModified&&(n.LastModified=new Date(e.LastModified)),n}(e,t)}))},wi=function(e,t){var n={DisplayName:void 0,ID:void 0};return void 0!==e.DisplayName&&(n.DisplayName=e.DisplayName),void 0!==e.ID&&(n.ID=e.ID),n},_i=function(e,t){return(e||[]).map((function(e){return function(e,t){var n={Size:void 0,LastModified:void 0,PartNumber:void 0,ETag:void 0};return void 0!==e.Size&&(n.Size=parseInt(e.Size)),void 0!==e.LastModified&&(n.LastModified=new Date(e.LastModified)),void 0!==e.PartNumber&&(n.PartNumber=parseInt(e.PartNumber)),void 0!==e.ETag&&(n.ETag=e.ETag),n}(e)}))},Si=function(e){return{httpStatusCode:e.statusCode,httpHeaders:e.headers,requestId:e.headers["x-amzn-requestid"]}},Ei=function(e,t){return void 0===e&&(e=new Uint8Array),e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array)},Mi=function(e){return!(void 0===e||""===e||Object.getOwnPropertyNames(e).includes("length")&&0==e.length||Object.getOwnPropertyNames(e).includes("size")&&0==e.size)},Ai=function(e,t){return function(e,t){return Ei(e,t).then((function(e){return t.utf8Encoder(e)}))}(e,t).then((function(e){if(e.length){var t=Object(ni.parse)(e,{attributeNamePrefix:"",ignoreAttributes:!1,parseNodeValue:!1,tagValueProcessor:function(e,t){return e.replace(/&/g,"&").replace(/'/g,"'").replace(/"/g,'"').replace(/>/g,">").replace(/</g,"<")}}),n=Object.keys(t)[0],r=t[n];return r["#text"]&&(r[n]=r["#text"],delete r["#text"]),Object(Zr.h)(r)}return{}}))},Ii=function(e,t){return void 0!==t.Code?t.Code:404==e.statusCode?"NotFound":""},ki=function(e){return"string"==typeof e&&0===e.indexOf("arn:")&&e.split(":").length>=6},Oi=/^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$/,xi=/(\d+\.){3}\d+/,Ci=/\.\./,Ti=/\./,Pi=/^(.+\.)?s3[.-]([a-z0-9-]+)\./,Ni=/^s3(-external-1)?\.amazonaws\.com$/,Ri=function(e){return ji(e)?e.replace(/fips-|-fips/,""):e},Li=function(e){var t=e.match(Pi);return[t[2],e.replace(new RegExp("^"+t[0]),"")]},ji=function(e){return e.startsWith("fips-")||e.endsWith("-fips")},Di=function(e,t){return e===t||Ri(e)===t||e===Ri(t)},Ui=function(e,t){if(void 0===t&&(t={tlsCompatible:!0}),e.length>=64||!/^[a-z0-9][a-z0-9.-]+[a-z0-9]$/.test(e)||/(\d+\.){3}\d+/.test(e)||/[.-]{2}/.test(e)||(null==t?void 0:t.tlsCompatible)&&Ti.test(e))throw new Error("Invalid DNS label "+e)},Bi=function(e){var t=e.baseHostname;return Pi.test(t)?function(e){return"string"==typeof e.bucketName}(e)?zi(e):Fi(e):{bucketEndpoint:!1,hostname:t}},Fi=function(e){var t,n=Object(Qr.__read)((t=e.baseHostname,Ni.test(t)?[t.replace(".amazonaws.com",""),"amazonaws.com"]:Li(t)),2),r=n[0],i=n[1],o=e.pathStyleEndpoint,s=e.dualstackEndpoint,a=void 0!==s&&s,u=e.accelerateEndpoint,c=void 0!==u&&u,f=e.tlsCompatible,l=void 0===f||f,d=e.useArnRegion,h=e.bucketName,p=e.clientPartition,v=void 0===p?"aws":p,g=e.clientSigningRegion,m=void 0===g?r:g;!function(e){if(e.pathStyleEndpoint)throw new Error("Path-style S3 endpoint is not supported when bucket is an ARN");if(e.accelerateEndpoint)throw new Error("Accelerate endpoint is not supported when bucket is an ARN");if(!e.tlsCompatible)throw new Error("HTTPS is required when bucket is an ARN")}({pathStyleEndpoint:o,accelerateEndpoint:c,tlsCompatible:l});var b=h.service,y=h.partition,w=h.accountId,_=h.region,S=h.resource;!function(e){if("s3"!==e&&"s3-outposts"!==e)throw new Error("Expect 's3' or 's3-outposts' in ARN service component")}(b),function(e,t){if(e!==t.clientPartition)throw new Error('Partition in ARN is incompatible, got "'+e+'" but expected "'+t.clientPartition+'"')}(y,{clientPartition:v}),function(e){if(!/[0-9]{12}/.exec(e))throw new Error("Access point ARN accountID does not match regex '[0-9]{12}'")}(w),function(e,t){if(""===e)throw new Error("ARN region is empty");if(!t.useArnRegion&&!Di(e,t.clientRegion)&&!Di(e,t.clientSigningRegion))throw new Error("Region in ARN is incompatible, got "+e+" but expected "+t.clientRegion);if(t.useArnRegion&&ji(e))throw new Error("Endpoint does not support FIPS region")}(_,{useArnRegion:d,clientRegion:r,clientSigningRegion:m});var E=function(e){var t=e.includes(":")?":":"/",n=Object(Qr.__read)(e.split(t)),r=n[0],i=n.slice(1);if("accesspoint"===r){if(1!==i.length||""===i[0])throw new Error("Access Point ARN should have one resource accesspoint"+t+"{accesspointname}");return{accesspointName:i[0]}}if("outpost"===r){if(!i[0]||"accesspoint"!==i[1]||!i[2]||3!==i.length)throw new Error("Outpost ARN should have resource outpost"+t+"{outpostId}"+t+"accesspoint"+t+"{accesspointName}");var o=Object(Qr.__read)(i,3),s=o[0];o[1];return{outpostId:s,accesspointName:o[2]}}throw new Error("ARN resource should begin with 'accesspoint"+t+"' or 'outpost"+t+"'")}(S),M=E.accesspointName,A=E.outpostId;Ui(M+"-"+w,{tlsCompatible:l});var I=d?_:r,k=d?_:m;return A?(function(e){if("s3-outposts"!==e)throw new Error("Expect 's3-posts' in Outpost ARN service component")}(b),Ui(A,{tlsCompatible:l}),function(e){if(e)throw new Error("Dualstack endpoint is not supported with Outpost")}(a),function(e){if(ji(null!=e?e:""))throw new Error("FIPS region is not supported with Outpost, got "+e)}(I),{bucketEndpoint:!0,hostname:M+"-"+w+"."+A+".s3-outposts."+I+"."+i,signingRegion:k,signingService:"s3-outposts"}):(function(e){if("s3"!==e)throw new Error("Expect 's3' in Accesspoint ARN service component")}(b),{bucketEndpoint:!0,hostname:M+"-"+w+".s3-accesspoint"+(a?".dualstack":"")+"."+I+"."+i,signingRegion:k})},zi=function(e){var t,n=e.accelerateEndpoint,r=void 0!==n&&n,i=e.baseHostname,o=e.bucketName,s=e.dualstackEndpoint,a=void 0!==s&&s,u=e.pathStyleEndpoint,c=void 0!==u&&u,f=e.tlsCompatible,l=void 0===f||f,d=Object(Qr.__read)((t=i,Ni.test(t)?["us-east-1","amazonaws.com"]:Li(t)),2),h=d[0],p=d[1];return c||!function(e){return Oi.test(e)&&!xi.test(e)&&!Ci.test(e)}(o)||l&&Ti.test(o)?{bucketEndpoint:!1,hostname:a?"s3.dualstack."+h+"."+p:i}:(r?i="s3-accelerate"+(a?".dualstack":"")+"."+p:a&&(i="s3.dualstack."+h+"."+p),{bucketEndpoint:!0,hostname:o+"."+i})},qi=function(e){return function(t,n){return function(r){return Object(Qr.__awaiter)(void 0,void 0,void 0,(function(){var i,o,s,a,u,c,f,l,d,h,p,v,g,m,b,y,w;return Object(Qr.__generator)(this,(function(_){switch(_.label){case 0:return i=r.input.Bucket,o=e.bucketEndpoint,s=r.request,Xr.a.isInstance(s)?e.bucketEndpoint?(s.hostname=i,[3,6]):[3,1]:[3,7];case 1:return ki(i)?(a=function(e){var t=e.split(":");if(t.length<6||"arn"!==t[0])throw new Error("Malformed ARN");var n=Object(Qr.__read)(t);return{partition:n[1],service:n[2],region:n[3],accountId:n[4],resource:n.slice(5).join(":")}}(i),c=Ri,[4,e.region()]):[3,5];case 2:return u=c.apply(void 0,[_.sent()]),[4,e.regionInfoProvider(u)];case 3:return f=_.sent()||{},l=f.partition,d=f.signingRegion,h=void 0===d?u:d,[4,e.useArnRegion()];case 4:return p=_.sent(),v=Bi({bucketName:a,baseHostname:s.hostname,accelerateEndpoint:e.useAccelerateEndpoint,dualstackEndpoint:e.useDualstackEndpoint,pathStyleEndpoint:e.forcePathStyle,tlsCompatible:"https:"===s.protocol,useArnRegion:p,clientPartition:l,clientSigningRegion:h}),y=v.hostname,w=v.bucketEndpoint,g=v.signingRegion,m=v.signingService,g&&g!==h&&(n.signing_region=g),m&&"s3"!==m&&(n.signing_service=m),s.hostname=y,o=w,[3,6];case 5:b=Bi({bucketName:i,baseHostname:s.hostname,accelerateEndpoint:e.useAccelerateEndpoint,dualstackEndpoint:e.useDualstackEndpoint,pathStyleEndpoint:e.forcePathStyle,tlsCompatible:"https:"===s.protocol}),y=b.hostname,w=b.bucketEndpoint,s.hostname=y,o=w,_.label=6;case 6:o&&(s.path=s.path.replace(/^(\/)?[^\/]+/,""),""===s.path&&(s.path="/")),_.label=7;case 7:return[2,t(Object(Qr.__assign)(Object(Qr.__assign)({},r),{request:s}))]}}))}))}}},Ki={tags:["BUCKET_ENDPOINT"],name:"bucketEndpointMiddleware",relation:"before",toMiddleware:"hostHeaderMiddleware"},Hi=function(e){return{applyToStack:function(t){t.addRelativeTo(qi(e),Ki)}}};var Vi=n(10);var Gi={name:"ssecMiddleware",step:"initialize",tags:["SSE"]},Wi=function(e){return{applyToStack:function(t){t.add(function(e){var t=this;return function(n){return function(r){return Object(Qr.__awaiter)(t,void 0,void 0,(function(){var t,i,o,s,a,u,c,f,l,d,h,p,v,g,m,b,y;return Object(Qr.__generator)(this,(function(w){switch(w.label){case 0:t=Object(Qr.__assign)({},r.input),i=[{target:"SSECustomerKey",hash:"SSECustomerKeyMD5"},{target:"CopySourceSSECustomerKey",hash:"CopySourceSSECustomerKeyMD5"}],w.label=1;case 1:w.trys.push([1,6,7,8]),o=Object(Qr.__values)(i),s=o.next(),w.label=2;case 2:return s.done?[3,5]:(a=s.value,(u=t[a.target])?(c=ArrayBuffer.isView(u)?new Uint8Array(u.buffer,u.byteOffset,u.byteLength):"string"==typeof u?e.utf8Decoder(u):new Uint8Array(u),f=e.base64Encoder(c),(l=new e.md5).update(c),d=[Object(Qr.__assign)({},t)],(y={})[a.target]=f,h=a.hash,v=(p=e).base64Encoder,[4,l.digest()]):[3,4]);case 3:t=Qr.__assign.apply(void 0,d.concat([(y[h]=v.apply(p,[w.sent()]),y)])),w.label=4;case 4:return s=o.next(),[3,2];case 5:return[3,8];case 6:return g=w.sent(),m={error:g},[3,8];case 7:try{s&&!s.done&&(b=o.return)&&b.call(o)}finally{if(m)throw m.error}return[7];case 8:return[2,n(Object(Qr.__assign)(Object(Qr.__assign)({},r),{input:t}))]}}))}))}}}(e),Gi)}}},$i=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Wi(t)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"GetObjectCommand",inputFilterSensitiveLog:Xt.filterSensitiveLog,outputFilterSensitiveLog:Zt.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"GetObjectCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n=f(f(f(f(f(f(f(f(f(f({"Content-Type":""},Mi(e.SSECustomerKey)&&{"x-amz-server-side-encryption-customer-key":e.SSECustomerKey}),Mi(e.SSECustomerAlgorithm)&&{"x-amz-server-side-encryption-customer-algorithm":e.SSECustomerAlgorithm}),Mi(e.SSECustomerKeyMD5)&&{"x-amz-server-side-encryption-customer-key-MD5":e.SSECustomerKeyMD5}),Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),Mi(e.IfUnmodifiedSince)&&{"If-Unmodified-Since":Object(Zr.e)(e.IfUnmodifiedSince).toString()}),Mi(e.IfModifiedSince)&&{"If-Modified-Since":Object(Zr.e)(e.IfModifiedSince).toString()}),Mi(e.IfNoneMatch)&&{"If-None-Match":e.IfNoneMatch}),Mi(e.IfMatch)&&{"If-Match":e.IfMatch}),Mi(e.Range)&&{Range:e.Range}),r="/{Bucket}/{Key+}",void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");if(r=r.replace("{Bucket}",Object(Zr.f)(i)),void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");return r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),o=f(f(f(f(f(f(f(f({"x-id":"GetObject"},void 0!==e.ResponseContentEncoding&&{"response-content-encoding":e.ResponseContentEncoding}),void 0!==e.ResponseCacheControl&&{"response-cache-control":e.ResponseCacheControl}),void 0!==e.ResponseContentLanguage&&{"response-content-language":e.ResponseContentLanguage}),void 0!==e.ResponseContentDisposition&&{"response-content-disposition":e.ResponseContentDisposition}),void 0!==e.PartNumber&&{partNumber:e.PartNumber.toString()}),void 0!==e.VersionId&&{versionId:e.VersionId}),void 0!==e.ResponseExpires&&{"response-expires":(e.ResponseExpires.toISOString().split(".")[0]+"Z").toString()}),void 0!==e.ResponseContentType&&{"response-content-type":e.ResponseContentType}),[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new Xr.a({protocol:c,hostname:a,port:l,method:"GET",headers:n,path:r,query:o,body:void 0})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r;return d(this,(function(i){return 200!==e.statusCode&&e.statusCode>=300?[2,ai(e,t)]:(n={$metadata:Si(e),AcceptRanges:void 0,Body:void 0,CacheControl:void 0,ContentDisposition:void 0,ContentEncoding:void 0,ContentLanguage:void 0,ContentLength:void 0,ContentRange:void 0,ContentType:void 0,DeleteMarker:void 0,ETag:void 0,Expiration:void 0,Expires:void 0,LastModified:void 0,Metadata:void 0,MissingMeta:void 0,ObjectLockLegalHoldStatus:void 0,ObjectLockMode:void 0,ObjectLockRetainUntilDate:void 0,PartsCount:void 0,ReplicationStatus:void 0,RequestCharged:void 0,Restore:void 0,SSECustomerAlgorithm:void 0,SSECustomerKeyMD5:void 0,SSEKMSKeyId:void 0,ServerSideEncryption:void 0,StorageClass:void 0,TagCount:void 0,VersionId:void 0,WebsiteRedirectLocation:void 0},void 0!==e.headers["x-amz-object-lock-mode"]&&(n.ObjectLockMode=e.headers["x-amz-object-lock-mode"]),void 0!==e.headers["content-language"]&&(n.ContentLanguage=e.headers["content-language"]),void 0!==e.headers["content-disposition"]&&(n.ContentDisposition=e.headers["content-disposition"]),void 0!==e.headers["cache-control"]&&(n.CacheControl=e.headers["cache-control"]),void 0!==e.headers["content-type"]&&(n.ContentType=e.headers["content-type"]),void 0!==e.headers["content-range"]&&(n.ContentRange=e.headers["content-range"]),void 0!==e.headers["x-amz-server-side-encryption-aws-kms-key-id"]&&(n.SSEKMSKeyId=e.headers["x-amz-server-side-encryption-aws-kms-key-id"]),void 0!==e.headers["content-length"]&&(n.ContentLength=parseInt(e.headers["content-length"],10)),void 0!==e.headers["x-amz-object-lock-retain-until-date"]&&(n.ObjectLockRetainUntilDate=new Date(e.headers["x-amz-object-lock-retain-until-date"])),void 0!==e.headers["x-amz-object-lock-legal-hold"]&&(n.ObjectLockLegalHoldStatus=e.headers["x-amz-object-lock-legal-hold"]),void 0!==e.headers["x-amz-delete-marker"]&&(n.DeleteMarker="true"===e.headers["x-amz-delete-marker"]),void 0!==e.headers["x-amz-storage-class"]&&(n.StorageClass=e.headers["x-amz-storage-class"]),void 0!==e.headers["content-encoding"]&&(n.ContentEncoding=e.headers["content-encoding"]),void 0!==e.headers["x-amz-restore"]&&(n.Restore=e.headers["x-amz-restore"]),void 0!==e.headers["x-amz-website-redirect-location"]&&(n.WebsiteRedirectLocation=e.headers["x-amz-website-redirect-location"]),void 0!==e.headers["x-amz-server-side-encryption"]&&(n.ServerSideEncryption=e.headers["x-amz-server-side-encryption"]),void 0!==e.headers["x-amz-mp-parts-count"]&&(n.PartsCount=parseInt(e.headers["x-amz-mp-parts-count"],10)),void 0!==e.headers["x-amz-server-side-encryption-customer-algorithm"]&&(n.SSECustomerAlgorithm=e.headers["x-amz-server-side-encryption-customer-algorithm"]),void 0!==e.headers["accept-ranges"]&&(n.AcceptRanges=e.headers["accept-ranges"]),void 0!==e.headers["x-amz-version-id"]&&(n.VersionId=e.headers["x-amz-version-id"]),void 0!==e.headers.expires&&(n.Expires=new Date(e.headers.expires)),void 0!==e.headers["x-amz-expiration"]&&(n.Expiration=e.headers["x-amz-expiration"]),void 0!==e.headers["x-amz-missing-meta"]&&(n.MissingMeta=parseInt(e.headers["x-amz-missing-meta"],10)),void 0!==e.headers["x-amz-replication-status"]&&(n.ReplicationStatus=e.headers["x-amz-replication-status"]),void 0!==e.headers["x-amz-tagging-count"]&&(n.TagCount=parseInt(e.headers["x-amz-tagging-count"],10)),void 0!==e.headers["x-amz-server-side-encryption-customer-key-md5"]&&(n.SSECustomerKeyMD5=e.headers["x-amz-server-side-encryption-customer-key-md5"]),void 0!==e.headers["last-modified"]&&(n.LastModified=new Date(e.headers["last-modified"])),void 0!==e.headers.etag&&(n.ETag=e.headers.etag),void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),Object.keys(e.headers).forEach((function(t){void 0===n.Metadata&&(n.Metadata={}),t.startsWith("x-amz-meta-")&&(n.Metadata[t.substring(11)]=e.headers[t])})),r=e.body,n.Body=r,[2,Promise.resolve(n)])}))}))}(e,t)},t}(Zr.b),Yi=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"DeleteObjectCommand",inputFilterSensitiveLog:Z.filterSensitiveLog,outputFilterSensitiveLog:J.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"DeleteObjectCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n=f(f(f(f({"Content-Type":""},Mi(e.MFA)&&{"x-amz-mfa":e.MFA}),Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),Mi(e.BypassGovernanceRetention)&&{"x-amz-bypass-governance-retention":e.BypassGovernanceRetention.toString()}),Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),r="/{Bucket}/{Key+}",void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");if(r=r.replace("{Bucket}",Object(Zr.f)(i)),void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");return r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),o=f({"x-id":"DeleteObject"},void 0!==e.VersionId&&{versionId:e.VersionId}),[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new Xr.a({protocol:c,hostname:a,port:l,method:"DELETE",headers:n,path:r,query:o,body:void 0})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n;return d(this,(function(r){switch(r.label){case 0:return 204!==e.statusCode&&e.statusCode>=300?[2,si(e,t)]:(n={$metadata:Si(e),DeleteMarker:void 0,RequestCharged:void 0,VersionId:void 0},void 0!==e.headers["x-amz-delete-marker"]&&(n.DeleteMarker="true"===e.headers["x-amz-delete-marker"]),void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),void 0!==e.headers["x-amz-version-id"]&&(n.VersionId=e.headers["x-amz-version-id"]),[4,Ei(e.body,t)]);case 1:return r.sent(),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b),Ji=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"ListObjectsCommand",inputFilterSensitiveLog:Fn.filterSensitiveLog,outputFilterSensitiveLog:Bn.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"ListObjectsCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n=f(f({"Content-Type":""},Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),r="/{Bucket}",void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");return r=r.replace("{Bucket}",Object(Zr.f)(i)),o=f(f(f(f(f({},void 0!==e.MaxKeys&&{"max-keys":e.MaxKeys.toString()}),void 0!==e.Marker&&{marker:e.Marker}),void 0!==e.Prefix&&{prefix:e.Prefix}),void 0!==e.Delimiter&&{delimiter:e.Delimiter}),void 0!==e.EncodingType&&{"encoding-type":e.EncodingType}),[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new Xr.a({protocol:c,hostname:a,port:l,method:"GET",headers:n,path:r,query:o,body:void 0})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r;return d(this,(function(i){switch(i.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,ui(e,t)]:(n={$metadata:Si(e),CommonPrefixes:void 0,Contents:void 0,Delimiter:void 0,EncodingType:void 0,IsTruncated:void 0,Marker:void 0,MaxKeys:void 0,Name:void 0,NextMarker:void 0,Prefix:void 0},[4,Ai(e.body,t)]);case 1:return""===(r=i.sent()).CommonPrefixes&&(n.CommonPrefixes=[]),void 0!==r.CommonPrefixes&&(n.CommonPrefixes=mi(Object(Zr.g)(r.CommonPrefixes),t)),""===r.Contents&&(n.Contents=[]),void 0!==r.Contents&&(n.Contents=yi(Object(Zr.g)(r.Contents),t)),void 0!==r.Delimiter&&(n.Delimiter=r.Delimiter),void 0!==r.EncodingType&&(n.EncodingType=r.EncodingType),void 0!==r.IsTruncated&&(n.IsTruncated="true"==r.IsTruncated),void 0!==r.Marker&&(n.Marker=r.Marker),void 0!==r.MaxKeys&&(n.MaxKeys=parseInt(r.MaxKeys)),void 0!==r.Name&&(n.Name=r.Name),void 0!==r.NextMarker&&(n.NextMarker=r.NextMarker),void 0!==r.Prefix&&(n.Prefix=r.Prefix),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b),Zi=n(153),Xi=n(38),Qi=n(110),eo=n(18);function to(e,t,n){return void 0===n&&(n=1048576),new Promise((function(r,i){var o=new FileReader;o.addEventListener("error",i),o.addEventListener("abort",i);var s=e.size,a=0;function u(){a>=s?r():o.readAsArrayBuffer(e.slice(a,Math.min(s,a+n)))}o.addEventListener("load",(function(e){var n=e.target.result;t(new Uint8Array(n)),a+=n.byteLength,u()})),u()}))}var no=n(24),ro=n(15),io=[1732584193,4023233417,2562383102,271733878],oo=function(){function e(){this.state=Uint32Array.from(io),this.buffer=new DataView(new ArrayBuffer(64)),this.bufferLength=0,this.bytesHashed=0,this.finished=!1}return e.prototype.update=function(e){if(!function(e){if("string"==typeof e)return 0===e.length;return 0===e.byteLength}(e)){if(this.finished)throw new Error("Attempted to update an already finished hash.");var t=function(e){if("string"==typeof e)return Object(ro.a)(e);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT);return new Uint8Array(e)}(e),n=0,r=t.byteLength;for(this.bytesHashed+=r;r>0;)this.buffer.setUint8(this.bufferLength++,t[n++]),r--,64===this.bufferLength&&(this.hashBuffer(),this.bufferLength=0)}},e.prototype.digest=function(){return Object(Qr.__awaiter)(this,void 0,void 0,(function(){var e,t,n,r,i,o,s;return Object(Qr.__generator)(this,(function(a){if(!this.finished){if(t=(e=this).buffer,n=e.bufferLength,r=e.bytesHashed,i=8*r,t.setUint8(this.bufferLength++,128),n%64>=56){for(s=this.bufferLength;s<64;s++)t.setUint8(s,0);this.hashBuffer(),this.bufferLength=0}for(s=this.bufferLength;s<56;s++)t.setUint8(s,0);t.setUint32(56,i>>>0,!0),t.setUint32(60,Math.floor(i/4294967296),!0),this.hashBuffer(),this.finished=!0}for(o=new DataView(new ArrayBuffer(16)),s=0;s<4;s++)o.setUint32(4*s,this.state[s],!0);return[2,new Uint8Array(o.buffer,o.byteOffset,o.byteLength)]}))}))},e.prototype.hashBuffer=function(){var e=this.buffer,t=this.state,n=t[0],r=t[1],i=t[2],o=t[3];n=ao(n,r,i,o,e.getUint32(0,!0),7,3614090360),o=ao(o,n,r,i,e.getUint32(4,!0),12,3905402710),i=ao(i,o,n,r,e.getUint32(8,!0),17,606105819),r=ao(r,i,o,n,e.getUint32(12,!0),22,3250441966),n=ao(n,r,i,o,e.getUint32(16,!0),7,4118548399),o=ao(o,n,r,i,e.getUint32(20,!0),12,1200080426),i=ao(i,o,n,r,e.getUint32(24,!0),17,2821735955),r=ao(r,i,o,n,e.getUint32(28,!0),22,4249261313),n=ao(n,r,i,o,e.getUint32(32,!0),7,1770035416),o=ao(o,n,r,i,e.getUint32(36,!0),12,2336552879),i=ao(i,o,n,r,e.getUint32(40,!0),17,4294925233),r=ao(r,i,o,n,e.getUint32(44,!0),22,2304563134),n=ao(n,r,i,o,e.getUint32(48,!0),7,1804603682),o=ao(o,n,r,i,e.getUint32(52,!0),12,4254626195),i=ao(i,o,n,r,e.getUint32(56,!0),17,2792965006),n=uo(n,r=ao(r,i,o,n,e.getUint32(60,!0),22,1236535329),i,o,e.getUint32(4,!0),5,4129170786),o=uo(o,n,r,i,e.getUint32(24,!0),9,3225465664),i=uo(i,o,n,r,e.getUint32(44,!0),14,643717713),r=uo(r,i,o,n,e.getUint32(0,!0),20,3921069994),n=uo(n,r,i,o,e.getUint32(20,!0),5,3593408605),o=uo(o,n,r,i,e.getUint32(40,!0),9,38016083),i=uo(i,o,n,r,e.getUint32(60,!0),14,3634488961),r=uo(r,i,o,n,e.getUint32(16,!0),20,3889429448),n=uo(n,r,i,o,e.getUint32(36,!0),5,568446438),o=uo(o,n,r,i,e.getUint32(56,!0),9,3275163606),i=uo(i,o,n,r,e.getUint32(12,!0),14,4107603335),r=uo(r,i,o,n,e.getUint32(32,!0),20,1163531501),n=uo(n,r,i,o,e.getUint32(52,!0),5,2850285829),o=uo(o,n,r,i,e.getUint32(8,!0),9,4243563512),i=uo(i,o,n,r,e.getUint32(28,!0),14,1735328473),n=co(n,r=uo(r,i,o,n,e.getUint32(48,!0),20,2368359562),i,o,e.getUint32(20,!0),4,4294588738),o=co(o,n,r,i,e.getUint32(32,!0),11,2272392833),i=co(i,o,n,r,e.getUint32(44,!0),16,1839030562),r=co(r,i,o,n,e.getUint32(56,!0),23,4259657740),n=co(n,r,i,o,e.getUint32(4,!0),4,2763975236),o=co(o,n,r,i,e.getUint32(16,!0),11,1272893353),i=co(i,o,n,r,e.getUint32(28,!0),16,4139469664),r=co(r,i,o,n,e.getUint32(40,!0),23,3200236656),n=co(n,r,i,o,e.getUint32(52,!0),4,681279174),o=co(o,n,r,i,e.getUint32(0,!0),11,3936430074),i=co(i,o,n,r,e.getUint32(12,!0),16,3572445317),r=co(r,i,o,n,e.getUint32(24,!0),23,76029189),n=co(n,r,i,o,e.getUint32(36,!0),4,3654602809),o=co(o,n,r,i,e.getUint32(48,!0),11,3873151461),i=co(i,o,n,r,e.getUint32(60,!0),16,530742520),n=fo(n,r=co(r,i,o,n,e.getUint32(8,!0),23,3299628645),i,o,e.getUint32(0,!0),6,4096336452),o=fo(o,n,r,i,e.getUint32(28,!0),10,1126891415),i=fo(i,o,n,r,e.getUint32(56,!0),15,2878612391),r=fo(r,i,o,n,e.getUint32(20,!0),21,4237533241),n=fo(n,r,i,o,e.getUint32(48,!0),6,1700485571),o=fo(o,n,r,i,e.getUint32(12,!0),10,2399980690),i=fo(i,o,n,r,e.getUint32(40,!0),15,4293915773),r=fo(r,i,o,n,e.getUint32(4,!0),21,2240044497),n=fo(n,r,i,o,e.getUint32(32,!0),6,1873313359),o=fo(o,n,r,i,e.getUint32(60,!0),10,4264355552),i=fo(i,o,n,r,e.getUint32(24,!0),15,2734768916),r=fo(r,i,o,n,e.getUint32(52,!0),21,1309151649),n=fo(n,r,i,o,e.getUint32(16,!0),6,4149444226),o=fo(o,n,r,i,e.getUint32(44,!0),10,3174756917),i=fo(i,o,n,r,e.getUint32(8,!0),15,718787259),r=fo(r,i,o,n,e.getUint32(36,!0),21,3951481745),t[0]=n+t[0]&4294967295,t[1]=r+t[1]&4294967295,t[2]=i+t[2]&4294967295,t[3]=o+t[3]&4294967295},e}();function so(e,t,n,r,i,o){return((t=(t+e&4294967295)+(r+o&4294967295)&4294967295)<<i|t>>>32-i)+n&4294967295}function ao(e,t,n,r,i,o,s){return so(t&n|~t&r,e,t,i,o,s)}function uo(e,t,n,r,i,o,s){return so(t&r|n&~r,e,t,i,o,s)}function co(e,t,n,r,i,o,s){return so(t^n^r,e,t,i,o,s)}function fo(e,t,n,r,i,o,s){return so(n^(t|~r),e,t,i,o,s)}var lo=n(11),ho=n(39),po=n(17),vo=n(40),go=n(41),mo=new Set(["ap-east-1","ap-northeast-1","ap-northeast-2","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-north-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-west-1","us-west-2"]),bo=new Set(["cn-north-1","cn-northwest-1"]),yo=new Set(["us-iso-east-1"]),wo=new Set(["us-isob-east-1"]),_o=new Set(["us-gov-east-1","us-gov-west-1"]),So=f(f({},{apiVersion:"2006-03-01",disableHostPrefix:!1,logger:{},regionInfoProvider:function(e,t){var n=void 0;switch(e){case"ap-east-1":n={hostname:"s3.ap-east-1.amazonaws.com",partition:"aws"};break;case"ap-northeast-1":n={hostname:"s3.ap-northeast-1.amazonaws.com",partition:"aws"};break;case"ap-northeast-2":n={hostname:"s3.ap-northeast-2.amazonaws.com",partition:"aws"};break;case"ap-south-1":n={hostname:"s3.ap-south-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-1":n={hostname:"s3.ap-southeast-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-2":n={hostname:"s3.ap-southeast-2.amazonaws.com",partition:"aws"};break;case"ca-central-1":n={hostname:"s3.ca-central-1.amazonaws.com",partition:"aws"};break;case"cn-north-1":n={hostname:"s3.cn-north-1.amazonaws.com.cn",partition:"aws-cn"};break;case"cn-northwest-1":n={hostname:"s3.cn-northwest-1.amazonaws.com.cn",partition:"aws-cn"};break;case"eu-central-1":n={hostname:"s3.eu-central-1.amazonaws.com",partition:"aws"};break;case"eu-north-1":n={hostname:"s3.eu-north-1.amazonaws.com",partition:"aws"};break;case"eu-west-1":n={hostname:"s3.eu-west-1.amazonaws.com",partition:"aws"};break;case"eu-west-2":n={hostname:"s3.eu-west-2.amazonaws.com",partition:"aws"};break;case"eu-west-3":n={hostname:"s3.eu-west-3.amazonaws.com",partition:"aws"};break;case"fips-us-gov-west-1":n={hostname:"s3-fips-us-gov-west-1.amazonaws.com",partition:"aws-us-gov",signingRegion:"us-gov-west-1"};break;case"me-south-1":n={hostname:"s3.me-south-1.amazonaws.com",partition:"aws"};break;case"s3-external-1":n={hostname:"s3-external-1.amazonaws.com",partition:"aws",signingRegion:"us-east-1"};break;case"sa-east-1":n={hostname:"s3.sa-east-1.amazonaws.com",partition:"aws"};break;case"us-east-1":n={hostname:"s3.amazonaws.com",partition:"aws"};break;case"us-east-2":n={hostname:"s3.us-east-2.amazonaws.com",partition:"aws"};break;case"us-gov-east-1":n={hostname:"s3.us-gov-east-1.amazonaws.com",partition:"aws-us-gov"};break;case"us-gov-west-1":n={hostname:"s3.us-gov-west-1.amazonaws.com",partition:"aws-us-gov"};break;case"us-iso-east-1":n={hostname:"s3.us-iso-east-1.c2s.ic.gov",partition:"aws-iso"};break;case"us-isob-east-1":n={hostname:"s3.us-isob-east-1.sc2s.sgov.gov",partition:"aws-iso-b"};break;case"us-west-1":n={hostname:"s3.us-west-1.amazonaws.com",partition:"aws"};break;case"us-west-2":n={hostname:"s3.us-west-2.amazonaws.com",partition:"aws"};break;default:mo.has(e)&&(n={hostname:"s3.{region}.amazonaws.com".replace("{region}",e),partition:"aws"}),bo.has(e)&&(n={hostname:"s3.{region}.amazonaws.com.cn".replace("{region}",e),partition:"aws-cn"}),yo.has(e)&&(n={hostname:"s3.{region}.c2s.ic.gov".replace("{region}",e),partition:"aws-iso"}),wo.has(e)&&(n={hostname:"s3.{region}.sc2s.sgov.gov".replace("{region}",e),partition:"aws-iso-b"}),_o.has(e)&&(n={hostname:"s3.{region}.amazonaws.com".replace("{region}",e),partition:"aws-us-gov"}),void 0===n&&(n={hostname:"s3.{region}.amazonaws.com".replace("{region}",e),partition:"aws"})}return Promise.resolve(n)},signingEscapePath:!1,signingName:"s3",useArnRegion:!1}),{runtime:"browser",base64Decoder:po.a,base64Encoder:po.b,bodyLengthChecker:vo.a,credentialDefaultProvider:Object(no.a)("Credential is missing"),defaultUserAgent:Object(go.a)(Zi.name,Zi.version),eventStreamSerdeProvider:Qi.a,maxAttempts:lo.a,md5:oo,region:Object(no.a)("Region is missing"),requestHandler:new eo.a,sha256:Xi.Sha256,streamCollector:eo.b,streamHasher:function(e,t){return Object(Qr.__awaiter)(this,void 0,void 0,(function(){var n;return Object(Qr.__generator)(this,(function(r){switch(r.label){case 0:return n=new e,[4,to(t,(function(e){n.update(e)}))];case 1:return r.sent(),[2,n.digest()]}}))}))},urlParser:ho.a,utf8Decoder:ro.a,utf8Encoder:ro.b}),Eo=n(22),Mo=n(112),Ao=n(37);var Io={step:"build",tags:["SET_EXPECT_HEADER","EXPECT_HEADER"],name:"addExpectContinueMiddleware"},ko=function(e){return{applyToStack:function(t){t.add(function(e){var t=this;return function(n){return function(r){return Object(Qr.__awaiter)(t,void 0,void 0,(function(){var t;return Object(Qr.__generator)(this,(function(i){return t=r.request,Xr.a.isInstance(t)&&t.body&&"node"===e.runtime&&(t.headers=Object(Qr.__assign)(Object(Qr.__assign)({},t.headers),{Expect:"100-continue"})),[2,n(Object(Qr.__assign)(Object(Qr.__assign)({},r),{request:t}))]}))}))}}}(e),Io)}}},Oo=n(21),xo=n(43);var Co={step:"initialize",tags:["VALIDATE_BUCKET_NAME"],name:"validateBucketNameMiddleware"},To=function(e){return{applyToStack:function(e){e.add(function(){var e=this;return function(t){return function(n){return Object(Qr.__awaiter)(e,void 0,void 0,(function(){var e,r;return Object(Qr.__generator)(this,(function(i){if("string"==typeof(e=n.input.Bucket)&&!ki(e)&&e.indexOf("/")>=0)throw(r=new Error("Bucket name shouldn't contain '/', received '"+e+"'")).name="InvalidBucketName",r;return[2,t(Object(Qr.__assign)({},n))]}))}))}}}(),Co)}}},Po={step:"build",tags:["USE_REGIONAL_ENDPOINT","S3"],name:"useRegionalEndpointMiddleware"},No=function(e){return{applyToStack:function(t){t.add(function(e){return function(t){return function(n){return Object(Qr.__awaiter)(void 0,void 0,void 0,(function(){var r,i;return Object(Qr.__generator)(this,(function(o){switch(o.label){case 0:return r=n.request,!Xr.a.isInstance(r)||e.isCustomEndpoint?[2,t(Object(Qr.__assign)({},n))]:"s3.amazonaws.com"!==r.hostname?[3,1]:(r.hostname="s3.us-east-1.amazonaws.com",[3,3]);case 1:return i="aws-global",[4,e.region()];case 2:i===o.sent()&&(r.hostname="s3.amazonaws.com"),o.label=3;case 3:return[2,t(Object(Qr.__assign)({},n))]}}))}))}}}(e),Po)}}},Ro=n(25),Lo=n(23),jo=function(e){function t(t){var n,r,i,o,s,a,u,c,l,d,h,p=this,v=f(f({},So),t),g=Object(Eo.b)(v),m=Object(Eo.a)(g),b=Object(Ro.b)(m),y=Object(lo.c)(b),w=Object(Lo.b)(y),_=(r=(n=w).bucketEndpoint,i=void 0!==r&&r,o=n.forcePathStyle,s=void 0!==o&&o,a=n.useAccelerateEndpoint,u=void 0!==a&&a,c=n.useDualstackEndpoint,l=void 0!==c&&c,d=n.useArnRegion,h=void 0!==d&&d,Object(Qr.__assign)(Object(Qr.__assign)({},n),{bucketEndpoint:i,forcePathStyle:s,useAccelerateEndpoint:u,useDualstackEndpoint:l,useArnRegion:"function"==typeof h?h:function(){return Promise.resolve(h)}})),S=Object(Oo.b)(_),E=Object(Mo.a)(S);return(p=e.call(this,E)||this).config=E,p.middlewareStack.use(Object(Ro.a)(p.config)),p.middlewareStack.use(Object(lo.b)(p.config)),p.middlewareStack.use(Object(Lo.a)(p.config)),p.middlewareStack.use(Object(Ao.a)(p.config)),p.middlewareStack.use(To(p.config)),p.middlewareStack.use(No(p.config)),p.middlewareStack.use(ko(p.config)),p.middlewareStack.use(Object(Oo.a)(p.config)),p.middlewareStack.use(Object(xo.a)(p.config)),p}return c(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this)},t}(Zr.a),Do=n(74);function Uo(e){var t=e.port,n=e.query,r=e.protocol,i=e.path,o=e.hostname;r&&":"!==r.substr(-1)&&(r+=":"),t&&(o+=":"+t),i&&"/"!==i.charAt(0)&&(i="/"+i);var s=n?Object(Do.a)(n):"";return s&&"?"!==s[0]&&(s="?"+s),r+"//"+o+i+s}function Bo(e,t){return Object(Qr.__awaiter)(this,void 0,void 0,(function(){var n,r,i=this;return Object(Qr.__generator)(this,(function(o){switch(o.label){case 0:return n=function(e){return function(e){return Object(Qr.__awaiter)(i,void 0,void 0,(function(){return Object(Qr.__generator)(this,(function(t){return[2,{output:{request:e.request},response:void 0}]}))}))}},(r=e.middlewareStack.clone()).add(n,{step:"build",priority:"low"}),[4,t.resolveMiddleware(r,e.config,void 0)(t).then((function(e){return e.output.request}))];case 1:return[2,o.sent()]}}))}))}var Fo=n(111),zo=function(){function e(e){var t=Object(Qr.__assign)({service:e.signingName||e.service||"s3",uriEscapePath:e.uriEscapePath||!1},e);this.signer=new Fo.a(t)}return e.prototype.presign=function(e,t){void 0===t&&(t={});var n=t.unsignableHeaders,r=void 0===n?new Set:n,i=Object(Qr.__rest)(t,["unsignableHeaders"]);return Object(Qr.__awaiter)(this,void 0,void 0,(function(){return Object(Qr.__generator)(this,(function(t){return r.add("content-type"),e.headers["X-Amz-Content-Sha256"]="UNSIGNED-PAYLOAD",[2,this.signer.presign(e,Object(Qr.__assign)({expiresIn:900,unsignableHeaders:r},i))]}))}))},e}(),qo=n(64),Ko=n.n(qo),Ho=new r.a("axios-http-handler"),Vo=function(){function e(e,t){void 0===e&&(e={}),this.httpOptions=e,this.emitter=t}return e.prototype.destroy=function(){},e.prototype.handle=function(e,t){var n=this.httpOptions.requestTimeout,r=this.emitter,i=e.path;if(e.query){var o=Object(Do.a)(e.query);o&&(i+="?"+o)}var s=e.port,a=e.protocol+"//"+e.hostname+(s?":"+s:"")+i,u={};u.url=a,u.method=e.method,u.headers=e.headers,delete u.headers.host,e.body?u.data=e.body:u.headers["Content-Type"]&&(u.data=null),r&&(u.onUploadProgress=function(e){r.emit("sendProgress",e),Ho.debug(e)}),u.responseType="blob";var c=[Ko.a.request(u).then((function(e){return{response:new Xr.b({headers:e.headers,statusCode:e.status,body:e.data})}})).catch((function(e){throw Ho.error(e),e})),Go(n)];return Promise.race(c)},e}();function Go(e){return void 0===e&&(e=0),new Promise((function(t,n){e&&setTimeout((function(){var t=new Error("Request did not complete within "+e+" ms");t.name="TimeoutError",n(t)}),e)}))}var Wo,$o,Yo,Jo,Zo,Xo,Qo,es,ts,ns,rs,is,os,ss,as,us,cs,fs,ls,ds,hs=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Wi(t)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"PutObjectCommand",inputFilterSensitiveLog:_r.filterSensitiveLog,outputFilterSensitiveLog:wr.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"PutObjectCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l,h,p;return d(this,(function(d){switch(d.label){case 0:if(n=f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f({"Content-Type":"application/octet-stream"},Mi(e.GrantFullControl)&&{"x-amz-grant-full-control":e.GrantFullControl}),Mi(e.ContentEncoding)&&{"Content-Encoding":e.ContentEncoding}),Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),Mi(e.GrantReadACP)&&{"x-amz-grant-read-acp":e.GrantReadACP}),Mi(e.SSECustomerKeyMD5)&&{"x-amz-server-side-encryption-customer-key-MD5":e.SSECustomerKeyMD5}),Mi(e.CacheControl)&&{"Cache-Control":e.CacheControl}),Mi(e.WebsiteRedirectLocation)&&{"x-amz-website-redirect-location":e.WebsiteRedirectLocation}),Mi(e.ObjectLockLegalHoldStatus)&&{"x-amz-object-lock-legal-hold":e.ObjectLockLegalHoldStatus}),Mi(e.GrantWriteACP)&&{"x-amz-grant-write-acp":e.GrantWriteACP}),Mi(e.ContentLength)&&{"Content-Length":e.ContentLength.toString()}),Mi(e.ObjectLockRetainUntilDate)&&{"x-amz-object-lock-retain-until-date":(e.ObjectLockRetainUntilDate.toISOString().split(".")[0]+"Z").toString()}),Mi(e.SSECustomerAlgorithm)&&{"x-amz-server-side-encryption-customer-algorithm":e.SSECustomerAlgorithm}),Mi(e.ContentDisposition)&&{"Content-Disposition":e.ContentDisposition}),Mi(e.SSECustomerKey)&&{"x-amz-server-side-encryption-customer-key":e.SSECustomerKey}),Mi(e.SSEKMSEncryptionContext)&&{"x-amz-server-side-encryption-context":e.SSEKMSEncryptionContext}),Mi(e.Tagging)&&{"x-amz-tagging":e.Tagging}),Mi(e.Expires)&&{Expires:Object(Zr.e)(e.Expires).toString()}),Mi(e.StorageClass)&&{"x-amz-storage-class":e.StorageClass}),Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),Mi(e.ContentMD5)&&{"Content-MD5":e.ContentMD5}),Mi(e.ServerSideEncryption)&&{"x-amz-server-side-encryption":e.ServerSideEncryption}),Mi(e.ObjectLockMode)&&{"x-amz-object-lock-mode":e.ObjectLockMode}),Mi(e.SSEKMSKeyId)&&{"x-amz-server-side-encryption-aws-kms-key-id":e.SSEKMSKeyId}),Mi(e.ContentLanguage)&&{"Content-Language":e.ContentLanguage}),Mi(e.GrantRead)&&{"x-amz-grant-read":e.GrantRead}),Mi(e.ACL)&&{"x-amz-acl":e.ACL}),Mi(e.ContentType)&&{"Content-Type":e.ContentType}),void 0!==e.Metadata&&Object.keys(e.Metadata).reduce((function(t,n){return t["x-amz-meta-"+n]=e.Metadata[n],t}),{})),r="/{Bucket}/{Key+}",void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");if(r=r.replace("{Bucket}",Object(Zr.f)(i)),void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");return r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),o={"x-id":"PutObject"},void 0!==e.Body&&(a=e.Body,s=a),[4,t.endpoint()];case 1:return u=d.sent(),c=u.hostname,l=u.protocol,h=void 0===l?"https":l,p=u.port,[2,new Xr.a({protocol:h,hostname:c,port:p,method:"PUT",headers:n,path:r,query:o,body:s})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n;return d(this,(function(r){switch(r.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,fi(e,t)]:(n={$metadata:Si(e),ETag:void 0,Expiration:void 0,RequestCharged:void 0,SSECustomerAlgorithm:void 0,SSECustomerKeyMD5:void 0,SSEKMSEncryptionContext:void 0,SSEKMSKeyId:void 0,ServerSideEncryption:void 0,VersionId:void 0},void 0!==e.headers["x-amz-server-side-encryption-context"]&&(n.SSEKMSEncryptionContext=e.headers["x-amz-server-side-encryption-context"]),void 0!==e.headers["x-amz-expiration"]&&(n.Expiration=e.headers["x-amz-expiration"]),void 0!==e.headers["x-amz-server-side-encryption-customer-key-md5"]&&(n.SSECustomerKeyMD5=e.headers["x-amz-server-side-encryption-customer-key-md5"]),void 0!==e.headers.etag&&(n.ETag=e.headers.etag),void 0!==e.headers["x-amz-server-side-encryption-customer-algorithm"]&&(n.SSECustomerAlgorithm=e.headers["x-amz-server-side-encryption-customer-algorithm"]),void 0!==e.headers["x-amz-version-id"]&&(n.VersionId=e.headers["x-amz-version-id"]),void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),void 0!==e.headers["x-amz-server-side-encryption-aws-kms-key-id"]&&(n.SSEKMSKeyId=e.headers["x-amz-server-side-encryption-aws-kms-key-id"]),void 0!==e.headers["x-amz-server-side-encryption"]&&(n.ServerSideEncryption=e.headers["x-amz-server-side-encryption"]),[4,Ei(e.body,t)]);case 1:return r.sent(),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b),ps=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Wi(t)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"CreateMultipartUploadCommand",inputFilterSensitiveLog:D.filterSensitiveLog,outputFilterSensitiveLog:j.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"CreateMultipartUploadCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n=f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f({"Content-Type":""},Mi(e.GrantFullControl)&&{"x-amz-grant-full-control":e.GrantFullControl}),Mi(e.SSECustomerKeyMD5)&&{"x-amz-server-side-encryption-customer-key-MD5":e.SSECustomerKeyMD5}),Mi(e.SSECustomerAlgorithm)&&{"x-amz-server-side-encryption-customer-algorithm":e.SSECustomerAlgorithm}),Mi(e.SSEKMSKeyId)&&{"x-amz-server-side-encryption-aws-kms-key-id":e.SSEKMSKeyId}),Mi(e.ObjectLockLegalHoldStatus)&&{"x-amz-object-lock-legal-hold":e.ObjectLockLegalHoldStatus}),Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),Mi(e.GrantRead)&&{"x-amz-grant-read":e.GrantRead}),Mi(e.GrantWriteACP)&&{"x-amz-grant-write-acp":e.GrantWriteACP}),Mi(e.WebsiteRedirectLocation)&&{"x-amz-website-redirect-location":e.WebsiteRedirectLocation}),Mi(e.ContentType)&&{"Content-Type":e.ContentType}),Mi(e.ContentLanguage)&&{"Content-Language":e.ContentLanguage}),Mi(e.CacheControl)&&{"Cache-Control":e.CacheControl}),Mi(e.GrantReadACP)&&{"x-amz-grant-read-acp":e.GrantReadACP}),Mi(e.Tagging)&&{"x-amz-tagging":e.Tagging}),Mi(e.SSEKMSEncryptionContext)&&{"x-amz-server-side-encryption-context":e.SSEKMSEncryptionContext}),Mi(e.ACL)&&{"x-amz-acl":e.ACL}),Mi(e.SSECustomerKey)&&{"x-amz-server-side-encryption-customer-key":e.SSECustomerKey}),Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),Mi(e.Expires)&&{Expires:Object(Zr.e)(e.Expires).toString()}),Mi(e.ObjectLockRetainUntilDate)&&{"x-amz-object-lock-retain-until-date":(e.ObjectLockRetainUntilDate.toISOString().split(".")[0]+"Z").toString()}),Mi(e.ServerSideEncryption)&&{"x-amz-server-side-encryption":e.ServerSideEncryption}),Mi(e.ContentDisposition)&&{"Content-Disposition":e.ContentDisposition}),Mi(e.ObjectLockMode)&&{"x-amz-object-lock-mode":e.ObjectLockMode}),Mi(e.StorageClass)&&{"x-amz-storage-class":e.StorageClass}),Mi(e.ContentEncoding)&&{"Content-Encoding":e.ContentEncoding}),void 0!==e.Metadata&&Object.keys(e.Metadata).reduce((function(t,n){return t["x-amz-meta-"+n]=e.Metadata[n],t}),{})),r="/{Bucket}/{Key+}",void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");if(r=r.replace("{Bucket}",Object(Zr.f)(i)),void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");return r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),o={uploads:""},[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new Xr.a({protocol:c,hostname:a,port:l,method:"POST",headers:n,path:r,query:o,body:void 0})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r;return d(this,(function(i){switch(i.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,oi(e,t)]:(n={$metadata:Si(e),AbortDate:void 0,AbortRuleId:void 0,Bucket:void 0,Key:void 0,RequestCharged:void 0,SSECustomerAlgorithm:void 0,SSECustomerKeyMD5:void 0,SSEKMSEncryptionContext:void 0,SSEKMSKeyId:void 0,ServerSideEncryption:void 0,UploadId:void 0},void 0!==e.headers["x-amz-server-side-encryption-context"]&&(n.SSEKMSEncryptionContext=e.headers["x-amz-server-side-encryption-context"]),void 0!==e.headers["x-amz-server-side-encryption"]&&(n.ServerSideEncryption=e.headers["x-amz-server-side-encryption"]),void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),void 0!==e.headers["x-amz-abort-date"]&&(n.AbortDate=new Date(e.headers["x-amz-abort-date"])),void 0!==e.headers["x-amz-server-side-encryption-customer-algorithm"]&&(n.SSECustomerAlgorithm=e.headers["x-amz-server-side-encryption-customer-algorithm"]),void 0!==e.headers["x-amz-server-side-encryption-aws-kms-key-id"]&&(n.SSEKMSKeyId=e.headers["x-amz-server-side-encryption-aws-kms-key-id"]),void 0!==e.headers["x-amz-abort-rule-id"]&&(n.AbortRuleId=e.headers["x-amz-abort-rule-id"]),void 0!==e.headers["x-amz-server-side-encryption-customer-key-md5"]&&(n.SSECustomerKeyMD5=e.headers["x-amz-server-side-encryption-customer-key-md5"]),[4,Ai(e.body,t)]);case 1:return void 0!==(r=i.sent()).Bucket&&(n.Bucket=r.Bucket),void 0!==r.Key&&(n.Key=r.Key),void 0!==r.UploadId&&(n.UploadId=r.UploadId),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b);!function(e){e.SELECT="SELECT"}(Wo||(Wo={})),($o||($o={})).filterSensitiveLog=function(e){return f(f({},e),e.OutputLocation&&{OutputLocation:Br.filterSensitiveLog(e.OutputLocation)})},(Yo||(Yo={})).filterSensitiveLog=function(e){return f(f({},e),e.RestoreRequest&&{RestoreRequest:$o.filterSensitiveLog(e.RestoreRequest)})},(Jo||(Jo={})).filterSensitiveLog=function(e){return f({},e)},(Zo||(Zo={})).filterSensitiveLog=function(e){return f({},e)},(Xo||(Xo={})).filterSensitiveLog=function(e){return f({},e)},(Qo||(Qo={})).filterSensitiveLog=function(e){return f({},e)},(es||(es={})).filterSensitiveLog=function(e){return f({},e)},(ts||(ts={})).filterSensitiveLog=function(e){return f({},e)},(ns||(ns={})).filterSensitiveLog=function(e){return f({},e)},function(e){e.visit=function(e,t){return void 0!==e.Cont?t.Cont(e.Cont):void 0!==e.Progress?t.Progress(e.Progress):void 0!==e.Stats?t.Stats(e.Stats):void 0!==e.End?t.End(e.End):void 0!==e.Records?t.Records(e.Records):t._(e.$unknown[0],e.$unknown[1])},e.filterSensitiveLog=function(e){var t;return void 0!==e.Cont?{Cont:Jo.filterSensitiveLog(e.Cont)}:void 0!==e.Progress?{Progress:Qo.filterSensitiveLog(e.Progress)}:void 0!==e.Stats?{Stats:ns.filterSensitiveLog(e.Stats)}:void 0!==e.End?{End:Zo.filterSensitiveLog(e.End)}:void 0!==e.Records?{Records:es.filterSensitiveLog(e.Records)}:void 0!==e.$unknown?((t={})[e.$unknown[0]]="UNKNOWN",t):void 0}}(rs||(rs={})),(is||(is={})).filterSensitiveLog=function(e){return f(f({},e),e.Payload&&{Payload:"STREAMING_CONTENT"})},(os||(os={})).filterSensitiveLog=function(e){return f({},e)},(ss||(ss={})).filterSensitiveLog=function(e){return f({},e)},(as||(as={})).filterSensitiveLog=function(e){return f(f({},e),e.SSECustomerKey&&{SSECustomerKey:Zr.d})},(us||(us={})).filterSensitiveLog=function(e){return f(f({},e),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(cs||(cs={})).filterSensitiveLog=function(e){return f(f({},e),e.SSECustomerKey&&{SSECustomerKey:Zr.d})},(fs||(fs={})).filterSensitiveLog=function(e){return f({},e)},(ls||(ls={})).filterSensitiveLog=function(e){return f(f({},e),e.SSEKMSKeyId&&{SSEKMSKeyId:Zr.d})},(ds||(ds={})).filterSensitiveLog=function(e){return f(f(f({},e),e.SSECustomerKey&&{SSECustomerKey:Zr.d}),e.CopySourceSSECustomerKey&&{CopySourceSSECustomerKey:Zr.d})};var vs=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Wi(t)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"UploadPartCommand",inputFilterSensitiveLog:cs.filterSensitiveLog,outputFilterSensitiveLog:us.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"UploadPartCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l,h,p;return d(this,(function(d){switch(d.label){case 0:if(n=f(f(f(f(f(f(f({"Content-Type":"application/octet-stream"},Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),Mi(e.ContentLength)&&{"Content-Length":e.ContentLength.toString()}),Mi(e.SSECustomerKey)&&{"x-amz-server-side-encryption-customer-key":e.SSECustomerKey}),Mi(e.SSECustomerAlgorithm)&&{"x-amz-server-side-encryption-customer-algorithm":e.SSECustomerAlgorithm}),Mi(e.SSECustomerKeyMD5)&&{"x-amz-server-side-encryption-customer-key-MD5":e.SSECustomerKeyMD5}),Mi(e.ContentMD5)&&{"Content-MD5":e.ContentMD5}),r="/{Bucket}/{Key+}",void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");if(r=r.replace("{Bucket}",Object(Zr.f)(i)),void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");return r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),o=f(f({"x-id":"UploadPart"},void 0!==e.PartNumber&&{partNumber:e.PartNumber.toString()}),void 0!==e.UploadId&&{uploadId:e.UploadId}),void 0!==e.Body&&(a=e.Body,s=a),[4,t.endpoint()];case 1:return u=d.sent(),c=u.hostname,l=u.protocol,h=void 0===l?"https":l,p=u.port,[2,new Xr.a({protocol:h,hostname:c,port:p,method:"PUT",headers:n,path:r,query:o,body:s})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n;return d(this,(function(r){switch(r.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,li(e,t)]:(n={$metadata:Si(e),ETag:void 0,RequestCharged:void 0,SSECustomerAlgorithm:void 0,SSECustomerKeyMD5:void 0,SSEKMSKeyId:void 0,ServerSideEncryption:void 0},void 0!==e.headers["x-amz-server-side-encryption-customer-key-md5"]&&(n.SSECustomerKeyMD5=e.headers["x-amz-server-side-encryption-customer-key-md5"]),void 0!==e.headers["x-amz-server-side-encryption"]&&(n.ServerSideEncryption=e.headers["x-amz-server-side-encryption"]),void 0!==e.headers["x-amz-server-side-encryption-aws-kms-key-id"]&&(n.SSEKMSKeyId=e.headers["x-amz-server-side-encryption-aws-kms-key-id"]),void 0!==e.headers["x-amz-server-side-encryption-customer-algorithm"]&&(n.SSECustomerAlgorithm=e.headers["x-amz-server-side-encryption-customer-algorithm"]),void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),void 0!==e.headers.etag&&(n.ETag=e.headers.etag),[4,Ei(e.body,t)]);case 1:return r.sent(),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b),gs=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"CompleteMultipartUploadCommand",inputFilterSensitiveLog:I.filterSensitiveLog,outputFilterSensitiveLog:E.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"CompleteMultipartUploadCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l,h,p;return d(this,(function(d){switch(d.label){case 0:if(n=f(f({"Content-Type":"application/xml"},Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),r="/{Bucket}/{Key+}",void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");if(r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");return r=r.replace("{Bucket}",Object(Zr.f)(i)),o=f({},void 0!==e.UploadId&&{uploadId:e.UploadId}),void 0!==e.MultipartUpload&&(a=vi(e.MultipartUpload,t),s='<?xml version="1.0" encoding="UTF-8"?>',a.addAttribute("xmlns","http://s3.amazonaws.com/doc/2006-03-01/"),s+=a.toString()),[4,t.endpoint()];case 1:return u=d.sent(),c=u.hostname,l=u.protocol,h=void 0===l?"https":l,p=u.port,[2,new Xr.a({protocol:h,hostname:c,port:p,method:"POST",headers:n,path:r,query:o,body:s})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r;return d(this,(function(i){switch(i.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,ii(e,t)]:(n={$metadata:Si(e),Bucket:void 0,ETag:void 0,Expiration:void 0,Key:void 0,Location:void 0,RequestCharged:void 0,SSEKMSKeyId:void 0,ServerSideEncryption:void 0,VersionId:void 0},void 0!==e.headers["x-amz-expiration"]&&(n.Expiration=e.headers["x-amz-expiration"]),void 0!==e.headers["x-amz-server-side-encryption"]&&(n.ServerSideEncryption=e.headers["x-amz-server-side-encryption"]),void 0!==e.headers["x-amz-server-side-encryption-aws-kms-key-id"]&&(n.SSEKMSKeyId=e.headers["x-amz-server-side-encryption-aws-kms-key-id"]),void 0!==e.headers["x-amz-version-id"]&&(n.VersionId=e.headers["x-amz-version-id"]),void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),[4,Ai(e.body,t)]);case 1:return void 0!==(r=i.sent()).Bucket&&(n.Bucket=r.Bucket),void 0!==r.ETag&&(n.ETag=r.ETag),void 0!==r.Key&&(n.Key=r.Key),void 0!==r.Location&&(n.Location=r.Location),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b),ms=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"AbortMultipartUploadCommand",inputFilterSensitiveLog:v.filterSensitiveLog,outputFilterSensitiveLog:p.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"AbortMultipartUploadCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n=f(f({"Content-Type":""},Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),r="/{Bucket}/{Key+}",void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");if(r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");return r=r.replace("{Bucket}",Object(Zr.f)(i)),o=f({"x-id":"AbortMultipartUpload"},void 0!==e.UploadId&&{uploadId:e.UploadId}),[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new Xr.a({protocol:c,hostname:a,port:l,method:"DELETE",headers:n,path:r,query:o,body:void 0})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n;return d(this,(function(r){switch(r.label){case 0:return 204!==e.statusCode&&e.statusCode>=300?[2,ri(e,t)]:(n={$metadata:Si(e),RequestCharged:void 0},void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),[4,Ei(e.body,t)]);case 1:return r.sent(),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b),bs=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Vi.a)(t,this.serialize,this.deserialize)),this.middlewareStack.use(Hi(t));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"S3Client",commandName:"ListPartsCommand",inputFilterSensitiveLog:Yn.filterSensitiveLog,outputFilterSensitiveLog:$n.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"S3Client",commandName:"ListPartsCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n=f(f({"Content-Type":""},Mi(e.RequestPayer)&&{"x-amz-request-payer":e.RequestPayer}),Mi(e.ExpectedBucketOwner)&&{"x-amz-expected-bucket-owner":e.ExpectedBucketOwner}),r="/{Bucket}/{Key+}",void 0===e.Bucket)throw new Error("No value provided for input HTTP label: Bucket.");if((i=e.Bucket).length<=0)throw new Error("Empty value provided for input HTTP label: Bucket.");if(r=r.replace("{Bucket}",Object(Zr.f)(i)),void 0===e.Key)throw new Error("No value provided for input HTTP label: Key.");if((i=e.Key).length<=0)throw new Error("Empty value provided for input HTTP label: Key.");return r=r.replace("{Key+}",i.split("/").map((function(e){return Object(Zr.f)(e)})).join("/")),o=f(f(f({"x-id":"ListParts"},void 0!==e.UploadId&&{uploadId:e.UploadId}),void 0!==e.MaxParts&&{"max-parts":e.MaxParts.toString()}),void 0!==e.PartNumberMarker&&{"part-number-marker":e.PartNumberMarker.toString()}),[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new Xr.a({protocol:c,hostname:a,port:l,method:"GET",headers:n,path:r,query:o,body:void 0})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r;return d(this,(function(i){switch(i.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,ci(e,t)]:(n={$metadata:Si(e),AbortDate:void 0,AbortRuleId:void 0,Bucket:void 0,Initiator:void 0,IsTruncated:void 0,Key:void 0,MaxParts:void 0,NextPartNumberMarker:void 0,Owner:void 0,PartNumberMarker:void 0,Parts:void 0,RequestCharged:void 0,StorageClass:void 0,UploadId:void 0},void 0!==e.headers["x-amz-abort-rule-id"]&&(n.AbortRuleId=e.headers["x-amz-abort-rule-id"]),void 0!==e.headers["x-amz-request-charged"]&&(n.RequestCharged=e.headers["x-amz-request-charged"]),void 0!==e.headers["x-amz-abort-date"]&&(n.AbortDate=new Date(e.headers["x-amz-abort-date"])),[4,Ai(e.body,t)]);case 1:return void 0!==(r=i.sent()).Bucket&&(n.Bucket=r.Bucket),void 0!==r.Initiator&&(n.Initiator=bi(r.Initiator,t)),void 0!==r.IsTruncated&&(n.IsTruncated="true"==r.IsTruncated),void 0!==r.Key&&(n.Key=r.Key),void 0!==r.MaxParts&&(n.MaxParts=parseInt(r.MaxParts)),void 0!==r.NextPartNumberMarker&&(n.NextPartNumberMarker=parseInt(r.NextPartNumberMarker)),void 0!==r.Owner&&(n.Owner=wi(r.Owner,t)),void 0!==r.PartNumberMarker&&(n.PartNumberMarker=parseInt(r.PartNumberMarker)),""===r.Part&&(n.Parts=[]),void 0!==r.Part&&(n.Parts=_i(Object(Zr.g)(r.Part),t)),void 0!==r.StorageClass&&(n.StorageClass=r.StorageClass),void 0!==r.UploadId&&(n.UploadId=r.UploadId),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Zr.b),ys=n(49),ws=n(106),_s=n(16),Ss=function(e){var t,n=Object(_s.parse)(e),r=n.hostname,i=void 0===r?"localhost":r,o=n.pathname,s=void 0===o?"/":o,a=n.port,u=n.protocol,c=void 0===u?"https:":u,f=n.search;return f&&(t=Object(ws.a)(f)),{hostname:i,port:a?parseInt(a):void 0,protocol:c,path:s,query:t}};function Es(e){return(Es="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)}var Ms=function(){return(Ms=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},As=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},Is=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},ks=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Os=new r.a("AWSS3ProviderManagedUpload"),xs=function(){function e(e,t,n){this.minPartSize=5242880,this.queueSize=4,this.body=null,this.params=null,this.opts=null,this.multiPartMap=[],this.cancel=!1,this.bytesUploaded=0,this.totalBytesToUpload=0,this.emitter=null,this.params=e,this.opts=t,this.emitter=n}return e.prototype.upload=function(){return As(this,void 0,void 0,(function(){var e,t,n,r,i,o;return Is(this,(function(s){switch(s.label){case 0:return e=this,[4,this.validateAndSanitizeBody(this.params.Body)];case 1:return e.body=s.sent(),this.totalBytesToUpload=this.byteLength(this.body),this.totalBytesToUpload<=this.minPartSize?(this.params.Body=this.body,t=new hs(this.params),[4,this._createNewS3Client(this.opts,this.emitter)]):[3,3];case 2:return[2,s.sent().send(t)];case 3:return[4,this.createMultiPartUpload()];case 4:n=s.sent(),r=Math.ceil(this.totalBytesToUpload/this.minPartSize),i=0,s.label=5;case 5:return i<r?[4,this.checkIfUploadCancelled(n)]:[3,10];case 6:return s.sent(),o=this.createParts(i),[4,this.uploadParts(n,o)];case 7:return s.sent(),[4,this.checkIfUploadCancelled(n)];case 8:s.sent(),s.label=9;case 9:return i+=this.queueSize,[3,5];case 10:return[4,this.finishMultiPartUpload(n)];case 11:return[2,s.sent()]}}))}))},e.prototype.createParts=function(e){for(var t=[],n=e,r=e*this.minPartSize;r<this.totalBytesToUpload&&t.length<this.queueSize;){var i=Math.min(r+this.minPartSize,this.totalBytesToUpload);t.push({bodyPart:this.body.slice(r,i),partNumber:++n,emitter:new ys.EventEmitter,_lastUploadedBytes:0}),r+=this.minPartSize}return t},e.prototype.createMultiPartUpload=function(){return As(this,void 0,void 0,(function(){var e,t;return Is(this,(function(n){switch(n.label){case 0:return e=new ps(this.params),[4,this._createNewS3Client(this.opts)];case 1:return[4,n.sent().send(e)];case 2:return t=n.sent(),Os.debug(t.UploadId),[2,t.UploadId]}}))}))},e.prototype.uploadParts=function(e,t){return As(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d,h,p;return Is(this,(function(v){switch(v.label){case 0:n=[],v.label=1;case 1:v.trys.push([1,6,7,8]),r=ks(t),i=r.next(),v.label=2;case 2:return i.done?[3,5]:(o=i.value,this.setupEventListener(o),s={PartNumber:o.partNumber,Body:o.bodyPart,UploadId:e,Key:this.params.Key,Bucket:this.params.Bucket},a=new vs(s),[4,this._createNewS3Client(this.opts,o.emitter)]);case 3:u=v.sent(),n.push(u.send(a)),v.label=4;case 4:return i=r.next(),[3,2];case 5:return[3,8];case 6:return c=v.sent(),h={error:c},[3,8];case 7:try{i&&!i.done&&(p=r.return)&&p.call(r)}finally{if(h)throw h.error}return[7];case 8:return v.trys.push([8,10,,11]),[4,Promise.all(n)];case 9:for(f=v.sent(),l=0;l<f.length;l++)this.multiPartMap.push({PartNumber:t[l].partNumber,ETag:f[l].ETag});return[3,11];case 10:return d=v.sent(),Os.error("error happened while uploading a part. Cancelling the multipart upload",d),this.cancelUpload(),[2];case 11:return[2]}}))}))},e.prototype.finishMultiPartUpload=function(e){return As(this,void 0,void 0,(function(){var t,n,r,i;return Is(this,(function(o){switch(o.label){case 0:return t={Bucket:this.params.Bucket,Key:this.params.Key,UploadId:e,MultipartUpload:{Parts:this.multiPartMap}},n=new gs(t),[4,this._createNewS3Client(this.opts)];case 1:r=o.sent(),o.label=2;case 2:return o.trys.push([2,4,,5]),[4,r.send(n)];case 3:return[2,o.sent().Key];case 4:return i=o.sent(),Os.error("error happened while finishing the upload. Cancelling the multipart upload",i),this.cancelUpload(),[2];case 5:return[2]}}))}))},e.prototype.checkIfUploadCancelled=function(e){return As(this,void 0,void 0,(function(){var t,n;return Is(this,(function(r){switch(r.label){case 0:if(!this.cancel)return[3,5];t="Upload was cancelled.",r.label=1;case 1:return r.trys.push([1,3,,4]),[4,this.cleanup(e)];case 2:return r.sent(),[3,4];case 3:return n=r.sent(),t+=n.errorMessage,[3,4];case 4:throw new Error(t);case 5:return[2]}}))}))},e.prototype.cancelUpload=function(){this.cancel=!0},e.prototype.cleanup=function(e){return As(this,void 0,void 0,(function(){var t,n,r;return Is(this,(function(i){switch(i.label){case 0:return this.body=null,this.multiPartMap=[],this.bytesUploaded=0,this.totalBytesToUpload=0,t={Bucket:this.params.Bucket,Key:this.params.Key,UploadId:e},[4,this._createNewS3Client(this.opts)];case 1:return[4,(n=i.sent()).send(new ms(t))];case 2:return i.sent(),[4,n.send(new bs(t))];case 3:if((r=i.sent())&&r.Parts&&r.Parts.length>0)throw new Error("Multi Part upload clean up failed");return[2]}}))}))},e.prototype.setupEventListener=function(e){var t=this;e.emitter.on("sendProgress",(function(n){t.progressChanged(e.partNumber,n.loaded-e._lastUploadedBytes),e._lastUploadedBytes=n.loaded}))},e.prototype.progressChanged=function(e,t){this.bytesUploaded+=t,this.emitter.emit("sendProgress",{loaded:this.bytesUploaded,total:this.totalBytesToUpload,part:e,key:this.params.Key})},e.prototype.byteLength=function(e){if(null==e)return 0;if("number"==typeof e.byteLength)return e.byteLength;if("number"==typeof e.length)return e.length;if("number"==typeof e.size)return e.size;if("string"!=typeof e.path)throw new Error("Cannot determine length of "+e)},e.prototype.validateAndSanitizeBody=function(e){return As(this,void 0,void 0,(function(){return Is(this,(function(t){switch(t.label){case 0:return this.isGenericObject(e)?[2,JSON.stringify(e)]:[3,1];case 1:return this.isBlob(e)?a.a.isReactNative?[4,Object(eo.b)(e)]:[3,3]:[3,4];case 2:return[2,t.sent()];case 3:case 4:return[2,e]}}))}))},e.prototype.isBlob=function(e){return"undefined"!=typeof Blob&&e instanceof Blob},e.prototype.isGenericObject=function(e){if(null!==e&&"object"===Es(e))try{return!(this.byteLength(e)>=0)}catch(e){return!0}return!1},e.prototype._createNewS3Client=function(e,t){return As(this,void 0,void 0,(function(){var n,r,i,o,s;return Is(this,(function(u){switch(u.label){case 0:return[4,this._getCredentials()];case 1:return n=u.sent(),r=e.region,i=e.dangerouslyConnectToHttpEndpointForTesting,o={},i&&(o={endpoint:"http://localhost:20005",tls:!1,bucketEndpoint:!1,forcePathStyle:!0}),(s=new jo(Ms(Ms({region:r,credentials:n},o),{requestHandler:new Vo({},t),customUserAgent:Object(a.b)(),urlParser:Ss}))).middlewareStack.remove("contentLengthMiddleware"),[2,s]}}))}))},e.prototype._getCredentials=function(){return s.a.get().then((function(e){if(!e)return!1;var t=s.a.shear(e);return Os.debug("set credentials for storage",t),t})).catch((function(e){return Os.warn("ensure credentials error",e),!1}))},e}();function Cs(e){return(Cs="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)}var Ts=function(){return(Ts=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},Ps=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},Ns=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},Rs=new r.a("AWSS3Provider"),Ls="undefined"!=typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("amplify_default"):"@@amplify_default",js=function(e,t,n,r,i){if(e){var s={attrs:n};r&&(s.metrics=r),o.a.dispatch("storage",{event:t,data:s,message:i},"Storage",Ls)}},Ds=function(){function e(e){this._config=e||{},Rs.debug("Storage Options",this._config)}return e.prototype.getCategory=function(){return e.CATEGORY},e.prototype.getProviderName=function(){return e.PROVIDER_NAME},e.prototype.configure=function(e){if(Rs.debug("configure Storage",e),!e)return this._config;var t=i.a.parseMobilehubConfig(e);return this._config=Object.assign({},this._config,t.Storage),this._config.bucket||Rs.debug("Do not have bucket yet"),this._config},e.prototype.get=function(e,t){return Ps(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d,h,p,v,g,m,b,y,w,_,S,E;return Ns(this,(function(M){switch(M.label){case 0:return[4,this._ensureCredentials()];case 1:if(!M.sent())return[2,Promise.reject("No credentials")];if(n=Object.assign({},this._config,t),r=n.bucket,i=n.download,o=n.cacheControl,s=n.contentDisposition,a=n.contentEncoding,u=n.contentLanguage,c=n.contentType,f=n.expires,l=n.track,d=this._prefix(n),h=d+e,p=this._createNewS3Client(n),Rs.debug("get "+e+" from "+h),v={Bucket:r,Key:h},o&&(v.ResponseCacheControl=o),s&&(v.ResponseContentDisposition=s),a&&(v.ResponseContentEncoding=a),u&&(v.ResponseContentLanguage=u),c&&(v.ResponseContentType=c),!0!==i)return[3,5];g=new $i(v),M.label=2;case 2:return M.trys.push([2,4,,5]),[4,p.send(g)];case 3:return m=M.sent(),js(l,"download",{method:"get",result:"success"},{fileSize:Number(m.Body.size||m.Body.length)},"Download success for "+e),[2,m];case 4:throw b=M.sent(),js(l,"download",{method:"get",result:"failed"},null,"Download failed with "+b.message),b;case 5:v.Expires=f||900,M.label=6;case 6:return M.trys.push([6,9,,10]),y=new zo(Ts({},p.config)),[4,Bo(p,new $i(v))];case 7:return w=M.sent(),S=Uo,[4,y.presign(w,{expiresIn:v.Expires})];case 8:return _=S.apply(void 0,[M.sent()]),js(l,"getSignedUrl",{method:"get",result:"success"},null,"Signed URL: "+_),[2,_];case 9:throw E=M.sent(),Rs.warn("get signed url error",E),js(l,"getSignedUrl",{method:"get",result:"failed"},null,"Could not get a signed URL for "+e),E;case 10:return[2]}}))}))},e.prototype.put=function(e,t,n){return Ps(this,void 0,void 0,(function(){var r,i,o,s,a,u,c,f,l,d,h,p,v,g,m,b,y,w,_,S,E,M,A,I;return Ns(this,(function(k){switch(k.label){case 0:return[4,this._ensureCredentials()];case 1:if(!k.sent())return[2,Promise.reject("No credentials")];r=Object.assign({},this._config,n),i=r.bucket,o=r.track,s=r.progressCallback,a=r.contentType,u=r.contentDisposition,c=r.cacheControl,f=r.expires,l=r.metadata,d=r.tagging,h=r.acl,p=r.serverSideEncryption,v=r.SSECustomerAlgorithm,g=r.SSECustomerKey,m=r.SSECustomerKeyMD5,b=r.SSEKMSKeyId,y=a||"binary/octet-stream",w=this._prefix(r),_=w+e,Rs.debug("put "+e+" to "+_),S={Bucket:i,Key:_,Body:t,ContentType:y},c&&(S.CacheControl=c),u&&(S.ContentDisposition=u),f&&(S.Expires=f),l&&(S.Metadata=l),d&&(S.Tagging=d),p&&(S.ServerSideEncryption=p,v&&(S.SSECustomerAlgorithm=v),g&&(S.SSECustomerKey=g),m&&(S.SSECustomerKeyMD5=m),b&&(S.SSEKMSKeyId=b)),E=new ys.EventEmitter,M=new xs(S,r,E),h&&(S.ACL=h),k.label=2;case 2:return k.trys.push([2,4,,5]),E.on("sendProgress",(function(e){s&&("function"==typeof s?s(e):Rs.warn("progressCallback should be a function, not a "+Cs(s)))})),[4,M.upload()];case 3:return A=k.sent(),Rs.debug("upload result",A),js(o,"upload",{method:"put",result:"success"},null,"Upload success for "+e),[2,{key:e}];case 4:throw I=k.sent(),Rs.warn("error uploading",I),js(o,"upload",{method:"put",result:"failed"},null,"Error uploading "+e),I;case 5:return[2]}}))}))},e.prototype.remove=function(e,t){return Ps(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f;return Ns(this,(function(l){switch(l.label){case 0:return[4,this._ensureCredentials()];case 1:if(!l.sent())return[2,Promise.reject("No credentials")];n=Object.assign({},this._config,t),r=n.bucket,i=n.track,o=this._prefix(n),s=o+e,a=this._createNewS3Client(n),Rs.debug("remove "+e+" from "+s),u=new Yi({Bucket:r,Key:s}),l.label=2;case 2:return l.trys.push([2,4,,5]),[4,a.send(u)];case 3:return c=l.sent(),js(i,"delete",{method:"remove",result:"success"},null,"Deleted "+e+" successfully"),[2,c];case 4:throw f=l.sent(),js(i,"delete",{method:"remove",result:"failed"},null,"Deletion of "+e+" failed with "+f),f;case 5:return[2]}}))}))},e.prototype.list=function(e,t){return Ps(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d;return Ns(this,(function(h){switch(h.label){case 0:return[4,this._ensureCredentials()];case 1:if(!h.sent())return[2,Promise.reject("No credentials")];n=Object.assign({},this._config,t),r=n.bucket,i=n.track,o=n.maxKeys,s=this._prefix(n),a=s+e,u=this._createNewS3Client(n),Rs.debug("list "+e+" from "+a),c=new Ji({Bucket:r,Prefix:a,MaxKeys:o}),h.label=2;case 2:return h.trys.push([2,4,,5]),[4,u.send(c)];case 3:return f=h.sent(),l=[],f&&f.Contents&&(l=f.Contents.map((function(e){return{key:e.Key.substr(s.length),eTag:e.ETag,lastModified:e.LastModified,size:e.Size}}))),js(i,"list",{method:"list",result:"success"},null,l.length+" items returned from list operation"),Rs.debug("list",l),[2,l];case 4:throw d=h.sent(),Rs.warn("list error",d),js(i,"list",{method:"list",result:"failed"},null,"Listing items failed: "+d.message),d;case 5:return[2]}}))}))},e.prototype._ensureCredentials=function(){var e=this;return s.a.get().then((function(t){if(!t)return!1;var n=s.a.shear(t);return Rs.debug("set credentials for storage",n),e._config.credentials=n,!0})).catch((function(e){return Rs.warn("ensure credentials error",e),!1}))},e.prototype._prefix=function(e){var t=e.credentials,n=e.level,r=e.customPrefix||{},i=e.identityId||t.identityId,o=(void 0!==r.private?r.private:"private/")+i+"/",s=(void 0!==r.protected?r.protected:"protected/")+i+"/",a=void 0!==r.public?r.public:"public/";switch(n){case"private":return o;case"protected":return s;default:return a}},e.prototype._createNewS3Client=function(e,t){var n=e.region,r=e.credentials,i={};return e.dangerouslyConnectToHttpEndpointForTesting&&(i={endpoint:"http://localhost:20005",tls:!1,bucketEndpoint:!1,forcePathStyle:!0}),new jo(Ts(Ts({region:n,credentials:r,customUserAgent:Object(a.b)()},i),{requestHandler:new Vo({},t)}))},e.CATEGORY="Storage",e.PROVIDER_NAME="AWSS3",e}(),Us=function(){return(Us=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},Bs=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},Fs=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},zs=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},qs=new r.a("StorageClass"),Ks=function(){function e(){this._config={},this._pluggables=[],qs.debug("Storage Options",this._config),this.get=this.get.bind(this),this.put=this.put.bind(this),this.remove=this.remove.bind(this),this.list=this.list.bind(this)}return e.prototype.getModuleName=function(){return"Storage"},e.prototype.addPluggable=function(e){if(e&&"Storage"===e.getCategory()){this._pluggables.push(e);return e.configure(this._config[e.getProviderName()])}},e.prototype.getPluggable=function(e){var t=this._pluggables.find((function(t){return t.getProviderName()===e}));return void 0===t?(qs.debug("No plugin found with providerName",e),null):t},e.prototype.removePluggable=function(e){this._pluggables=this._pluggables.filter((function(t){return t.getProviderName()!==e}))},e.prototype.configure=function(e){var t=this;if(qs.debug("configure Storage"),!e)return this._config;var n=i.a.parseMobilehubConfig(e),r=Object.keys(n.Storage),o=["bucket","region","level","track","customPrefix","serverSideEncryption","SSECustomerAlgorithm","SSECustomerKey","SSECustomerKeyMD5","SSEKMSKeyId"],s=function(e){return o.some((function(t){return t===e}))};return r&&r.find((function(e){return s(e)}))&&!n.Storage.AWSS3&&(n.Storage.AWSS3={}),Object.entries(n.Storage).map((function(e){var t=zs(e,2),r=t[0],i=t[1];r&&s(r)&&void 0!==i&&(n.Storage.AWSS3[r]=i,delete n.Storage[r])})),Object.keys(n.Storage).forEach((function(e){"string"!=typeof n.Storage[e]&&(t._config[e]=Us(Us({},t._config[e]),n.Storage[e]))})),this._pluggables.forEach((function(e){e.configure(t._config[e.getProviderName()])})),0===this._pluggables.length&&this.addPluggable(new Ds),this._config},e.prototype.get=function(e,t){return Bs(this,void 0,void 0,(function(){var n,r,i;return Fs(this,(function(o){return n=(t||{}).provider,r=void 0===n?"AWSS3":n,void 0===(i=this._pluggables.find((function(e){return e.getProviderName()===r})))&&(qs.debug("No plugin found with providerName",r),Promise.reject("No plugin found in Storage for the provider")),[2,i.get(e,t)]}))}))},e.prototype.put=function(e,t,n){return Bs(this,void 0,void 0,(function(){var r,i,o;return Fs(this,(function(s){return r=(n||{}).provider,i=void 0===r?"AWSS3":r,void 0===(o=this._pluggables.find((function(e){return e.getProviderName()===i})))&&(qs.debug("No plugin found with providerName",i),Promise.reject("No plugin found in Storage for the provider")),[2,o.put(e,t,n)]}))}))},e.prototype.remove=function(e,t){return Bs(this,void 0,void 0,(function(){var n,r,i;return Fs(this,(function(o){return n=(t||{}).provider,r=void 0===n?"AWSS3":n,void 0===(i=this._pluggables.find((function(e){return e.getProviderName()===r})))&&(qs.debug("No plugin found with providerName",r),Promise.reject("No plugin found in Storage for the provider")),[2,i.remove(e,t)]}))}))},e.prototype.list=function(e,t){return Bs(this,void 0,void 0,(function(){var n,r,i;return Fs(this,(function(o){return n=(t||{}).provider,r=void 0===n?"AWSS3":n,void 0===(i=this._pluggables.find((function(e){return e.getProviderName()===r})))&&(qs.debug("No plugin found with providerName",r),Promise.reject("No plugin found in Storage for the provider")),[2,i.list(e,t)]}))}))},e}(),Hs=n(19),Vs=function(){return(Vs=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},Gs=new r.a("Storage"),Ws=null,$s=function(){if(Ws)return Ws;Gs.debug("Create Storage Instance, debug"),(Ws=new Ks).vault=new Ks;var e=Ws.configure;return Ws.configure=function(t){Gs.debug("storage configure called");var n=Vs({},e.call(Ws,t));Object.keys(n).forEach((function(e){"string"!=typeof n[e]&&(n[e]=Vs(Vs({},n[e]),{level:"private"}))})),Gs.debug("storage vault configure called"),Ws.vault.configure(n)},Ws}();Hs.a.register($s)},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=new(n(44).a)("ClientDevice_Browser");function i(){return"undefined"==typeof window?{}:function(){if("undefined"==typeof window)return r.warn("No window object available to get browser client info"),{};var e=window.navigator;if(!e)return r.warn("No navigator object available to get browser client info"),{};var t=e.platform,n=e.product,i=e.vendor,o=e.userAgent,s=e.language,a=function(e){var t=/.+(Opera[\s[A-Z]*|OPR[\sA-Z]*)\/([0-9\.]+).*/i.exec(e);if(t)return{type:t[1],version:t[2]};var n=/.+(Trident|Edge)\/([0-9\.]+).*/i.exec(e);if(n)return{type:n[1],version:n[2]};var r=/.+(Chrome|Firefox|FxiOS)\/([0-9\.]+).*/i.exec(e);if(r)return{type:r[1],version:r[2]};var i=/.+(Safari)\/([0-9\.]+).*/i.exec(e);if(i)return{type:i[1],version:i[2]};var o=/.+(AppleWebKit)\/([0-9\.]+).*/i.exec(e);if(o)return{type:o[1],version:o[2]};var s=/.*([A-Z]+)\/([0-9\.]+).*/i.exec(e);if(s)return{type:s[1],version:s[2]};return{type:"",version:""}}(o),u=(c=/\(([A-Za-z\s].*)\)/.exec((new Date).toString()),c&&c[1]||"");var c;return{platform:t,make:n||i,model:a.type,version:a.version,appVersion:[a.type,a.version].join("/"),language:s,timezone:u}}()}var o=function(){function e(){}return e.clientInfo=function(){return i()},e.dimension=function(){return"undefined"==typeof window?(r.warn("No window object available to get browser client info"),{width:320,height:320}):{width:window.innerWidth,height:window.innerHeight}},e}()},function(e,t,n){"use strict";n.d(t,"a",(function(){return f}));var r=n(44),i=new r.a("I18n"),o=function(){function e(e){this._options=null,this._lang=null,this._dict={},this._options=Object.assign({},e),this._lang=this._options.language,!this._lang&&"undefined"!=typeof window&&window&&window.navigator&&(this._lang=window.navigator.language),i.debug(this._lang)}return e.prototype.setLanguage=function(e){this._lang=e},e.prototype.get=function(e,t){if(void 0===t&&(t=void 0),!this._lang)return void 0!==t?t:e;var n=this._lang,r=this.getByLanguage(e,n);return r||(n.indexOf("-")>0&&(r=this.getByLanguage(e,n.split("-")[0])),r||(void 0!==t?t:e))},e.prototype.getByLanguage=function(e,t,n){if(void 0===n&&(n=null),!t)return n;var r=this._dict[t];return r?r[e]:n},e.prototype.putVocabulariesForLanguage=function(e,t){var n=this._dict[e];n||(n=this._dict[e]={}),Object.assign(n,t)},e.prototype.putVocabularies=function(e){var t=this;Object.keys(e).map((function(n){t.putVocabulariesForLanguage(n,e[n])}))},e}(),s=n(19),a=new r.a("I18n"),u=null,c=null,f=function(){function e(){}return e.configure=function(t){return a.debug("configure I18n"),t?(u=Object.assign({},u,t.I18n||t),e.createInstance(),u):u},e.getModuleName=function(){return"I18n"},e.createInstance=function(){a.debug("create I18n instance"),c||(c=new o(u))},e.setLanguage=function(t){return e.checkConfig(),c.setLanguage(t)},e.get=function(t,n){return e.checkConfig()?c.get(t,n):void 0===n?t:n},e.putVocabulariesForLanguage=function(t,n){return e.checkConfig(),c.putVocabulariesForLanguage(t,n)},e.putVocabularies=function(t){return e.checkConfig(),c.putVocabularies(t)},e.checkConfig=function(){return c||(c=new o(u)),!0},e}();s.a.register(f)},function(e,t,n){"use strict";n.d(t,"a",(function(){return a}));var r=n(44),i=n(33),o=n(19);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)}var a=function(){function e(){this._logger=new r.a("ServiceWorker")}return Object.defineProperty(e.prototype,"serviceWorker",{get:function(){return this._serviceWorker},enumerable:!0,configurable:!0}),e.prototype.register=function(e,t){var n=this;return void 0===e&&(e="/service-worker.js"),void 0===t&&(t="/"),this._logger.debug("registering "+e),this._logger.debug("registering service worker with scope "+t),new Promise((function(r,i){if(!navigator||!("serviceWorker"in navigator))return i(new Error("Service Worker not available"));navigator.serviceWorker.register(e,{scope:t}).then((function(e){return e.installing?n._serviceWorker=e.installing:e.waiting?n._serviceWorker=e.waiting:e.active&&(n._serviceWorker=e.active),n._registration=e,n._setupListeners(),n._logger.debug("Service Worker Registration Success: "+e),r(e)})).catch((function(e){return n._logger.debug("Service Worker Registration Failed "+e),i(e)}))}))},e.prototype.enablePush=function(e){var t=this;if(!this._registration)throw new Error("Service Worker not registered");return this._publicKey=e,new Promise((function(n,r){if(!Object(i.b)().isBrowser)return r(new Error("Service Worker not available"));t._registration.pushManager.getSubscription().then((function(r){if(!r)return t._logger.debug("User is NOT subscribed to push"),t._registration.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:t._urlB64ToUint8Array(e)}).then((function(e){t._subscription=e,t._logger.debug("User subscribed: "+JSON.stringify(e)),n(e)})).catch((function(e){t._logger.error(e)}));t._subscription=r,t._logger.debug("User is subscribed to push: "+JSON.stringify(r)),n(r)}))}))},e.prototype._urlB64ToUint8Array=function(e){for(var t=(e+"=".repeat((4-e.length%4)%4)).replace(/\-/g,"+").replace(/_/g,"/"),n=window.atob(t),r=new Uint8Array(n.length),i=0;i<n.length;++i)r[i]=n.charCodeAt(i);return r},e.prototype.send=function(e){this._serviceWorker&&this._serviceWorker.postMessage("object"===s(e)?JSON.stringify(e):e)},e.prototype._setupListeners=function(){var e=this;this._serviceWorker.addEventListener("statechange",(function(t){var n=e._serviceWorker.state;e._logger.debug("ServiceWorker statechange: "+n),o.a.Analytics&&"function"==typeof o.a.Analytics.record&&o.a.Analytics.record({name:"ServiceWorker",attributes:{state:n}})})),this._serviceWorker.addEventListener("message",(function(t){e._logger.debug("ServiceWorker message event: "+t)}))},e}()},function(e,t,n){"use strict";n.d(t,"a",(function(){return dr}));var r=n(88),i=n(44),o=n(141),s=n(104),a=n(33),u=n(50),c=n(89),f=function(e,t){return(f=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function l(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}f(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var d=function(){return(d=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function h(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function p(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}Object.create;function v(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s}var g,m,b,y,w,_,S,E,M,A,I,k,O,x,C,T,P,N,R,L,j,D,U,B,F,z,q,K,H,V,G,W,$,Y,J,Z,X,Q,ee,te,ne,re,ie,oe,se,ae,ue,ce,fe,le,de,he,pe,ve,ge,me,be,ye,we,_e,Se,Ee,Me,Ae,Ie,ke,Oe,xe,Ce,Te,Pe,Ne,Re,Le,je,De,Ue,Be,Fe,ze,qe,Ke,He,Ve,Ge,We,$e,Ye,Je,Ze,Xe,Qe,et,tt,nt,rt,it,ot,st,at,ut,ct,ft,lt,dt,ht,pt,vt,gt,mt,bt,yt,wt,_t,St,Et,Mt,At,It,kt,Ot,xt,Ct,Tt,Pt,Nt,Rt,Lt,jt,Dt,Ut;Object.create;(g||(g={})).filterSensitiveLog=function(e){return d({},e)},(m||(m={})).filterSensitiveLog=function(e){return d({},e)},(b||(b={})).filterSensitiveLog=function(e){return d({},e)},(y||(y={})).filterSensitiveLog=function(e){return d({},e)},(w||(w={})).filterSensitiveLog=function(e){return d({},e)},(_||(_={})).filterSensitiveLog=function(e){return d({},e)},(S||(S={})).filterSensitiveLog=function(e){return d({},e)},(E||(E={})).filterSensitiveLog=function(e){return d({},e)},(M||(M={})).filterSensitiveLog=function(e){return d({},e)},(A||(A={})).filterSensitiveLog=function(e){return d({},e)},(I||(I={})).filterSensitiveLog=function(e){return d({},e)},(k||(k={})).filterSensitiveLog=function(e){return d({},e)},(O||(O={})).filterSensitiveLog=function(e){return d({},e)},(x||(x={})).filterSensitiveLog=function(e){return d({},e)},(C||(C={})).filterSensitiveLog=function(e){return d({},e)},(T||(T={})).filterSensitiveLog=function(e){return d({},e)},(P||(P={})).filterSensitiveLog=function(e){return d({},e)},(N||(N={})).filterSensitiveLog=function(e){return d({},e)},(R||(R={})).filterSensitiveLog=function(e){return d({},e)},(L||(L={})).filterSensitiveLog=function(e){return d({},e)},(j||(j={})).filterSensitiveLog=function(e){return d({},e)},(D||(D={})).filterSensitiveLog=function(e){return d({},e)},(U||(U={})).filterSensitiveLog=function(e){return d({},e)},(B||(B={})).filterSensitiveLog=function(e){return d({},e)},(F||(F={})).filterSensitiveLog=function(e){return d({},e)},(z||(z={})).filterSensitiveLog=function(e){return d({},e)},(q||(q={})).filterSensitiveLog=function(e){return d({},e)},(K||(K={})).filterSensitiveLog=function(e){return d({},e)},(H||(H={})).filterSensitiveLog=function(e){return d({},e)},(V||(V={})).filterSensitiveLog=function(e){return d({},e)},(G||(G={})).filterSensitiveLog=function(e){return d({},e)},(W||(W={})).filterSensitiveLog=function(e){return d({},e)},($||($={})).filterSensitiveLog=function(e){return d({},e)},(Y||(Y={})).filterSensitiveLog=function(e){return d({},e)},(J||(J={})).filterSensitiveLog=function(e){return d({},e)},(Z||(Z={})).filterSensitiveLog=function(e){return d({},e)},(X||(X={})).filterSensitiveLog=function(e){return d({},e)},(Q||(Q={})).filterSensitiveLog=function(e){return d({},e)},(ee||(ee={})).filterSensitiveLog=function(e){return d({},e)},(te||(te={})).filterSensitiveLog=function(e){return d({},e)},(ne||(ne={})).filterSensitiveLog=function(e){return d({},e)},(re||(re={})).filterSensitiveLog=function(e){return d({},e)},(ie||(ie={})).filterSensitiveLog=function(e){return d({},e)},(oe||(oe={})).filterSensitiveLog=function(e){return d({},e)},(se||(se={})).filterSensitiveLog=function(e){return d({},e)},(ae||(ae={})).filterSensitiveLog=function(e){return d({},e)},(ue||(ue={})).filterSensitiveLog=function(e){return d({},e)},(ce||(ce={})).filterSensitiveLog=function(e){return d({},e)},(fe||(fe={})).filterSensitiveLog=function(e){return d({},e)},(le||(le={})).filterSensitiveLog=function(e){return d({},e)},(de||(de={})).filterSensitiveLog=function(e){return d({},e)},(he||(he={})).filterSensitiveLog=function(e){return d({},e)},(pe||(pe={})).filterSensitiveLog=function(e){return d({},e)},(ve||(ve={})).filterSensitiveLog=function(e){return d({},e)},(ge||(ge={})).filterSensitiveLog=function(e){return d({},e)},(me||(me={})).filterSensitiveLog=function(e){return d({},e)},(be||(be={})).filterSensitiveLog=function(e){return d({},e)},(ye||(ye={})).filterSensitiveLog=function(e){return d({},e)},(we||(we={})).filterSensitiveLog=function(e){return d({},e)},(_e||(_e={})).filterSensitiveLog=function(e){return d({},e)},(Se||(Se={})).filterSensitiveLog=function(e){return d({},e)},(Ee||(Ee={})).filterSensitiveLog=function(e){return d({},e)},(Me||(Me={})).filterSensitiveLog=function(e){return d({},e)},(Ae||(Ae={})).filterSensitiveLog=function(e){return d({},e)},(Ie||(Ie={})).filterSensitiveLog=function(e){return d({},e)},(ke||(ke={})).filterSensitiveLog=function(e){return d({},e)},(Oe||(Oe={})).filterSensitiveLog=function(e){return d({},e)},(xe||(xe={})).filterSensitiveLog=function(e){return d({},e)},(Ce||(Ce={})).filterSensitiveLog=function(e){return d({},e)},(Te||(Te={})).filterSensitiveLog=function(e){return d({},e)},(Pe||(Pe={})).filterSensitiveLog=function(e){return d({},e)},(Ne||(Ne={})).filterSensitiveLog=function(e){return d({},e)},(Re||(Re={})).filterSensitiveLog=function(e){return d({},e)},(Le||(Le={})).filterSensitiveLog=function(e){return d({},e)},(je||(je={})).filterSensitiveLog=function(e){return d({},e)},(De||(De={})).filterSensitiveLog=function(e){return d({},e)},(Ue||(Ue={})).filterSensitiveLog=function(e){return d({},e)},(Be||(Be={})).filterSensitiveLog=function(e){return d({},e)},(Fe||(Fe={})).filterSensitiveLog=function(e){return d({},e)},(ze||(ze={})).filterSensitiveLog=function(e){return d({},e)},(qe||(qe={})).filterSensitiveLog=function(e){return d({},e)},(Ke||(Ke={})).filterSensitiveLog=function(e){return d({},e)},(He||(He={})).filterSensitiveLog=function(e){return d({},e)},(Ve||(Ve={})).filterSensitiveLog=function(e){return d({},e)},(Ge||(Ge={})).filterSensitiveLog=function(e){return d({},e)},(We||(We={})).filterSensitiveLog=function(e){return d({},e)},($e||($e={})).filterSensitiveLog=function(e){return d({},e)},(Ye||(Ye={})).filterSensitiveLog=function(e){return d({},e)},(Je||(Je={})).filterSensitiveLog=function(e){return d({},e)},(Ze||(Ze={})).filterSensitiveLog=function(e){return d({},e)},(Xe||(Xe={})).filterSensitiveLog=function(e){return d({},e)},(Qe||(Qe={})).filterSensitiveLog=function(e){return d({},e)},(et||(et={})).filterSensitiveLog=function(e){return d({},e)},(tt||(tt={})).filterSensitiveLog=function(e){return d({},e)},(nt||(nt={})).filterSensitiveLog=function(e){return d({},e)},(rt||(rt={})).filterSensitiveLog=function(e){return d({},e)},(it||(it={})).filterSensitiveLog=function(e){return d({},e)},(ot||(ot={})).filterSensitiveLog=function(e){return d({},e)},(st||(st={})).filterSensitiveLog=function(e){return d({},e)},(at||(at={})).filterSensitiveLog=function(e){return d({},e)},(ut||(ut={})).filterSensitiveLog=function(e){return d({},e)},(ct||(ct={})).filterSensitiveLog=function(e){return d({},e)},(ft||(ft={})).filterSensitiveLog=function(e){return d({},e)},(lt||(lt={})).filterSensitiveLog=function(e){return d({},e)},(dt||(dt={})).filterSensitiveLog=function(e){return d({},e)},(ht||(ht={})).filterSensitiveLog=function(e){return d({},e)},(pt||(pt={})).filterSensitiveLog=function(e){return d({},e)},(vt||(vt={})).filterSensitiveLog=function(e){return d({},e)},(gt||(gt={})).filterSensitiveLog=function(e){return d({},e)},(mt||(mt={})).filterSensitiveLog=function(e){return d({},e)},(bt||(bt={})).filterSensitiveLog=function(e){return d({},e)},(yt||(yt={})).filterSensitiveLog=function(e){return d({},e)},(wt||(wt={})).filterSensitiveLog=function(e){return d({},e)},(_t||(_t={})).filterSensitiveLog=function(e){return d({},e)},(St||(St={})).filterSensitiveLog=function(e){return d({},e)},(Et||(Et={})).filterSensitiveLog=function(e){return d({},e)},(Mt||(Mt={})).filterSensitiveLog=function(e){return d({},e)},(At||(At={})).filterSensitiveLog=function(e){return d({},e)},(It||(It={})).filterSensitiveLog=function(e){return d({},e)},(kt||(kt={})).filterSensitiveLog=function(e){return d({},e)},(Ot||(Ot={})).filterSensitiveLog=function(e){return d({},e)},(xt||(xt={})).filterSensitiveLog=function(e){return d({},e)},(Ct||(Ct={})).filterSensitiveLog=function(e){return d({},e)},(Tt||(Tt={})).filterSensitiveLog=function(e){return d({},e)},(Pt||(Pt={})).filterSensitiveLog=function(e){return d({},e)},(Nt||(Nt={})).filterSensitiveLog=function(e){return d({},e)},(Rt||(Rt={})).filterSensitiveLog=function(e){return d({},e)},(Lt||(Lt={})).filterSensitiveLog=function(e){return d({},e)},(jt||(jt={})).filterSensitiveLog=function(e){return d({},e)},(Dt||(Dt={})).filterSensitiveLog=function(e){return d({},e)},(Ut||(Ut={})).filterSensitiveLog=function(e){return d({},e)};var Bt=n(2),Ft=n(0),zt=function(e,t){return h(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,h,v,g,m;return p(this,(function(p){switch(p.label){case 0:return r=[d({},e)],m={},[4,mn(e.body,t)];case 1:switch(n=d.apply(void 0,r.concat([(m.body=p.sent(),m)])),o="UnknownError",o=bn(e,n.body),o){case"BadRequestException":case"com.amazonaws.pinpoint#BadRequestException":return[3,2];case"ForbiddenException":case"com.amazonaws.pinpoint#ForbiddenException":return[3,4];case"InternalServerErrorException":case"com.amazonaws.pinpoint#InternalServerErrorException":return[3,6];case"MethodNotAllowedException":case"com.amazonaws.pinpoint#MethodNotAllowedException":return[3,8];case"NotFoundException":case"com.amazonaws.pinpoint#NotFoundException":return[3,10];case"PayloadTooLargeException":case"com.amazonaws.pinpoint#PayloadTooLargeException":return[3,12];case"TooManyRequestsException":case"com.amazonaws.pinpoint#TooManyRequestsException":return[3,14]}return[3,16];case 2:return s=[{}],[4,Kt(n,t)];case 3:return i=d.apply(void 0,[d.apply(void 0,s.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 4:return a=[{}],[4,Ht(n,t)];case 5:return i=d.apply(void 0,[d.apply(void 0,a.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 6:return u=[{}],[4,Vt(n,t)];case 7:return i=d.apply(void 0,[d.apply(void 0,u.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 8:return c=[{}],[4,Gt(n,t)];case 9:return i=d.apply(void 0,[d.apply(void 0,c.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 10:return f=[{}],[4,Wt(n,t)];case 11:return i=d.apply(void 0,[d.apply(void 0,f.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 12:return l=[{}],[4,$t(n,t)];case 13:return i=d.apply(void 0,[d.apply(void 0,l.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 14:return h=[{}],[4,Yt(n,t)];case 15:return i=d.apply(void 0,[d.apply(void 0,h.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 16:v=n.body,o=v.code||v.Code||o,i=d(d({},v),{name:""+o,message:v.message||v.Message||o,$fault:"client",$metadata:vn(e)}),p.label=17;case 17:return g=i.message||i.Message||o,i.message=g,delete i.Message,[2,Promise.reject(Object.assign(new Error(g),i))]}}))}))},qt=function(e,t){return h(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,h,v,g,m;return p(this,(function(p){switch(p.label){case 0:return r=[d({},e)],m={},[4,mn(e.body,t)];case 1:switch(n=d.apply(void 0,r.concat([(m.body=p.sent(),m)])),o="UnknownError",o=bn(e,n.body),o){case"BadRequestException":case"com.amazonaws.pinpoint#BadRequestException":return[3,2];case"ForbiddenException":case"com.amazonaws.pinpoint#ForbiddenException":return[3,4];case"InternalServerErrorException":case"com.amazonaws.pinpoint#InternalServerErrorException":return[3,6];case"MethodNotAllowedException":case"com.amazonaws.pinpoint#MethodNotAllowedException":return[3,8];case"NotFoundException":case"com.amazonaws.pinpoint#NotFoundException":return[3,10];case"PayloadTooLargeException":case"com.amazonaws.pinpoint#PayloadTooLargeException":return[3,12];case"TooManyRequestsException":case"com.amazonaws.pinpoint#TooManyRequestsException":return[3,14]}return[3,16];case 2:return s=[{}],[4,Kt(n,t)];case 3:return i=d.apply(void 0,[d.apply(void 0,s.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 4:return a=[{}],[4,Ht(n,t)];case 5:return i=d.apply(void 0,[d.apply(void 0,a.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 6:return u=[{}],[4,Vt(n,t)];case 7:return i=d.apply(void 0,[d.apply(void 0,u.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 8:return c=[{}],[4,Gt(n,t)];case 9:return i=d.apply(void 0,[d.apply(void 0,c.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 10:return f=[{}],[4,Wt(n,t)];case 11:return i=d.apply(void 0,[d.apply(void 0,f.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 12:return l=[{}],[4,$t(n,t)];case 13:return i=d.apply(void 0,[d.apply(void 0,l.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 14:return h=[{}],[4,Yt(n,t)];case 15:return i=d.apply(void 0,[d.apply(void 0,h.concat([p.sent()])),{name:o,$metadata:vn(e)}]),[3,17];case 16:v=n.body,o=v.code||v.Code||o,i=d(d({},v),{name:""+o,message:v.message||v.Message||o,$fault:"client",$metadata:vn(e)}),p.label=17;case 17:return g=i.message||i.Message||o,i.message=g,delete i.Message,[2,Promise.reject(Object.assign(new Error(g),i))]}}))}))},Kt=function(e,t){return h(void 0,void 0,void 0,(function(){var t,n;return p(this,(function(r){return t={name:"BadRequestException",$fault:"client",$metadata:vn(e),Message:void 0,RequestID:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),void 0!==n.RequestID&&null!==n.RequestID&&(t.RequestID=n.RequestID),[2,t]}))}))},Ht=function(e,t){return h(void 0,void 0,void 0,(function(){var t,n;return p(this,(function(r){return t={name:"ForbiddenException",$fault:"client",$metadata:vn(e),Message:void 0,RequestID:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),void 0!==n.RequestID&&null!==n.RequestID&&(t.RequestID=n.RequestID),[2,t]}))}))},Vt=function(e,t){return h(void 0,void 0,void 0,(function(){var t,n;return p(this,(function(r){return t={name:"InternalServerErrorException",$fault:"server",$metadata:vn(e),Message:void 0,RequestID:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),void 0!==n.RequestID&&null!==n.RequestID&&(t.RequestID=n.RequestID),[2,t]}))}))},Gt=function(e,t){return h(void 0,void 0,void 0,(function(){var t,n;return p(this,(function(r){return t={name:"MethodNotAllowedException",$fault:"client",$metadata:vn(e),Message:void 0,RequestID:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),void 0!==n.RequestID&&null!==n.RequestID&&(t.RequestID=n.RequestID),[2,t]}))}))},Wt=function(e,t){return h(void 0,void 0,void 0,(function(){var t,n;return p(this,(function(r){return t={name:"NotFoundException",$fault:"client",$metadata:vn(e),Message:void 0,RequestID:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),void 0!==n.RequestID&&null!==n.RequestID&&(t.RequestID=n.RequestID),[2,t]}))}))},$t=function(e,t){return h(void 0,void 0,void 0,(function(){var t,n;return p(this,(function(r){return t={name:"PayloadTooLargeException",$fault:"client",$metadata:vn(e),Message:void 0,RequestID:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),void 0!==n.RequestID&&null!==n.RequestID&&(t.RequestID=n.RequestID),[2,t]}))}))},Yt=function(e,t){return h(void 0,void 0,void 0,(function(){var t,n;return p(this,(function(r){return t={name:"TooManyRequestsException",$fault:"client",$metadata:vn(e),Message:void 0,RequestID:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),void 0!==n.RequestID&&null!==n.RequestID&&(t.RequestID=n.RequestID),[2,t]}))}))},Jt=function(e,t){return d(d(d(d(d(d(d(d({},void 0!==e.AppVersion&&{AppVersion:e.AppVersion}),void 0!==e.Locale&&{Locale:e.Locale}),void 0!==e.Make&&{Make:e.Make}),void 0!==e.Model&&{Model:e.Model}),void 0!==e.ModelVersion&&{ModelVersion:e.ModelVersion}),void 0!==e.Platform&&{Platform:e.Platform}),void 0!==e.PlatformVersion&&{PlatformVersion:e.PlatformVersion}),void 0!==e.Timezone&&{Timezone:e.Timezone})},Zt=function(e,t){return d(d(d(d(d(d({},void 0!==e.City&&{City:e.City}),void 0!==e.Country&&{Country:e.Country}),void 0!==e.Latitude&&{Latitude:e.Latitude}),void 0!==e.Longitude&&{Longitude:e.Longitude}),void 0!==e.PostalCode&&{PostalCode:e.PostalCode}),void 0!==e.Region&&{Region:e.Region})},Xt=function(e,t){return d(d(d(d(d(d(d(d(d(d(d({},void 0!==e.Address&&{Address:e.Address}),void 0!==e.Attributes&&{Attributes:an(e.Attributes,t)}),void 0!==e.ChannelType&&{ChannelType:e.ChannelType}),void 0!==e.Demographic&&{Demographic:Jt(e.Demographic,t)}),void 0!==e.EffectiveDate&&{EffectiveDate:e.EffectiveDate}),void 0!==e.EndpointStatus&&{EndpointStatus:e.EndpointStatus}),void 0!==e.Location&&{Location:Zt(e.Location,t)}),void 0!==e.Metrics&&{Metrics:nn(e.Metrics,t)}),void 0!==e.OptOut&&{OptOut:e.OptOut}),void 0!==e.RequestId&&{RequestId:e.RequestId}),void 0!==e.User&&{User:Qt(e.User,t)})},Qt=function(e,t){return d(d({},void 0!==e.UserAttributes&&{UserAttributes:an(e.UserAttributes,t)}),void 0!==e.UserId&&{UserId:e.UserId})},en=function(e,t){return d({},void 0!==e.BatchItem&&{BatchItem:sn(e.BatchItem,t)})},tn=function(e,t){return e.map((function(e){return e}))},nn=function(e,t){return Object.entries(e).reduce((function(e,t){var n,r=v(t,2),i=r[0],o=r[1];return d(d({},e),((n={})[i]=o,n))}),{})},rn=function(e,t){return Object.entries(e).reduce((function(e,t){var n,r=v(t,2),i=r[0],o=r[1];return d(d({},e),((n={})[i]=o,n))}),{})},on=function(e,t){return Object.entries(e).reduce((function(e,n){var r,i=v(n,2),o=i[0],s=i[1];return d(d({},e),((r={})[o]=function(e,t){return d(d(d(d(d(d(d(d(d(d({},void 0!==e.AppPackageName&&{AppPackageName:e.AppPackageName}),void 0!==e.AppTitle&&{AppTitle:e.AppTitle}),void 0!==e.AppVersionCode&&{AppVersionCode:e.AppVersionCode}),void 0!==e.Attributes&&{Attributes:rn(e.Attributes,t)}),void 0!==e.ClientSdkVersion&&{ClientSdkVersion:e.ClientSdkVersion}),void 0!==e.EventType&&{EventType:e.EventType}),void 0!==e.Metrics&&{Metrics:nn(e.Metrics,t)}),void 0!==e.SdkName&&{SdkName:e.SdkName}),void 0!==e.Session&&{Session:cn(e.Session,t)}),void 0!==e.Timestamp&&{Timestamp:e.Timestamp})}(s,t),r))}),{})},sn=function(e,t){return Object.entries(e).reduce((function(e,n){var r,i=v(n,2),o=i[0],s=i[1];return d(d({},e),((r={})[o]=function(e,t){return d(d({},void 0!==e.Endpoint&&{Endpoint:un(e.Endpoint,t)}),void 0!==e.Events&&{Events:on(e.Events,t)})}(s,t),r))}),{})},an=function(e,t){return Object.entries(e).reduce((function(e,n){var r,i=v(n,2),o=i[0],s=i[1];return d(d({},e),((r={})[o]=tn(s,t),r))}),{})},un=function(e,t){return d(d(d(d(d(d(d(d(d(d(d({},void 0!==e.Address&&{Address:e.Address}),void 0!==e.Attributes&&{Attributes:an(e.Attributes,t)}),void 0!==e.ChannelType&&{ChannelType:e.ChannelType}),void 0!==e.Demographic&&{Demographic:Jt(e.Demographic,t)}),void 0!==e.EffectiveDate&&{EffectiveDate:e.EffectiveDate}),void 0!==e.EndpointStatus&&{EndpointStatus:e.EndpointStatus}),void 0!==e.Location&&{Location:Zt(e.Location,t)}),void 0!==e.Metrics&&{Metrics:nn(e.Metrics,t)}),void 0!==e.OptOut&&{OptOut:e.OptOut}),void 0!==e.RequestId&&{RequestId:e.RequestId}),void 0!==e.User&&{User:Qt(e.User,t)})},cn=function(e,t){return d(d(d(d({},void 0!==e.Duration&&{Duration:e.Duration}),void 0!==e.Id&&{Id:e.Id}),void 0!==e.StartTimestamp&&{StartTimestamp:e.StartTimestamp}),void 0!==e.StopTimestamp&&{StopTimestamp:e.StopTimestamp})},fn=function(e,t){return{Message:void 0!==e.Message&&null!==e.Message?e.Message:void 0,StatusCode:void 0!==e.StatusCode&&null!==e.StatusCode?e.StatusCode:void 0}},ln=function(e,t){return{Results:void 0!==e.Results&&null!==e.Results?hn(e.Results,t):void 0}},dn=function(e,t){return Object.entries(e).reduce((function(e,t){var n,r=v(t,2),i=r[0],o=r[1];return d(d({},e),((n={})[i]=function(e,t){return{Message:void 0!==e.Message&&null!==e.Message?e.Message:void 0,StatusCode:void 0!==e.StatusCode&&null!==e.StatusCode?e.StatusCode:void 0}}(o),n))}),{})},hn=function(e,t){return Object.entries(e).reduce((function(e,n){var r,i=v(n,2),o=i[0],s=i[1];return d(d({},e),((r={})[o]=function(e,t){return{EndpointItemResponse:void 0!==e.EndpointItemResponse&&null!==e.EndpointItemResponse?fn(e.EndpointItemResponse):void 0,EventsItemResponse:void 0!==e.EventsItemResponse&&null!==e.EventsItemResponse?dn(e.EventsItemResponse,t):void 0}}(s,t),r))}),{})},pn=function(e,t){return{Message:void 0!==e.Message&&null!==e.Message?e.Message:void 0,RequestID:void 0!==e.RequestID&&null!==e.RequestID?e.RequestID:void 0}},vn=function(e){return{httpStatusCode:e.statusCode,httpHeaders:e.headers,requestId:e.headers["x-amzn-requestid"]}},gn=function(e,t){return void 0===e&&(e=new Uint8Array),e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array)},mn=function(e,t){return function(e,t){return gn(e,t).then((function(e){return t.utf8Encoder(e)}))}(e,t).then((function(e){return e.length?JSON.parse(e):{}}))},bn=function(e,t){var n,r,i=function(e){var t=e;return t.indexOf(":")>=0&&(t=t.split(":")[0]),t.indexOf("#")>=0&&(t=t.split("#")[1]),t},o=(n=e.headers,r="x-amzn-errortype",Object.keys(n).find((function(e){return e.toLowerCase()===r.toLowerCase()})));return void 0!==o?i(e.headers[o]):void 0!==t.code?i(t.code):void 0!==t.__type?i(t.__type):""},yn=n(10),wn=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return l(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(yn.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"PinpointClient",commandName:"PutEventsCommand",inputFilterSensitiveLog:Ie.filterSensitiveLog,outputFilterSensitiveLog:ke.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"PinpointClient",commandName:"PutEventsCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return h(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f;return p(this,(function(l){switch(l.label){case 0:if(n={"Content-Type":"application/json"},r="/v1/apps/{ApplicationId}/events",void 0===e.ApplicationId)throw new Error("No value provided for input HTTP label: ApplicationId.");if((i=e.ApplicationId).length<=0)throw new Error("Empty value provided for input HTTP label: ApplicationId.");return r=r.replace("{ApplicationId}",Object(Ft.f)(i)),void 0!==e.EventsRequest&&(o=en(e.EventsRequest,t)),void 0===o&&(o={}),o=JSON.stringify(o),[4,t.endpoint()];case 1:return s=l.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,f=s.port,[2,new Bt.a({protocol:c,hostname:a,port:f,method:"POST",headers:n,path:r,body:o})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return h(void 0,void 0,void 0,(function(){var n,r;return p(this,(function(i){switch(i.label){case 0:return 202!==e.statusCode&&e.statusCode>=300?[2,zt(e,t)]:(n={$metadata:vn(e),EventsResponse:void 0},[4,mn(e.body,t)]);case 1:return r=i.sent(),n.EventsResponse=ln(r,t),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Ft.b),_n=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return l(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(yn.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"PinpointClient",commandName:"UpdateEndpointCommand",inputFilterSensitiveLog:ct.filterSensitiveLog,outputFilterSensitiveLog:ft.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"PinpointClient",commandName:"UpdateEndpointCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return h(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f;return p(this,(function(l){switch(l.label){case 0:if(n={"Content-Type":"application/json"},r="/v1/apps/{ApplicationId}/endpoints/{EndpointId}",void 0===e.ApplicationId)throw new Error("No value provided for input HTTP label: ApplicationId.");if((i=e.ApplicationId).length<=0)throw new Error("Empty value provided for input HTTP label: ApplicationId.");if(r=r.replace("{ApplicationId}",Object(Ft.f)(i)),void 0===e.EndpointId)throw new Error("No value provided for input HTTP label: EndpointId.");if((i=e.EndpointId).length<=0)throw new Error("Empty value provided for input HTTP label: EndpointId.");return r=r.replace("{EndpointId}",Object(Ft.f)(i)),void 0!==e.EndpointRequest&&(o=Xt(e.EndpointRequest,t)),void 0===o&&(o={}),o=JSON.stringify(o),[4,t.endpoint()];case 1:return s=l.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,f=s.port,[2,new Bt.a({protocol:c,hostname:a,port:f,method:"PUT",headers:n,path:r,body:o})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return h(void 0,void 0,void 0,(function(){var n,r;return p(this,(function(i){switch(i.label){case 0:return 202!==e.statusCode&&e.statusCode>=300?[2,qt(e,t)]:(n={$metadata:vn(e),MessageBody:void 0},[4,mn(e.body,t)]);case 1:return r=i.sent(),n.MessageBody=pn(r,t),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(Ft.b),Sn=n(149),En=n(38),Mn=n(18),An=n(24),In=n(11),kn=n(39),On=n(17),xn=n(40),Cn=n(41),Tn=n(15),Pn=new Set(["ap-east-1","ap-northeast-1","ap-northeast-2","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-north-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-west-1","us-west-2"]),Nn=new Set(["cn-north-1","cn-northwest-1"]),Rn=new Set(["us-iso-east-1"]),Ln=new Set(["us-isob-east-1"]),jn=new Set(["us-gov-east-1","us-gov-west-1"]),Dn=d(d({},{apiVersion:"2016-12-01",disableHostPrefix:!1,logger:{},regionInfoProvider:function(e,t){var n=void 0;switch(e){case"ap-south-1":n={hostname:"pinpoint.ap-south-1.amazonaws.com",partition:"aws",signingService:"mobiletargeting"};break;case"ap-southeast-2":n={hostname:"pinpoint.ap-southeast-2.amazonaws.com",partition:"aws",signingService:"mobiletargeting"};break;case"eu-central-1":n={hostname:"pinpoint.eu-central-1.amazonaws.com",partition:"aws",signingService:"mobiletargeting"};break;case"eu-west-1":n={hostname:"pinpoint.eu-west-1.amazonaws.com",partition:"aws",signingService:"mobiletargeting"};break;case"us-east-1":n={hostname:"pinpoint.us-east-1.amazonaws.com",partition:"aws",signingService:"mobiletargeting"};break;case"us-west-2":n={hostname:"pinpoint.us-west-2.amazonaws.com",partition:"aws",signingService:"mobiletargeting"};break;default:Pn.has(e)&&(n={hostname:"pinpoint.{region}.amazonaws.com".replace("{region}",e),partition:"aws",signingService:"mobiletargeting"}),Nn.has(e)&&(n={hostname:"pinpoint.{region}.amazonaws.com.cn".replace("{region}",e),partition:"aws-cn"}),Rn.has(e)&&(n={hostname:"pinpoint.{region}.c2s.ic.gov".replace("{region}",e),partition:"aws-iso"}),Ln.has(e)&&(n={hostname:"pinpoint.{region}.sc2s.sgov.gov".replace("{region}",e),partition:"aws-iso-b"}),jn.has(e)&&(n={hostname:"pinpoint.{region}.amazonaws.com".replace("{region}",e),partition:"aws-us-gov"}),void 0===n&&(n={hostname:"pinpoint.{region}.amazonaws.com".replace("{region}",e),partition:"aws",signingService:"mobiletargeting"})}return Promise.resolve(n)},signingName:"mobiletargeting"}),{runtime:"browser",base64Decoder:On.a,base64Encoder:On.b,bodyLengthChecker:xn.a,credentialDefaultProvider:Object(An.a)("Credential is missing"),defaultUserAgent:Object(Cn.a)(Sn.name,Sn.version),maxAttempts:In.a,region:Object(An.a)("Region is missing"),requestHandler:new Mn.a,sha256:En.Sha256,streamCollector:Mn.b,urlParser:kn.a,utf8Decoder:Tn.a,utf8Encoder:Tn.b}),Un=n(22),Bn=n(37),Fn=n(21),zn=n(43),qn=n(25),Kn=n(23),Hn=function(e){function t(t){var n=this,r=d(d({},Dn),t),i=Object(Un.b)(r),o=Object(Un.a)(i),s=Object(qn.b)(o),a=Object(In.c)(s),u=Object(Kn.b)(a),c=Object(Fn.b)(u);return(n=e.call(this,c)||this).config=c,n.middlewareStack.use(Object(qn.a)(n.config)),n.middlewareStack.use(Object(In.b)(n.config)),n.middlewareStack.use(Object(Kn.a)(n.config)),n.middlewareStack.use(Object(Bn.a)(n.config)),n.middlewareStack.use(Object(Fn.a)(n.config)),n.middlewareStack.use(Object(zn.a)(n.config)),n}return l(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this)},t}(Ft.a),Vn=n(26),Gn=n(27),Wn=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},$n=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},Yn=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},Jn=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(Yn(arguments[t]));return e},Zn=new i.a("EventsBuffer"),Xn=[429,500],Qn=[202],er=function(){function e(e,t){this._pause=!1,this._flush=!1,Zn.debug("Instantiating buffer with config:",t),this._buffer=[],this._client=e,this._config=t,this._sendBatch=this._sendBatch.bind(this),this._startLoop()}return e.prototype.push=function(e){var t;if(this._buffer>this._config.bufferSize)return Zn.debug("Exceeded analytics events buffer size"),e.handlers.reject(new Error("Exceeded the size of analytics events buffer"));var n=((t={})[e.params.event.eventId]=e,t);this._buffer.push(n)},e.prototype.pause=function(){this._pause=!0},e.prototype.resume=function(){this._pause=!1},e.prototype.updateClient=function(e){this._client=e},e.prototype.flush=function(){this._flush=!0},e.prototype._startLoop=function(){this._interval&&clearInterval(this._interval);var e=this._config.flushInterval;this._interval=setInterval(this._sendBatch,e)},e.prototype._sendBatch=function(){var e=this._buffer.length;if(this._flush&&!e&&clearInterval(this._interval),!this._pause&&e){var t=this._config.flushSize,n=Math.min(t,e),r=this._buffer.splice(0,n);this._putEvents(r)}},e.prototype._putEvents=function(e){return Wn(this,void 0,void 0,(function(){var t,n,r,i,o;return $n(this,(function(s){switch(s.label){case 0:t=this._bufferToMap(e),n=this._generateBatchEventParams(t),s.label=1;case 1:return s.trys.push([1,3,,4]),r=new wn(n),[4,this._client.send(r)];case 2:return i=s.sent(),this._processPutEventsSuccessResponse(i,t),[3,4];case 3:return o=s.sent(),[2,this._handlePutEventsFailure(o,t)];case 4:return[2]}}))}))},e.prototype._generateBatchEventParams=function(e){var t={ApplicationId:"",EventsRequest:{BatchItem:{}}};return Object.values(e).forEach((function(e){var n=e.params,r=n.event,i=n.timestamp,o=n.config,s=r.name,a=r.attributes,u=r.metrics,c=r.eventId,f=r.session,l=o.appId,d=o.endpointId,h=t.EventsRequest.BatchItem;t.ApplicationId=t.ApplicationId||l,h[d]||(h[d]={Endpoint:{},Events:{}}),h[d].Events[c]={EventType:s,Timestamp:new Date(i).toISOString(),Attributes:a,Metrics:u,Session:f}})),t},e.prototype._handlePutEventsFailure=function(e,t){Zn.debug("_putEvents Failed: ",e);var n=e.$metadata&&e.$metadata.httpStatusCode;if(Xn.includes(n)){var r=Object.values(t);this._retry(r)}else;},e.prototype._processPutEventsSuccessResponse=function(e,t){var n=e.EventsResponse.Results,r=[];Object.entries(n).forEach((function(e){var n=Yn(e,2),i=n[0],o=n[1].EventsItemResponse;Object.entries(o).forEach((function(e){var n,o,s=Yn(e,2),a=s[0],u=s[1],c=u.StatusCode,f=u.Message,l=t[a],d={EventsResponse:{Results:(n={},n[i]={EventsItemResponse:(o={},o[a]={StatusCode:c,Message:f},o)},n)}};if(Qn.includes(c))l.handlers.resolve(d);else{if(!Xn.includes(c)){var h=l.params.event.name;return Zn.error("event "+a+" : "+h+" failed with error: "+f),l.handlers.reject(d)}r.push(l)}}))})),r.length&&this._retry(r)},e.prototype._retry=function(e){var t,n=[];e.forEach((function(e){var t,r=e.params,i=r.event,o=i.eventId,s=i.name;if(r.resendLimit-- >0)return Zn.debug("resending event "+o+" : "+s+" with "+r.resendLimit+" retry attempts remaining"),void n.push((t={},t[o]=e,t));Zn.debug("no retry attempts remaining for event "+o+" : "+s)})),(t=this._buffer).unshift.apply(t,Jn(n))},e.prototype._bufferToMap=function(e){return e.reduce((function(e,t){var n=Yn(Object.entries(t),1),r=Yn(n[0],2),i=r[0],o=r[1];return e[i]=o,e}),{})},e}();function tr(e){return(tr="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)}var nr=function(){return(nr=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},rr=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},ir=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},or=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},sr="undefined"!=typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("amplify_default"):"@@amplify_default",ar=function(e,t){r.a.dispatch("analytics",{event:e,data:t},"Analytics",sr)},ur=new i.a("AWSPinpointProvider"),cr=[429,500],fr=[202],lr="undefined"!=typeof navigator&&navigator&&"function"==typeof navigator.sendBeacon,dr=function(){function e(e){this._endpointGenerating=!0,this._endpointUpdateInProgress=!1,this._buffer=null,this._endpointBuffer=[],this._config=e||{},this._config.bufferSize=this._config.bufferSize||1e3,this._config.flushSize=this._config.flushSize||100,this._config.flushInterval=this._config.flushInterval||5e3,this._config.resendLimit=this._config.resendLimit||5,this._clientInfo=o.a.clientInfo()}return e.prototype.getCategory=function(){return e.category},e.prototype.getProviderName=function(){return e.providerName},e.prototype.configure=function(e){var t=this;ur.debug("configure Analytics",e);var n=e||{};if(this._config=Object.assign({},this._config,n),this._endpointGenerating=!!e.autoSessionRecord,this._config.appId&&!this._config.disabled)if(this._config.endpointId)ar("pinpointProvider_configured",null);else{var r=this.getProviderName()+"_"+this._config.appId;this._getEndpointId(r).then((function(e){ur.debug("setting endpoint id from the cache",e),t._config.endpointId=e,ar("pinpointProvider_configured",null)})).catch((function(e){ur.debug("Failed to generate endpointId",e)}))}else this._flushBuffer();return this._config},e.prototype.record=function(e,t){return rr(this,void 0,void 0,(function(){var n,r;return ir(this,(function(i){switch(i.label){case 0:return ur.debug("_public record",e),[4,this._getCredentials()];case 1:return(n=i.sent())&&this._config.appId&&this._config.region?(this._initClients(n),r=(new Date).getTime(),this._generateSession(e),e.event.eventId=Object(Gn.v1)(),Object.assign(e,{timestamp:r,config:this._config}),e.event.immediate?[2,this._send(e,t)]:(this._putToBuffer(e,t),[2])):(ur.debug("cannot send events without credentials, applicationId or region"),[2,t.reject(new Error("No credentials, applicationId or region"))])}}))}))},e.prototype._sendEndpointUpdate=function(e){return rr(this,void 0,void 0,(function(){var t;return ir(this,(function(n){switch(n.label){case 0:return this._endpointUpdateInProgress?(this._endpointBuffer.push(e),[2]):(this._endpointUpdateInProgress=!0,[4,this._updateEndpoint(e)]);case 1:return n.sent(),t=this._endpointBuffer.shift(),this._endpointUpdateInProgress=!1,t&&this._sendEndpointUpdate(t),[2]}}))}))},e.prototype._putToBuffer=function(e,t){"_update_endpoint"!==e.event.name?this._buffer&&this._buffer.push({params:e,handlers:t}):this._sendEndpointUpdate({params:e,handlers:t})},e.prototype._generateSession=function(e){this._sessionId=this._sessionId||Object(Gn.v1)();var t=e.event;switch(t.name){case"_session.start":this._sessionStartTimestamp=(new Date).getTime(),this._sessionId=Object(Gn.v1)(),t.session={Id:this._sessionId,StartTimestamp:new Date(this._sessionStartTimestamp).toISOString()};break;case"_session.stop":var n=(new Date).getTime();this._sessionStartTimestamp=this._sessionStartTimestamp||(new Date).getTime(),this._sessionId=this._sessionId||Object(Gn.v1)(),t.session={Id:this._sessionId,Duration:n-this._sessionStartTimestamp,StartTimestamp:new Date(this._sessionStartTimestamp).toISOString(),StopTimestamp:new Date(n).toISOString()},this._sessionId=void 0,this._sessionStartTimestamp=void 0;break;default:this._sessionStartTimestamp=this._sessionStartTimestamp||(new Date).getTime(),this._sessionId=this._sessionId||Object(Gn.v1)(),t.session={Id:this._sessionId,StartTimestamp:new Date(this._sessionStartTimestamp).toISOString()}}},e.prototype._send=function(e,t){return rr(this,void 0,void 0,(function(){return ir(this,(function(n){switch(e.event.name){case"_update_endpoint":return[2,this._updateEndpoint({params:e,handlers:t})];case"_session.stop":return[2,this._pinpointSendStopSession(e,t)];default:return[2,this._pinpointPutEvents(e,t)]}return[2]}))}))},e.prototype._generateBatchItemContext=function(e){var t,n=e.event,r=e.timestamp,i=e.config,o=n.name,s=n.attributes,a=n.metrics,u=n.eventId,c=n.session,f=i.appId,l=i.endpointId,d={ApplicationId:f,EventsRequest:{BatchItem:{}}},h={Endpoint:{}};return h.Events=((t={})[u]={EventType:o,Timestamp:new Date(r).toISOString(),Attributes:s,Metrics:a,Session:c},t),d.EventsRequest.BatchItem[l]=h,d},e.prototype._pinpointPutEvents=function(e,t){return rr(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d;return ir(this,(function(h){switch(h.label){case 0:n=e.event.eventId,r=e.config.endpointId,i=this._generateBatchItemContext(e),o=new wn(i),h.label=1;case 1:return h.trys.push([1,3,,4]),[4,this.pinpointClient.send(o)];case 2:return s=h.sent(),a=r,u=n,c=s.EventsResponse.Results[a].EventsItemResponse[u],f=c.StatusCode,l=c.Message,fr.includes(f)?(ur.debug("record event success. ",s),[2,t.resolve(s)]):cr.includes(f)?(this._retry(e,t),[3,4]):(ur.error("Event "+n+" is not accepted, the error is "+l),[2,t.reject(s)]);case 3:return d=h.sent(),this._eventError(d),[2,t.reject(d)];case 4:return[2]}}))}))},e.prototype._pinpointSendStopSession=function(e,t){if(lr){var n=this._generateBatchItemContext(e),r=this._config.region,i=n.ApplicationId,o=n.EventsRequest,a={secret_key:this._config.credentials.secretAccessKey,access_key:this._config.credentials.accessKeyId,session_token:this._config.credentials.sessionToken},u="https://pinpoint."+r+".amazonaws.com/v1/apps/"+i+"/events/legacy",c=JSON.stringify(o),f={url:u,body:c,method:"POST"},l={region:r,service:"mobiletargeting"},d=s.a.signUrl(f,a,l,null);return navigator.sendBeacon(d,c)?t.resolve("sendBeacon success"):t.reject("sendBeacon failure")}this._pinpointPutEvents(e,t)},e.prototype._retry=function(e,t){var n=e.config.resendLimit;e.resendLimit="number"==typeof e.resendLimit?e.resendLimit:n,e.resendLimit-- >0?(ur.debug("resending event "+e.eventName+" with "+e.resendLimit+" retry times left"),this._pinpointPutEvents(e,t)):ur.debug("retry times used up for event "+e.eventName)},e.prototype._updateEndpoint=function(e){return rr(this,void 0,void 0,(function(){var t,n,r,i,o,s,u,c,f,l,d,h;return ir(this,(function(p){switch(p.label){case 0:t=e.params,n=e.handlers,r=t.config,i=t.event,o=r.appId,s=r.endpointId,u=this._endpointRequest(r,a.a.transferKeyToLowerCase(i,[],["attributes","userAttributes","Attributes","UserAttributes"])),c={ApplicationId:o,EndpointId:s,EndpointRequest:u},p.label=1;case 1:return p.trys.push([1,3,,4]),f=new _n(c),[4,this.pinpointClient.send(f)];case 2:return l=p.sent(),ur.debug("updateEndpoint success",l),this._endpointGenerating=!1,this._resumeBuffer(),n.resolve(l),[2];case 3:return d=p.sent(),h={err:d,update_params:c,endpointObject:e},[2,this._handleEndpointUpdateFailure(h)];case 4:return[2]}}))}))},e.prototype._handleEndpointUpdateFailure=function(e){return rr(this,void 0,void 0,(function(){var t,n,r;return ir(this,(function(i){switch(t=e.err,n=e.endpointObject,r=t.$metadata&&t.$metadata.httpStatusCode,ur.debug("updateEndpoint error",t),r){case 403:return[2,this._handleEndpointUpdateForbidden(e)];default:if(cr.includes(r))return!0,[2,this._retryEndpointUpdate(n,!0)];ur.error("updateEndpoint failed",t),n.handlers.reject(t)}return[2]}))}))},e.prototype._handleEndpointUpdateForbidden=function(e){var t=e.err,n=e.endpointObject,r=t.code,i=t.retryable;if("ExpiredTokenException"!==r&&!i)return n.handlers.reject(t);this._retryEndpointUpdate(n)},e.prototype._retryEndpointUpdate=function(e,t){void 0===t&&(t=!1),ur.debug("_retryEndpointUpdate",e);var n=e.params,r=n.config.resendLimit;if(n.resendLimit="number"==typeof n.resendLimit?n.resendLimit:r,n.resendLimit-- >0)return ur.debug("resending endpoint update "+n.event.eventId+" with "+n.resendLimit+" retry attempts remaining"),void(this._endpointBuffer.length?this._endpointBuffer.unshift(e):this._updateEndpoint(e));ur.warn("resending endpoint update "+n.event.eventId+" failed after "+n.config.resendLimit+" attempts"),this._endpointGenerating&&ur.error("Initial endpoint update failed. ")},e.prototype._initClients=function(e){return rr(this,void 0,void 0,(function(){var t,n;return ir(this,(function(r){return ur.debug("init clients"),this.pinpointClient&&this._config.credentials&&this._config.credentials.sessionToken===e.sessionToken&&this._config.credentials.identityId===e.identityId?(ur.debug("no change for aws credentials, directly return from init"),[2]):(t=this._config.credentials?this._config.credentials.identityId:null,this._config.credentials=e,n=this._config.region,ur.debug("init clients with credentials",e),this.pinpointClient=new Hn({region:n,credentials:e,customUserAgent:Object(u.b)()}),this.pinpointClient.middlewareStack.addRelativeTo((function(e){return function(t){return delete t.request.headers["amz-sdk-invocation-id"],delete t.request.headers["amz-sdk-request"],e(t)}}),{step:"finalizeRequest",relation:"after",toMiddleware:"retryMiddleware"}),this._bufferExists()&&t===e.identityId?this._updateBufferClient():this._initBuffer(),this._customizePinpointClientReq(),[2])}))}))},e.prototype._bufferExists=function(){return this._buffer&&this._buffer instanceof er},e.prototype._initBuffer=function(){this._bufferExists()&&this._flushBuffer(),this._buffer=new er(this.pinpointClient,this._config),this._endpointGenerating&&this._buffer.pause()},e.prototype._updateBufferClient=function(){this._bufferExists()&&this._buffer.updateClient(this.pinpointClient)},e.prototype._flushBuffer=function(){this._bufferExists()&&(this._buffer.flush(),this._buffer=null)},e.prototype._resumeBuffer=function(){this._bufferExists()&&this._buffer.resume()},e.prototype._customizePinpointClientReq=function(){},e.prototype._getEndpointId=function(e){return rr(this,void 0,void 0,(function(){var t;return ir(this,(function(n){switch(n.label){case 0:return[4,Vn.a.getItem(e)];case 1:return t=n.sent(),ur.debug("endpointId from cache",t,"type",tr(t)),t||(t=Object(Gn.v1)(),Vn.a.setItem(e,t)),[2,t]}}))}))},e.prototype._endpointRequest=function(e,t){var n=e.credentials,r=this._clientInfo||{},i=e.clientContext||{},o=e.endpoint||{},s={appVersion:r.appVersion,make:r.make,model:r.model,modelVersion:r.version,platform:r.platform},u=(i.clientId,i.appTitle,i.appVersionName,i.appVersionCode,i.appPackageName,or(i,["clientId","appTitle","appVersionName","appVersionCode","appPackageName"])),c=t.address?"android"===r.platform?"GCM":"APNS":void 0,f=nr(nr(nr({channelType:c,requestId:Object(Gn.v1)(),effectiveDate:(new Date).toISOString()},o),t),{attributes:nr(nr({},o.attributes),t.attributes),demographic:nr(nr(nr(nr({},s),u),o.demographic),t.demographic),location:nr(nr({},o.location),t.location),metrics:nr(nr({},o.metrics),t.metrics),user:{userId:t.userId||o.userId||n.identityId,userAttributes:nr(nr({},o.userAttributes),t.userAttributes)}}),l=(f.userId,f.userAttributes,f.name,f.session,f.eventId,f.immediate,or(f,["userId","userAttributes","name","session","eventId","immediate"]));return a.a.transferKeyToUpperCase(l,[],["metrics","userAttributes","attributes"])},e.prototype._eventError=function(e){ur.error("record event failed.",e),ur.warn('Please ensure you have updated your Pinpoint IAM Policy with the Action: "mobiletargeting:PutEvents" in order to record events')},e.prototype._getCredentials=function(){return rr(this,void 0,void 0,(function(){var e,t;return ir(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,c.a.get()];case 1:return(e=n.sent())?(ur.debug("set credentials for analytics",e),[2,c.a.shear(e)]):[2,null];case 2:return t=n.sent(),ur.debug("ensure credentials error",t),[2,null];case 3:return[2]}}))}))},e.category="Analytics",e.providerName="AWSPinpoint",e}()},function(e,t,n){"use strict";n.d(t,"a",(function(){return Ht}));var r=n(44),i=n(50),o=n(89),s=function(e,t){return(s=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}s(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function c(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function f(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}Object.create;var l,d,h,p,v,g,m,b,y,w,_,S,E,M,A,I,k,O,x,C,T,P,N,R,L,j,D,U,B,F,z,q,K,H,V,G,W,$,Y,J,Z,X,Q,ee,te,ne,re,ie,oe,se,ae,ue,ce,fe,le,de,he,pe,ve,ge,me,be,ye,we,_e,Se,Ee,Me,Ae,Ie,ke,Oe,xe,Ce,Te,Pe,Ne,Re,Le,je,De,Ue;Object.create;(l||(l={})).filterSensitiveLog=function(e){return u({},e)},(d||(d={})).filterSensitiveLog=function(e){return u({},e)},(h||(h={})).filterSensitiveLog=function(e){return u({},e)},(p||(p={})).filterSensitiveLog=function(e){return u({},e)},(v||(v={})).filterSensitiveLog=function(e){return u({},e)},(g||(g={})).filterSensitiveLog=function(e){return u({},e)},(m||(m={})).filterSensitiveLog=function(e){return u({},e)},function(e){e.ACTIVE="ACTIVE",e.CREATING="CREATING",e.DELETING="DELETING"}(b||(b={})),(y||(y={})).filterSensitiveLog=function(e){return u({},e)},(w||(w={})).filterSensitiveLog=function(e){return u({},e)},(_||(_={})).filterSensitiveLog=function(e){return u({},e)},(S||(S={})).filterSensitiveLog=function(e){return u({},e)},(E||(E={})).filterSensitiveLog=function(e){return u({},e)},(M||(M={})).filterSensitiveLog=function(e){return u({},e)},(A||(A={})).filterSensitiveLog=function(e){return u({},e)},(I||(I={})).filterSensitiveLog=function(e){return u({},e)},(k||(k={})).filterSensitiveLog=function(e){return u({},e)},function(e){e.KMS="KMS",e.NONE="NONE"}(O||(O={})),function(e){e.ALL="ALL",e.INCOMING_BYTES="IncomingBytes",e.INCOMING_RECORDS="IncomingRecords",e.ITERATOR_AGE_MILLISECONDS="IteratorAgeMilliseconds",e.OUTGOING_BYTES="OutgoingBytes",e.OUTGOING_RECORDS="OutgoingRecords",e.READ_PROVISIONED_THROUGHPUT_EXCEEDED="ReadProvisionedThroughputExceeded",e.WRITE_PROVISIONED_THROUGHPUT_EXCEEDED="WriteProvisionedThroughputExceeded"}(x||(x={})),(C||(C={})).filterSensitiveLog=function(e){return u({},e)},(T||(T={})).filterSensitiveLog=function(e){return u({},e)},(P||(P={})).filterSensitiveLog=function(e){return u({},e)},function(e){e.ACTIVE="ACTIVE",e.CREATING="CREATING",e.DELETING="DELETING",e.UPDATING="UPDATING"}(N||(N={})),(R||(R={})).filterSensitiveLog=function(e){return u({},e)},(L||(L={})).filterSensitiveLog=function(e){return u({},e)},(j||(j={})).filterSensitiveLog=function(e){return u({},e)},(D||(D={})).filterSensitiveLog=function(e){return u({},e)},(U||(U={})).filterSensitiveLog=function(e){return u({},e)},(B||(B={})).filterSensitiveLog=function(e){return u({},e)},(F||(F={})).filterSensitiveLog=function(e){return u({},e)},(z||(z={})).filterSensitiveLog=function(e){return u({},e)},(q||(q={})).filterSensitiveLog=function(e){return u({},e)},(K||(K={})).filterSensitiveLog=function(e){return u({},e)},(H||(H={})).filterSensitiveLog=function(e){return u({},e)},(V||(V={})).filterSensitiveLog=function(e){return u({},e)},(G||(G={})).filterSensitiveLog=function(e){return u({},e)},(W||(W={})).filterSensitiveLog=function(e){return u({},e)},($||($={})).filterSensitiveLog=function(e){return u({},e)},(Y||(Y={})).filterSensitiveLog=function(e){return u({},e)},(J||(J={})).filterSensitiveLog=function(e){return u({},e)},(Z||(Z={})).filterSensitiveLog=function(e){return u({},e)},(X||(X={})).filterSensitiveLog=function(e){return u({},e)},(Q||(Q={})).filterSensitiveLog=function(e){return u({},e)},(ee||(ee={})).filterSensitiveLog=function(e){return u({},e)},(te||(te={})).filterSensitiveLog=function(e){return u({},e)},function(e){e.AFTER_SEQUENCE_NUMBER="AFTER_SEQUENCE_NUMBER",e.AT_SEQUENCE_NUMBER="AT_SEQUENCE_NUMBER",e.AT_TIMESTAMP="AT_TIMESTAMP",e.LATEST="LATEST",e.TRIM_HORIZON="TRIM_HORIZON"}(ne||(ne={})),(re||(re={})).filterSensitiveLog=function(e){return u({},e)},(ie||(ie={})).filterSensitiveLog=function(e){return u({},e)},(oe||(oe={})).filterSensitiveLog=function(e){return u({},e)},(se||(se={})).filterSensitiveLog=function(e){return u({},e)},function(e){e.AFTER_SHARD_ID="AFTER_SHARD_ID",e.AT_LATEST="AT_LATEST",e.AT_TIMESTAMP="AT_TIMESTAMP",e.AT_TRIM_HORIZON="AT_TRIM_HORIZON",e.FROM_TIMESTAMP="FROM_TIMESTAMP",e.FROM_TRIM_HORIZON="FROM_TRIM_HORIZON"}(ae||(ae={})),(ue||(ue={})).filterSensitiveLog=function(e){return u({},e)},(ce||(ce={})).filterSensitiveLog=function(e){return u({},e)},(fe||(fe={})).filterSensitiveLog=function(e){return u({},e)},(le||(le={})).filterSensitiveLog=function(e){return u({},e)},(de||(de={})).filterSensitiveLog=function(e){return u({},e)},(he||(he={})).filterSensitiveLog=function(e){return u({},e)},(pe||(pe={})).filterSensitiveLog=function(e){return u({},e)},(ve||(ve={})).filterSensitiveLog=function(e){return u({},e)},(ge||(ge={})).filterSensitiveLog=function(e){return u({},e)},(me||(me={})).filterSensitiveLog=function(e){return u({},e)},(be||(be={})).filterSensitiveLog=function(e){return u({},e)},(ye||(ye={})).filterSensitiveLog=function(e){return u({},e)},(we||(we={})).filterSensitiveLog=function(e){return u({},e)},(_e||(_e={})).filterSensitiveLog=function(e){return u({},e)},(Se||(Se={})).filterSensitiveLog=function(e){return u({},e)},(Ee||(Ee={})).filterSensitiveLog=function(e){return u({},e)},(Me||(Me={})).filterSensitiveLog=function(e){return u({},e)},(Ae||(Ae={})).filterSensitiveLog=function(e){return u({},e)},(Ie||(Ie={})).filterSensitiveLog=function(e){return u({},e)},(ke||(ke={})).filterSensitiveLog=function(e){return u({},e)},(Oe||(Oe={})).filterSensitiveLog=function(e){return u({},e)},(xe||(xe={})).filterSensitiveLog=function(e){return u({},e)},(Ce||(Ce={})).filterSensitiveLog=function(e){return u({},e)},(Te||(Te={})).filterSensitiveLog=function(e){return u({},e)},(Pe||(Pe={})).filterSensitiveLog=function(e){return u({},e)},(Ne||(Ne={})).filterSensitiveLog=function(e){return u({},e)},function(e){e.visit=function(e,t){return void 0!==e.KMSThrottlingException?t.KMSThrottlingException(e.KMSThrottlingException):void 0!==e.InternalFailureException?t.InternalFailureException(e.InternalFailureException):void 0!==e.ResourceInUseException?t.ResourceInUseException(e.ResourceInUseException):void 0!==e.KMSOptInRequired?t.KMSOptInRequired(e.KMSOptInRequired):void 0!==e.KMSDisabledException?t.KMSDisabledException(e.KMSDisabledException):void 0!==e.KMSAccessDeniedException?t.KMSAccessDeniedException(e.KMSAccessDeniedException):void 0!==e.KMSInvalidStateException?t.KMSInvalidStateException(e.KMSInvalidStateException):void 0!==e.KMSNotFoundException?t.KMSNotFoundException(e.KMSNotFoundException):void 0!==e.ResourceNotFoundException?t.ResourceNotFoundException(e.ResourceNotFoundException):void 0!==e.SubscribeToShardEvent?t.SubscribeToShardEvent(e.SubscribeToShardEvent):t._(e.$unknown[0],e.$unknown[1])},e.filterSensitiveLog=function(e){var t;return void 0!==e.KMSThrottlingException?{KMSThrottlingException:ee.filterSensitiveLog(e.KMSThrottlingException)}:void 0!==e.InternalFailureException?{InternalFailureException:se.filterSensitiveLog(e.InternalFailureException)}:void 0!==e.ResourceInUseException?{ResourceInUseException:p.filterSensitiveLog(e.ResourceInUseException)}:void 0!==e.KMSOptInRequired?{KMSOptInRequired:Q.filterSensitiveLog(e.KMSOptInRequired)}:void 0!==e.KMSDisabledException?{KMSDisabledException:J.filterSensitiveLog(e.KMSDisabledException)}:void 0!==e.KMSAccessDeniedException?{KMSAccessDeniedException:Y.filterSensitiveLog(e.KMSAccessDeniedException)}:void 0!==e.KMSInvalidStateException?{KMSInvalidStateException:Z.filterSensitiveLog(e.KMSInvalidStateException)}:void 0!==e.KMSNotFoundException?{KMSNotFoundException:X.filterSensitiveLog(e.KMSNotFoundException)}:void 0!==e.ResourceNotFoundException?{ResourceNotFoundException:v.filterSensitiveLog(e.ResourceNotFoundException)}:void 0!==e.SubscribeToShardEvent?{SubscribeToShardEvent:Ne.filterSensitiveLog(e.SubscribeToShardEvent)}:void 0!==e.$unknown?((t={})[e.$unknown[0]]="UNKNOWN",t):void 0}}(Re||(Re={})),(Le||(Le={})).filterSensitiveLog=function(e){return u(u({},e),e.EventStream&&{EventStream:"STREAMING_CONTENT"})},function(e){e.UNIFORM_SCALING="UNIFORM_SCALING"}(je||(je={})),(De||(De={})).filterSensitiveLog=function(e){return u({},e)},(Ue||(Ue={})).filterSensitiveLog=function(e){return u({},e)};var Be=n(2),Fe=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,c,l,d,h,p,v,g,m,b,y,w;return f(this,(function(f){switch(f.label){case 0:return r=[u({},e)],w={},[4,dt(e.body,t)];case 1:switch(n=u.apply(void 0,r.concat([(w.body=f.sent(),w)])),o="UnknownError",s=n.body.__type.split("#"),o=void 0===s[1]?s[0]:s[1],o){case"InvalidArgumentException":case"com.amazonaws.kinesis#InvalidArgumentException":return[3,2];case"KMSAccessDeniedException":case"com.amazonaws.kinesis#KMSAccessDeniedException":return[3,4];case"KMSDisabledException":case"com.amazonaws.kinesis#KMSDisabledException":return[3,6];case"KMSInvalidStateException":case"com.amazonaws.kinesis#KMSInvalidStateException":return[3,8];case"KMSNotFoundException":case"com.amazonaws.kinesis#KMSNotFoundException":return[3,10];case"KMSOptInRequired":case"com.amazonaws.kinesis#KMSOptInRequired":return[3,12];case"KMSThrottlingException":case"com.amazonaws.kinesis#KMSThrottlingException":return[3,14];case"ProvisionedThroughputExceededException":case"com.amazonaws.kinesis#ProvisionedThroughputExceededException":return[3,16];case"ResourceNotFoundException":case"com.amazonaws.kinesis#ResourceNotFoundException":return[3,18]}return[3,20];case 2:return a=[{}],[4,ze(n,t)];case 3:return i=u.apply(void 0,[u.apply(void 0,a.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 4:return c=[{}],[4,qe(n,t)];case 5:return i=u.apply(void 0,[u.apply(void 0,c.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 6:return l=[{}],[4,Ke(n,t)];case 7:return i=u.apply(void 0,[u.apply(void 0,l.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 8:return d=[{}],[4,He(n,t)];case 9:return i=u.apply(void 0,[u.apply(void 0,d.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 10:return h=[{}],[4,Ve(n,t)];case 11:return i=u.apply(void 0,[u.apply(void 0,h.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 12:return p=[{}],[4,Ge(n,t)];case 13:return i=u.apply(void 0,[u.apply(void 0,p.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 14:return v=[{}],[4,We(n,t)];case 15:return i=u.apply(void 0,[u.apply(void 0,v.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 16:return g=[{}],[4,$e(n,t)];case 17:return i=u.apply(void 0,[u.apply(void 0,g.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 18:return m=[{}],[4,Ye(n,t)];case 19:return i=u.apply(void 0,[u.apply(void 0,m.concat([f.sent()])),{name:o,$metadata:ct(e)}]),[3,21];case 20:b=n.body,o=b.code||b.Code||o,i=u(u({},b),{name:""+o,message:b.message||b.Message||o,$fault:"client",$metadata:ct(e)}),f.label=21;case 21:return y=i.message||i.Message||o,i.message=y,delete i.Message,[2,Promise.reject(Object.assign(new Error(y),i))]}}))}))},ze=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=Xe(n,t),[2,u({name:"InvalidArgumentException",$fault:"client",$metadata:ct(e)},r)]}))}))},qe=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=Qe(n,t),[2,u({name:"KMSAccessDeniedException",$fault:"client",$metadata:ct(e)},r)]}))}))},Ke=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=et(n,t),[2,u({name:"KMSDisabledException",$fault:"client",$metadata:ct(e)},r)]}))}))},He=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=tt(n,t),[2,u({name:"KMSInvalidStateException",$fault:"client",$metadata:ct(e)},r)]}))}))},Ve=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=nt(n,t),[2,u({name:"KMSNotFoundException",$fault:"client",$metadata:ct(e)},r)]}))}))},Ge=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=rt(n,t),[2,u({name:"KMSOptInRequired",$fault:"client",$metadata:ct(e)},r)]}))}))},We=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=it(n,t),[2,u({name:"KMSThrottlingException",$fault:"client",$metadata:ct(e)},r)]}))}))},$e=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=ot(n,t),[2,u({name:"ProvisionedThroughputExceededException",$fault:"client",$metadata:ct(e)},r)]}))}))},Ye=function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n=e.body,r=ut(n,t),[2,u({name:"ResourceNotFoundException",$fault:"client",$metadata:ct(e)},r)]}))}))},Je=function(e,t){return u(u({},void 0!==e.Records&&{Records:Ze(e.Records,t)}),void 0!==e.StreamName&&{StreamName:e.StreamName})},Ze=function(e,t){return e.map((function(e){return function(e,t){return u(u(u({},void 0!==e.Data&&{Data:t.base64Encoder(e.Data)}),void 0!==e.ExplicitHashKey&&{ExplicitHashKey:e.ExplicitHashKey}),void 0!==e.PartitionKey&&{PartitionKey:e.PartitionKey})}(e,t)}))},Xe=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},Qe=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},et=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},tt=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},nt=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},rt=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},it=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},ot=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},st=function(e,t){return{EncryptionType:void 0!==e.EncryptionType&&null!==e.EncryptionType?e.EncryptionType:void 0,FailedRecordCount:void 0!==e.FailedRecordCount&&null!==e.FailedRecordCount?e.FailedRecordCount:void 0,Records:void 0!==e.Records&&null!==e.Records?at(e.Records,t):void 0}},at=function(e,t){return(e||[]).map((function(e){return function(e,t){return{ErrorCode:void 0!==e.ErrorCode&&null!==e.ErrorCode?e.ErrorCode:void 0,ErrorMessage:void 0!==e.ErrorMessage&&null!==e.ErrorMessage?e.ErrorMessage:void 0,SequenceNumber:void 0!==e.SequenceNumber&&null!==e.SequenceNumber?e.SequenceNumber:void 0,ShardId:void 0!==e.ShardId&&null!==e.ShardId?e.ShardId:void 0}}(e)}))},ut=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},ct=function(e){return{httpStatusCode:e.statusCode,httpHeaders:e.headers,requestId:e.headers["x-amzn-requestid"]}},ft=function(e,t){return void 0===e&&(e=new Uint8Array),e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array)},lt=function(e,t,n,r,i){return c(void 0,void 0,void 0,(function(){var o,s,a,u,c,l;return f(this,(function(f){switch(f.label){case 0:return[4,e.endpoint()];case 1:return o=f.sent(),s=o.hostname,a=o.protocol,u=void 0===a?"https":a,c=o.port,l={protocol:u,hostname:s,port:c,method:"POST",path:n,headers:t},void 0!==r&&(l.hostname=r),void 0!==i&&(l.body=i),[2,new Be.a(l)]}}))}))},dt=function(e,t){return function(e,t){return ft(e,t).then((function(e){return t.utf8Encoder(e)}))}(e,t).then((function(e){return e.length?JSON.parse(e):{}}))},ht=n(10),pt=n(0),vt=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return a(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(ht.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"KinesisClient",commandName:"PutRecordsCommand",inputFilterSensitiveLog:Se.filterSensitiveLog,outputFilterSensitiveLog:Me.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"KinesisClient",commandName:"PutRecordsCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return c(void 0,void 0,void 0,(function(){var n,r;return f(this,(function(i){return n={"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"Kinesis_20131202.PutRecords"},r=JSON.stringify(Je(e,t)),[2,lt(t,n,"/",void 0,r)]}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return c(void 0,void 0,void 0,(function(){var n,r,i;return f(this,(function(o){switch(o.label){case 0:return e.statusCode>=300?[2,Fe(e,t)]:[4,dt(e.body,t)];case 1:return n=o.sent(),{},r=st(n,t),i=u({$metadata:ct(e)},r),[2,Promise.resolve(i)]}}))}))}(e,t)},t}(pt.b),gt=n(150),mt=n(38),bt=n(110),yt=n(18),wt=n(24),_t=n(11),St=n(39),Et=n(17),Mt=n(40),At=n(41),It=n(15),kt=new Set(["ap-east-1","ap-northeast-1","ap-northeast-2","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-north-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-west-1","us-west-2"]),Ot=new Set(["cn-north-1","cn-northwest-1"]),xt=new Set(["us-iso-east-1"]),Ct=new Set(["us-isob-east-1"]),Tt=new Set(["us-gov-east-1","us-gov-west-1"]),Pt=u(u({},{apiVersion:"2013-12-02",disableHostPrefix:!1,logger:{},regionInfoProvider:function(e,t){var n=void 0;switch(e){case"ap-east-1":n={hostname:"kinesis.ap-east-1.amazonaws.com",partition:"aws"};break;case"ap-northeast-1":n={hostname:"kinesis.ap-northeast-1.amazonaws.com",partition:"aws"};break;case"ap-northeast-2":n={hostname:"kinesis.ap-northeast-2.amazonaws.com",partition:"aws"};break;case"ap-south-1":n={hostname:"kinesis.ap-south-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-1":n={hostname:"kinesis.ap-southeast-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-2":n={hostname:"kinesis.ap-southeast-2.amazonaws.com",partition:"aws"};break;case"ca-central-1":n={hostname:"kinesis.ca-central-1.amazonaws.com",partition:"aws"};break;case"cn-north-1":n={hostname:"kinesis.cn-north-1.amazonaws.com.cn",partition:"aws-cn"};break;case"cn-northwest-1":n={hostname:"kinesis.cn-northwest-1.amazonaws.com.cn",partition:"aws-cn"};break;case"eu-central-1":n={hostname:"kinesis.eu-central-1.amazonaws.com",partition:"aws"};break;case"eu-north-1":n={hostname:"kinesis.eu-north-1.amazonaws.com",partition:"aws"};break;case"eu-west-1":n={hostname:"kinesis.eu-west-1.amazonaws.com",partition:"aws"};break;case"eu-west-2":n={hostname:"kinesis.eu-west-2.amazonaws.com",partition:"aws"};break;case"eu-west-3":n={hostname:"kinesis.eu-west-3.amazonaws.com",partition:"aws"};break;case"me-south-1":n={hostname:"kinesis.me-south-1.amazonaws.com",partition:"aws"};break;case"sa-east-1":n={hostname:"kinesis.sa-east-1.amazonaws.com",partition:"aws"};break;case"us-east-1":n={hostname:"kinesis.us-east-1.amazonaws.com",partition:"aws"};break;case"us-east-2":n={hostname:"kinesis.us-east-2.amazonaws.com",partition:"aws"};break;case"us-gov-east-1":n={hostname:"kinesis.us-gov-east-1.amazonaws.com",partition:"aws-us-gov"};break;case"us-gov-west-1":n={hostname:"kinesis.us-gov-west-1.amazonaws.com",partition:"aws-us-gov"};break;case"us-iso-east-1":n={hostname:"kinesis.us-iso-east-1.c2s.ic.gov",partition:"aws-iso"};break;case"us-isob-east-1":n={hostname:"kinesis.us-isob-east-1.sc2s.sgov.gov",partition:"aws-iso-b"};break;case"us-west-1":n={hostname:"kinesis.us-west-1.amazonaws.com",partition:"aws"};break;case"us-west-2":n={hostname:"kinesis.us-west-2.amazonaws.com",partition:"aws"};break;default:kt.has(e)&&(n={hostname:"kinesis.{region}.amazonaws.com".replace("{region}",e),partition:"aws"}),Ot.has(e)&&(n={hostname:"kinesis.{region}.amazonaws.com.cn".replace("{region}",e),partition:"aws-cn"}),xt.has(e)&&(n={hostname:"kinesis.{region}.c2s.ic.gov".replace("{region}",e),partition:"aws-iso"}),Ct.has(e)&&(n={hostname:"kinesis.{region}.sc2s.sgov.gov".replace("{region}",e),partition:"aws-iso-b"}),Tt.has(e)&&(n={hostname:"kinesis.{region}.amazonaws.com".replace("{region}",e),partition:"aws-us-gov"}),void 0===n&&(n={hostname:"kinesis.{region}.amazonaws.com".replace("{region}",e),partition:"aws"})}return Promise.resolve(n)},signingName:"kinesis"}),{runtime:"browser",base64Decoder:Et.a,base64Encoder:Et.b,bodyLengthChecker:Mt.a,credentialDefaultProvider:Object(wt.a)("Credential is missing"),defaultUserAgent:Object(At.a)(gt.name,gt.version),eventStreamSerdeProvider:bt.a,maxAttempts:_t.a,region:Object(wt.a)("Region is missing"),requestHandler:new yt.a,sha256:mt.Sha256,streamCollector:yt.b,urlParser:St.a,utf8Decoder:It.a,utf8Encoder:It.b}),Nt=n(22),Rt=n(112),Lt=n(37),jt=n(21),Dt=n(43),Ut=n(25),Bt=n(23),Ft=function(e){function t(t){var n=this,r=u(u({},Pt),t),i=Object(Nt.b)(r),o=Object(Nt.a)(i),s=Object(Ut.b)(o),a=Object(_t.c)(s),c=Object(Bt.b)(a),f=Object(jt.b)(c),l=Object(Rt.a)(f);return(n=e.call(this,l)||this).config=l,n.middlewareStack.use(Object(Ut.a)(n.config)),n.middlewareStack.use(Object(_t.b)(n.config)),n.middlewareStack.use(Object(Bt.a)(n.config)),n.middlewareStack.use(Object(Lt.a)(n.config)),n.middlewareStack.use(Object(jt.a)(n.config)),n.middlewareStack.use(Object(Dt.a)(n.config)),n}return a(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this)},t}(pt.a),zt=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},qt=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},Kt=new r.a("AWSKinesisProvider"),Ht=function(){function e(e){this._buffer=[],this._config=e||{},this._config.bufferSize=this._config.bufferSize||1e3,this._config.flushSize=this._config.flushSize||100,this._config.flushInterval=this._config.flushInterval||5e3,this._config.resendLimit=this._config.resendLimit||5,this._setupTimer()}return e.prototype._setupTimer=function(){var e=this;this._timer&&clearInterval(this._timer);var t=this._config,n=t.flushSize,r=t.flushInterval;this._timer=setInterval((function(){for(var t=e._buffer.length<n?e._buffer.length:n,r=[],i=0;i<t;i+=1){var o=e._buffer.shift();r.push(o)}e._sendFromBuffer(r)}),r)},e.prototype.getCategory=function(){return"Analytics"},e.prototype.getProviderName=function(){return"AWSKinesis"},e.prototype.configure=function(e){Kt.debug("configure Analytics",e);var t=e||{};return this._config=Object.assign({},this._config,t),this._setupTimer(),this._config},e.prototype.record=function(e){return zt(this,void 0,void 0,(function(){var t;return qt(this,(function(n){switch(n.label){case 0:return[4,this._getCredentials()];case 1:return(t=n.sent())?(Object.assign(e,{config:this._config,credentials:t}),[2,this._putToBuffer(e)]):[2,Promise.resolve(!1)]}}))}))},e.prototype.updateEndpoint=function(){return Kt.debug("updateEndpoint is not implemented in Kinesis provider"),Promise.resolve(!0)},e.prototype._putToBuffer=function(e){return this._buffer.length<1e3?(this._buffer.push(e),Promise.resolve(!0)):(Kt.debug("exceed analytics events buffer size"),Promise.reject(!1))},e.prototype._sendFromBuffer=function(e){for(var t=this,n=[],r=null,i=[],o=0;o<e.length;o+=1){var s=e[o].credentials;0===o?(i.push(e[o]),r=s):s.sessionToken===r.sessionToken&&s.identityId===r.identityId?(Kt.debug("no change for cred, put event in the same group"),i.push(e[o])):(n.push(i),(i=[]).push(e[o]),r=s)}n.push(i),n.map((function(e){t._sendEvents(e)}))},e.prototype._sendEvents=function(e){var t=this;if(0!==e.length){var n=e[0],r=n.config,i=n.credentials;if(!this._init(r,i))return!1;var o={};e.map((function(e){var t=e.event,n=t.streamName;void 0===o[n]&&(o[n]=[]);var r=t.data&&"string"!=typeof t.data?JSON.stringify(t.data):t.data,s={Data:Object(It.a)(r),PartitionKey:t.partitionKey||"partition-"+i.identityId};o[n].push(s)})),Object.keys(o).map((function(e){return zt(t,void 0,void 0,(function(){var t,n;return qt(this,(function(r){switch(r.label){case 0:Kt.debug("putting records to kinesis with records",o[e]),r.label=1;case 1:return r.trys.push([1,3,,4]),t=new vt({Records:o[e],StreamName:e}),[4,this._kinesis.send(t)];case 2:return r.sent(),Kt.debug("Upload records to stream",e),[3,4];case 3:return n=r.sent(),Kt.debug("Failed to upload records to Kinesis",n),[3,4];case 4:return[2]}}))}))}))}},e.prototype._init=function(e,t){if(Kt.debug("init clients"),this._kinesis&&this._config.credentials&&this._config.credentials.sessionToken===t.sessionToken&&this._config.credentials.identityId===t.identityId)return Kt.debug("no change for analytics config, directly return from init"),!0;this._config.credentials=t;var n=e.region,r=e.endpoint;return this._initKinesis(n,r,t)},e.prototype._initKinesis=function(e,t,n){return Kt.debug("initialize kinesis with credentials",n),this._kinesis=new Ft({region:e,credentials:n,customUserAgent:Object(i.b)(),endpoint:t}),!0},e.prototype._getCredentials=function(){var e=this;return o.a.get().then((function(t){return t?(Kt.debug("set credentials for analytics",e._config.credentials),o.a.shear(t)):null})).catch((function(e){return Kt.debug("ensure credentials error",e),null}))},e}()},function(e,t,n){"use strict";n.d(t,"a",(function(){return c}));var r=n(76);function i(e,t){void 0===t&&(t={});var n=function(e){if(e&&"j"===e[0]&&":"===e[1])return e.substr(2);return e}(e);if(function(e,t){return void 0===t&&(t=!e||"{"!==e[0]&&"["!==e[0]&&'"'!==e[0]),!t}(n,t.doNotParse))try{return JSON.parse(n)}catch(e){}return e}var o=function(){return(o=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},s=function(){function e(e,t){var n=this;this.changeListeners=[],this.HAS_DOCUMENT_COOKIE=!1,this.cookies=function(e,t){return"string"==typeof e?r.parse(e,t):"object"==typeof e&&null!==e?e:{}}(e,t),new Promise((function(){n.HAS_DOCUMENT_COOKIE="object"==typeof document&&"string"==typeof document.cookie})).catch((function(){}))}return e.prototype._updateBrowserValues=function(e){this.HAS_DOCUMENT_COOKIE&&(this.cookies=r.parse(document.cookie,e))},e.prototype._emitChange=function(e){for(var t=0;t<this.changeListeners.length;++t)this.changeListeners[t](e)},e.prototype.get=function(e,t,n){return void 0===t&&(t={}),this._updateBrowserValues(n),i(this.cookies[e],t)},e.prototype.getAll=function(e,t){void 0===e&&(e={}),this._updateBrowserValues(t);var n={};for(var r in this.cookies)n[r]=i(this.cookies[r],e);return n},e.prototype.set=function(e,t,n){var i;"object"==typeof t&&(t=JSON.stringify(t)),this.cookies=o(o({},this.cookies),((i={})[e]=t,i)),this.HAS_DOCUMENT_COOKIE&&(document.cookie=r.serialize(e,t,n)),this._emitChange({name:e,value:t,options:n})},e.prototype.remove=function(e,t){var n=t=o(o({},t),{expires:new Date(1970,1,1,0,0,1),maxAge:0});this.cookies=o({},this.cookies),delete this.cookies[e],this.HAS_DOCUMENT_COOKIE&&(document.cookie=r.serialize(e,"",n)),this._emitChange({name:e,value:void 0,options:t})},e.prototype.addChangeListener=function(e){this.changeListeners.push(e)},e.prototype.removeChangeListener=function(e){var t=this.changeListeners.indexOf(e);t>=0&&this.changeListeners.splice(t,1)},e}(),a=n(33),u=Object(a.b)().isBrowser,c=function(){function e(e){void 0===e&&(e={}),this.cookies=new s,this.store=u?window.localStorage:Object.create(null),this.cookies=e.req?new s(e.req.headers.cookie):new s,Object.assign(this.store,this.cookies.getAll())}return Object.defineProperty(e.prototype,"length",{get:function(){return Object.entries(this.store).length},enumerable:!0,configurable:!0}),e.prototype.clear=function(){var e=this;Array.from(new Array(this.length)).map((function(t,n){return e.key(n)})).forEach((function(t){return e.removeItem(t)}))},e.prototype.getItem=function(e){return this.getLocalItem(e)},e.prototype.getLocalItem=function(e){return Object.prototype.hasOwnProperty.call(this.store,e)?this.store[e]:null},e.prototype.getUniversalItem=function(e){return this.cookies.get(e)},e.prototype.key=function(e){return Object.keys(this.store)[e]},e.prototype.removeItem=function(e){this.removeLocalItem(e),this.removeUniversalItem(e)},e.prototype.removeLocalItem=function(e){delete this.store[e]},e.prototype.removeUniversalItem=function(e){this.cookies.remove(e,{path:"/"})},e.prototype.setItem=function(e,t){switch(this.setLocalItem(e,t),e.split(".").pop()){case"LastAuthUser":case"accessToken":case"idToken":this.setUniversalItem(e,t)}},e.prototype.setLocalItem=function(e,t){this.store[e]=t},e.prototype.setUniversalItem=function(e,t){this.cookies.set(e,t,{path:"/",sameSite:!0,secure:"localhost"!==window.location.hostname})},e}()},function(e){e.exports=JSON.parse('{"name":"@aws-sdk/client-cognito-identity","description":"AWS SDK for JavaScript Cognito Identity Client for Node.js, Browser and React Native","version":"1.0.0-rc.4","scripts":{"clean":"npm run remove-definitions && npm run remove-dist","build-documentation":"npm run clean && typedoc ./","prepublishOnly":"yarn build","pretest":"yarn build:cjs","remove-definitions":"rimraf ./types","remove-dist":"rimraf ./dist","remove-documentation":"rimraf ./docs","test:unit":"mocha **/cjs/**/*.spec.js","test:e2e":"mocha **/cjs/**/*.ispec.js && karma start karma.conf.js","test":"yarn test:unit","build:cjs":"tsc -p tsconfig.json","build:es":"tsc -p tsconfig.es.json","build":"yarn build:cjs && yarn build:es"},"main":"./dist/cjs/index.js","types":"./types/index.d.ts","module":"./dist/es/index.js","browser":{"./runtimeConfig":"./runtimeConfig.browser"},"react-native":{"./runtimeConfig":"./runtimeConfig.native"},"sideEffects":false,"dependencies":{"@aws-crypto/sha256-browser":"^1.0.0","@aws-crypto/sha256-js":"^1.0.0","@aws-sdk/config-resolver":"1.0.0-rc.3","@aws-sdk/credential-provider-node":"1.0.0-rc.3","@aws-sdk/fetch-http-handler":"1.0.0-rc.3","@aws-sdk/hash-node":"1.0.0-rc.3","@aws-sdk/invalid-dependency":"1.0.0-rc.3","@aws-sdk/middleware-content-length":"1.0.0-rc.3","@aws-sdk/middleware-host-header":"1.0.0-rc.3","@aws-sdk/middleware-logger":"1.0.0-rc.4","@aws-sdk/middleware-retry":"1.0.0-rc.4","@aws-sdk/middleware-serde":"1.0.0-rc.3","@aws-sdk/middleware-signing":"1.0.0-rc.3","@aws-sdk/middleware-stack":"1.0.0-rc.4","@aws-sdk/middleware-user-agent":"1.0.0-rc.3","@aws-sdk/node-config-provider":"1.0.0-rc.3","@aws-sdk/node-http-handler":"1.0.0-rc.3","@aws-sdk/protocol-http":"1.0.0-rc.3","@aws-sdk/smithy-client":"1.0.0-rc.4","@aws-sdk/types":"1.0.0-rc.3","@aws-sdk/url-parser-browser":"1.0.0-rc.3","@aws-sdk/url-parser-node":"1.0.0-rc.3","@aws-sdk/util-base64-browser":"1.0.0-rc.3","@aws-sdk/util-base64-node":"1.0.0-rc.3","@aws-sdk/util-body-length-browser":"1.0.0-rc.3","@aws-sdk/util-body-length-node":"1.0.0-rc.3","@aws-sdk/util-user-agent-browser":"1.0.0-rc.3","@aws-sdk/util-user-agent-node":"1.0.0-rc.3","@aws-sdk/util-utf8-browser":"1.0.0-rc.3","@aws-sdk/util-utf8-node":"1.0.0-rc.3","tslib":"^2.0.0"},"devDependencies":{"@aws-sdk/client-documentation-generator":"1.0.0-rc.3","@aws-sdk/client-iam":"1.0.0-rc.4","@types/chai":"^4.2.11","@types/mocha":"^7.0.2","@types/node":"^12.7.5","jest":"^26.1.0","rimraf":"^3.0.0","typedoc":"^0.17.8","typescript":"~4.0.2"},"engines":{"node":">=10.0.0"},"author":{"name":"AWS SDK for JavaScript Team","url":"https://aws.amazon.com/javascript/"},"license":"Apache-2.0","homepage":"https://github.com/aws/aws-sdk-js-v3/tree/master/clients/client-cognito-identity","repository":{"type":"git","url":"https://github.com/aws/aws-sdk-js-v3.git","directory":"clients/client-cognito-identity"}}')},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(1).__exportStar(n(385),t)},function(e){e.exports=JSON.parse('{"name":"@aws-sdk/client-pinpoint","description":"AWS SDK for JavaScript Pinpoint Client for Node.js, Browser and React Native","version":"1.0.0-rc.4","scripts":{"clean":"npm run remove-definitions && npm run remove-dist","build-documentation":"npm run clean && typedoc ./","prepublishOnly":"yarn build","pretest":"yarn build:cjs","remove-definitions":"rimraf ./types","remove-dist":"rimraf ./dist","remove-documentation":"rimraf ./docs","test":"exit 0","build:cjs":"tsc -p tsconfig.json","build:es":"tsc -p tsconfig.es.json","build":"yarn build:cjs && yarn build:es"},"main":"./dist/cjs/index.js","types":"./types/index.d.ts","module":"./dist/es/index.js","browser":{"./runtimeConfig":"./runtimeConfig.browser"},"react-native":{"./runtimeConfig":"./runtimeConfig.native"},"sideEffects":false,"dependencies":{"@aws-crypto/sha256-browser":"^1.0.0","@aws-crypto/sha256-js":"^1.0.0","@aws-sdk/config-resolver":"1.0.0-rc.3","@aws-sdk/credential-provider-node":"1.0.0-rc.3","@aws-sdk/fetch-http-handler":"1.0.0-rc.3","@aws-sdk/hash-node":"1.0.0-rc.3","@aws-sdk/invalid-dependency":"1.0.0-rc.3","@aws-sdk/middleware-content-length":"1.0.0-rc.3","@aws-sdk/middleware-host-header":"1.0.0-rc.3","@aws-sdk/middleware-logger":"1.0.0-rc.4","@aws-sdk/middleware-retry":"1.0.0-rc.4","@aws-sdk/middleware-serde":"1.0.0-rc.3","@aws-sdk/middleware-signing":"1.0.0-rc.3","@aws-sdk/middleware-stack":"1.0.0-rc.4","@aws-sdk/middleware-user-agent":"1.0.0-rc.3","@aws-sdk/node-config-provider":"1.0.0-rc.3","@aws-sdk/node-http-handler":"1.0.0-rc.3","@aws-sdk/protocol-http":"1.0.0-rc.3","@aws-sdk/smithy-client":"1.0.0-rc.4","@aws-sdk/types":"1.0.0-rc.3","@aws-sdk/url-parser-browser":"1.0.0-rc.3","@aws-sdk/url-parser-node":"1.0.0-rc.3","@aws-sdk/util-base64-browser":"1.0.0-rc.3","@aws-sdk/util-base64-node":"1.0.0-rc.3","@aws-sdk/util-body-length-browser":"1.0.0-rc.3","@aws-sdk/util-body-length-node":"1.0.0-rc.3","@aws-sdk/util-user-agent-browser":"1.0.0-rc.3","@aws-sdk/util-user-agent-node":"1.0.0-rc.3","@aws-sdk/util-utf8-browser":"1.0.0-rc.3","@aws-sdk/util-utf8-node":"1.0.0-rc.3","tslib":"^2.0.0"},"devDependencies":{"@aws-sdk/client-documentation-generator":"1.0.0-rc.3","@types/node":"^12.7.5","jest":"^26.1.0","rimraf":"^3.0.0","typedoc":"^0.17.8","typescript":"~4.0.2"},"engines":{"node":">=10.0.0"},"author":{"name":"AWS SDK for JavaScript Team","url":"https://aws.amazon.com/javascript/"},"license":"Apache-2.0","homepage":"https://github.com/aws/aws-sdk-js-v3/tree/master/clients/client-pinpoint","repository":{"type":"git","url":"https://github.com/aws/aws-sdk-js-v3.git","directory":"clients/client-pinpoint"}}')},function(e){e.exports=JSON.parse('{"name":"@aws-sdk/client-kinesis","description":"AWS SDK for JavaScript Kinesis Client for Node.js, Browser and React Native","version":"1.0.0-rc.4","scripts":{"clean":"npm run remove-definitions && npm run remove-dist","build-documentation":"npm run clean && typedoc ./","prepublishOnly":"yarn build","pretest":"yarn build:cjs","remove-definitions":"rimraf ./types","remove-dist":"rimraf ./dist","remove-documentation":"rimraf ./docs","test":"exit 0","build:cjs":"tsc -p tsconfig.json","build:es":"tsc -p tsconfig.es.json","build":"yarn build:cjs && yarn build:es"},"main":"./dist/cjs/index.js","types":"./types/index.d.ts","module":"./dist/es/index.js","browser":{"./runtimeConfig":"./runtimeConfig.browser"},"react-native":{"./runtimeConfig":"./runtimeConfig.native"},"sideEffects":false,"dependencies":{"@aws-crypto/sha256-browser":"^1.0.0","@aws-crypto/sha256-js":"^1.0.0","@aws-sdk/config-resolver":"1.0.0-rc.3","@aws-sdk/credential-provider-node":"1.0.0-rc.3","@aws-sdk/eventstream-serde-browser":"1.0.0-rc.3","@aws-sdk/eventstream-serde-config-resolver":"1.0.0-rc.3","@aws-sdk/eventstream-serde-node":"1.0.0-rc.3","@aws-sdk/fetch-http-handler":"1.0.0-rc.3","@aws-sdk/hash-node":"1.0.0-rc.3","@aws-sdk/invalid-dependency":"1.0.0-rc.3","@aws-sdk/middleware-content-length":"1.0.0-rc.3","@aws-sdk/middleware-host-header":"1.0.0-rc.3","@aws-sdk/middleware-logger":"1.0.0-rc.4","@aws-sdk/middleware-retry":"1.0.0-rc.4","@aws-sdk/middleware-serde":"1.0.0-rc.3","@aws-sdk/middleware-signing":"1.0.0-rc.3","@aws-sdk/middleware-stack":"1.0.0-rc.4","@aws-sdk/middleware-user-agent":"1.0.0-rc.3","@aws-sdk/node-config-provider":"1.0.0-rc.3","@aws-sdk/node-http-handler":"1.0.0-rc.3","@aws-sdk/protocol-http":"1.0.0-rc.3","@aws-sdk/smithy-client":"1.0.0-rc.4","@aws-sdk/types":"1.0.0-rc.3","@aws-sdk/url-parser-browser":"1.0.0-rc.3","@aws-sdk/url-parser-node":"1.0.0-rc.3","@aws-sdk/util-base64-browser":"1.0.0-rc.3","@aws-sdk/util-base64-node":"1.0.0-rc.3","@aws-sdk/util-body-length-browser":"1.0.0-rc.3","@aws-sdk/util-body-length-node":"1.0.0-rc.3","@aws-sdk/util-user-agent-browser":"1.0.0-rc.3","@aws-sdk/util-user-agent-node":"1.0.0-rc.3","@aws-sdk/util-utf8-browser":"1.0.0-rc.3","@aws-sdk/util-utf8-node":"1.0.0-rc.3","tslib":"^2.0.0"},"devDependencies":{"@aws-sdk/client-documentation-generator":"1.0.0-rc.3","@types/node":"^12.7.5","jest":"^26.1.0","rimraf":"^3.0.0","typedoc":"^0.17.8","typescript":"~4.0.2"},"engines":{"node":">=10.0.0"},"author":{"name":"AWS SDK for JavaScript Team","url":"https://aws.amazon.com/javascript/"},"license":"Apache-2.0","homepage":"https://github.com/aws/aws-sdk-js-v3/tree/master/clients/client-kinesis","repository":{"type":"git","url":"https://github.com/aws/aws-sdk-js-v3.git","directory":"clients/client-kinesis"}}')},function(e){e.exports=JSON.parse('{"name":"@aws-sdk/client-firehose","description":"AWS SDK for JavaScript Firehose Client for Node.js, Browser and React Native","version":"1.0.0-rc.4","scripts":{"clean":"npm run remove-definitions && npm run remove-dist","build-documentation":"npm run clean && typedoc ./","prepublishOnly":"yarn build","pretest":"yarn build:cjs","remove-definitions":"rimraf ./types","remove-dist":"rimraf ./dist","remove-documentation":"rimraf ./docs","test":"exit 0","build:cjs":"tsc -p tsconfig.json","build:es":"tsc -p tsconfig.es.json","build":"yarn build:cjs && yarn build:es"},"main":"./dist/cjs/index.js","types":"./types/index.d.ts","module":"./dist/es/index.js","browser":{"./runtimeConfig":"./runtimeConfig.browser"},"react-native":{"./runtimeConfig":"./runtimeConfig.native"},"sideEffects":false,"dependencies":{"@aws-crypto/sha256-browser":"^1.0.0","@aws-crypto/sha256-js":"^1.0.0","@aws-sdk/config-resolver":"1.0.0-rc.3","@aws-sdk/credential-provider-node":"1.0.0-rc.3","@aws-sdk/fetch-http-handler":"1.0.0-rc.3","@aws-sdk/hash-node":"1.0.0-rc.3","@aws-sdk/invalid-dependency":"1.0.0-rc.3","@aws-sdk/middleware-content-length":"1.0.0-rc.3","@aws-sdk/middleware-host-header":"1.0.0-rc.3","@aws-sdk/middleware-logger":"1.0.0-rc.4","@aws-sdk/middleware-retry":"1.0.0-rc.4","@aws-sdk/middleware-serde":"1.0.0-rc.3","@aws-sdk/middleware-signing":"1.0.0-rc.3","@aws-sdk/middleware-stack":"1.0.0-rc.4","@aws-sdk/middleware-user-agent":"1.0.0-rc.3","@aws-sdk/node-config-provider":"1.0.0-rc.3","@aws-sdk/node-http-handler":"1.0.0-rc.3","@aws-sdk/protocol-http":"1.0.0-rc.3","@aws-sdk/smithy-client":"1.0.0-rc.4","@aws-sdk/types":"1.0.0-rc.3","@aws-sdk/url-parser-browser":"1.0.0-rc.3","@aws-sdk/url-parser-node":"1.0.0-rc.3","@aws-sdk/util-base64-browser":"1.0.0-rc.3","@aws-sdk/util-base64-node":"1.0.0-rc.3","@aws-sdk/util-body-length-browser":"1.0.0-rc.3","@aws-sdk/util-body-length-node":"1.0.0-rc.3","@aws-sdk/util-user-agent-browser":"1.0.0-rc.3","@aws-sdk/util-user-agent-node":"1.0.0-rc.3","@aws-sdk/util-utf8-browser":"1.0.0-rc.3","@aws-sdk/util-utf8-node":"1.0.0-rc.3","tslib":"^2.0.0"},"devDependencies":{"@aws-sdk/client-documentation-generator":"1.0.0-rc.3","@types/node":"^12.7.5","jest":"^26.1.0","rimraf":"^3.0.0","typedoc":"^0.17.8","typescript":"~4.0.2"},"engines":{"node":">=10.0.0"},"author":{"name":"AWS SDK for JavaScript Team","url":"https://aws.amazon.com/javascript/"},"license":"Apache-2.0","homepage":"https://github.com/aws/aws-sdk-js-v3/tree/master/clients/client-firehose","repository":{"type":"git","url":"https://github.com/aws/aws-sdk-js-v3.git","directory":"clients/client-firehose"}}')},function(e){e.exports=JSON.parse('{"name":"@aws-sdk/client-personalize-events","description":"AWS SDK for JavaScript Personalize Events Client for Node.js, Browser and React Native","version":"1.0.0-rc.4","scripts":{"clean":"npm run remove-definitions && npm run remove-dist","build-documentation":"npm run clean && typedoc ./","prepublishOnly":"yarn build","pretest":"yarn build:cjs","remove-definitions":"rimraf ./types","remove-dist":"rimraf ./dist","remove-documentation":"rimraf ./docs","test":"exit 0","build:cjs":"tsc -p tsconfig.json","build:es":"tsc -p tsconfig.es.json","build":"yarn build:cjs && yarn build:es"},"main":"./dist/cjs/index.js","types":"./types/index.d.ts","module":"./dist/es/index.js","browser":{"./runtimeConfig":"./runtimeConfig.browser"},"react-native":{"./runtimeConfig":"./runtimeConfig.native"},"sideEffects":false,"dependencies":{"@aws-crypto/sha256-browser":"^1.0.0","@aws-crypto/sha256-js":"^1.0.0","@aws-sdk/config-resolver":"1.0.0-rc.3","@aws-sdk/credential-provider-node":"1.0.0-rc.3","@aws-sdk/fetch-http-handler":"1.0.0-rc.3","@aws-sdk/hash-node":"1.0.0-rc.3","@aws-sdk/invalid-dependency":"1.0.0-rc.3","@aws-sdk/middleware-content-length":"1.0.0-rc.3","@aws-sdk/middleware-host-header":"1.0.0-rc.3","@aws-sdk/middleware-logger":"1.0.0-rc.4","@aws-sdk/middleware-retry":"1.0.0-rc.4","@aws-sdk/middleware-serde":"1.0.0-rc.3","@aws-sdk/middleware-signing":"1.0.0-rc.3","@aws-sdk/middleware-stack":"1.0.0-rc.4","@aws-sdk/middleware-user-agent":"1.0.0-rc.3","@aws-sdk/node-config-provider":"1.0.0-rc.3","@aws-sdk/node-http-handler":"1.0.0-rc.3","@aws-sdk/protocol-http":"1.0.0-rc.3","@aws-sdk/smithy-client":"1.0.0-rc.4","@aws-sdk/types":"1.0.0-rc.3","@aws-sdk/url-parser-browser":"1.0.0-rc.3","@aws-sdk/url-parser-node":"1.0.0-rc.3","@aws-sdk/util-base64-browser":"1.0.0-rc.3","@aws-sdk/util-base64-node":"1.0.0-rc.3","@aws-sdk/util-body-length-browser":"1.0.0-rc.3","@aws-sdk/util-body-length-node":"1.0.0-rc.3","@aws-sdk/util-user-agent-browser":"1.0.0-rc.3","@aws-sdk/util-user-agent-node":"1.0.0-rc.3","@aws-sdk/util-utf8-browser":"1.0.0-rc.3","@aws-sdk/util-utf8-node":"1.0.0-rc.3","tslib":"^2.0.0"},"devDependencies":{"@aws-sdk/client-documentation-generator":"1.0.0-rc.3","@types/node":"^12.7.5","jest":"^26.1.0","rimraf":"^3.0.0","typedoc":"^0.17.8","typescript":"~4.0.2"},"engines":{"node":">=10.0.0"},"author":{"name":"AWS SDK for JavaScript Team","url":"https://aws.amazon.com/javascript/"},"license":"Apache-2.0","homepage":"https://github.com/aws/aws-sdk-js-v3/tree/master/clients/client-personalize-events","repository":{"type":"git","url":"https://github.com/aws/aws-sdk-js-v3.git","directory":"clients/client-personalize-events"}}')},function(e){e.exports=JSON.parse('{"name":"@aws-sdk/client-s3","description":"AWS SDK for JavaScript S3 Client for Node.js, Browser and React Native","version":"1.0.0-rc.4","scripts":{"clean":"npm run remove-definitions && npm run remove-dist","build-documentation":"npm run clean && typedoc ./","prepublishOnly":"yarn build","pretest":"yarn build:cjs","remove-definitions":"rimraf ./types","remove-dist":"rimraf ./dist","remove-documentation":"rimraf ./docs","test:unit":"mocha **/cjs/**/*.spec.js","test:e2e":"mocha **/cjs/**/*.ispec.js && karma start karma.conf.js","test":"yarn test:unit","build:cjs":"tsc -p tsconfig.json","build:es":"tsc -p tsconfig.es.json","build":"yarn build:cjs && yarn build:es"},"main":"./dist/cjs/index.js","types":"./types/index.d.ts","module":"./dist/es/index.js","browser":{"./runtimeConfig":"./runtimeConfig.browser"},"react-native":{"./runtimeConfig":"./runtimeConfig.native"},"sideEffects":false,"dependencies":{"@aws-crypto/sha256-browser":"^1.0.0","@aws-crypto/sha256-js":"^1.0.0","@aws-sdk/config-resolver":"1.0.0-rc.3","@aws-sdk/credential-provider-node":"1.0.0-rc.3","@aws-sdk/eventstream-serde-browser":"1.0.0-rc.3","@aws-sdk/eventstream-serde-config-resolver":"1.0.0-rc.3","@aws-sdk/eventstream-serde-node":"1.0.0-rc.3","@aws-sdk/fetch-http-handler":"1.0.0-rc.3","@aws-sdk/hash-blob-browser":"1.0.0-rc.3","@aws-sdk/hash-node":"1.0.0-rc.3","@aws-sdk/hash-stream-node":"1.0.0-rc.3","@aws-sdk/invalid-dependency":"1.0.0-rc.3","@aws-sdk/md5-js":"1.0.0-rc.3","@aws-sdk/middleware-apply-body-checksum":"1.0.0-rc.3","@aws-sdk/middleware-bucket-endpoint":"1.0.0-rc.4","@aws-sdk/middleware-content-length":"1.0.0-rc.3","@aws-sdk/middleware-expect-continue":"1.0.0-rc.3","@aws-sdk/middleware-host-header":"1.0.0-rc.3","@aws-sdk/middleware-location-constraint":"1.0.0-rc.3","@aws-sdk/middleware-logger":"1.0.0-rc.4","@aws-sdk/middleware-retry":"1.0.0-rc.4","@aws-sdk/middleware-sdk-s3":"1.0.0-rc.3","@aws-sdk/middleware-serde":"1.0.0-rc.3","@aws-sdk/middleware-signing":"1.0.0-rc.3","@aws-sdk/middleware-ssec":"1.0.0-rc.3","@aws-sdk/middleware-stack":"1.0.0-rc.4","@aws-sdk/middleware-user-agent":"1.0.0-rc.3","@aws-sdk/node-config-provider":"1.0.0-rc.3","@aws-sdk/node-http-handler":"1.0.0-rc.3","@aws-sdk/protocol-http":"1.0.0-rc.3","@aws-sdk/smithy-client":"1.0.0-rc.4","@aws-sdk/types":"1.0.0-rc.3","@aws-sdk/url-parser-browser":"1.0.0-rc.3","@aws-sdk/url-parser-node":"1.0.0-rc.3","@aws-sdk/util-base64-browser":"1.0.0-rc.3","@aws-sdk/util-base64-node":"1.0.0-rc.3","@aws-sdk/util-body-length-browser":"1.0.0-rc.3","@aws-sdk/util-body-length-node":"1.0.0-rc.3","@aws-sdk/util-user-agent-browser":"1.0.0-rc.3","@aws-sdk/util-user-agent-node":"1.0.0-rc.3","@aws-sdk/util-utf8-browser":"1.0.0-rc.3","@aws-sdk/util-utf8-node":"1.0.0-rc.3","@aws-sdk/xml-builder":"1.0.0-rc.3","fast-xml-parser":"^3.16.0","tslib":"^2.0.0"},"devDependencies":{"@aws-sdk/client-documentation-generator":"1.0.0-rc.3","@types/chai":"^4.2.11","@types/mocha":"^7.0.2","@types/node":"^12.7.5","jest":"^26.1.0","rimraf":"^3.0.0","typedoc":"^0.17.8","typescript":"~4.0.2"},"engines":{"node":">=10.0.0"},"author":{"name":"AWS SDK for JavaScript Team","url":"https://aws.amazon.com/javascript/"},"license":"Apache-2.0","homepage":"https://github.com/aws/aws-sdk-js-v3/tree/master/clients/client-s3","repository":{"type":"git","url":"https://github.com/aws/aws-sdk-js-v3.git","directory":"clients/client-s3"}}')},function(e,t,n){"use strict";(function(e){n.d(t,"a",(function(){return r})),n.d(t,"b",(function(){return S}));var r,i,o=n(58),s=n(52),a=n(42),u=n(26),c=n(44),f=n(88),l=n(34),d=n(14),h=n(13),p=n(9),v=n(3),g=function(){return(g=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},m=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},b=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},y=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},w=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(y(arguments[t]));return e},_=new c.a("DataStore");!function(e){e.CONNECTED="CONNECTED"}(r||(r={})),function(e){e[e.none=0]="none",e[e.unauth=1]="unauth",e[e.auth=2]="auth"}(i||(i={}));var S=function(){function t(e,t,n){void 0===n&&(n={}),this.schema=e,this.syncPredicates=t,this.amplifyConfig=n,this.typeQuery=new WeakMap,this.buffer=[]}return t.prototype.buildSubscription=function(e,t,n,r,i,o){var s=this.amplifyConfig.aws_appsync_authenticationType,a=this.getAuthorizationInfo(t,r,s,i,o)||{},u=a.authMode,c=a.isOwner,f=a.ownerField,l=a.ownerValue,d=y(Object(h.c)(e,t,n,c,f),3);return{authMode:u,opType:d[0],opName:d[1],query:d[2],isOwner:c,ownerField:f,ownerValue:l}},t.prototype.getAuthorizationInfo=function(e,t,n,r,s){void 0===r&&(r={}),void 0===s&&(s={});var a=Object(h.e)(e);if(n===o.a.AWS_IAM&&a.find((function(e){return"private"===e.authStrategy&&"iam"===e.provider}))&&t===i.unauth)return null;var u,c=a.filter((function(e){return"groups"===e.authStrategy&&["userPools","oidc"].includes(e.provider)}));return n!==o.a.AMAZON_COGNITO_USER_POOLS&&n!==o.a.OPENID_CONNECT||!c.find((function(e){var t=Object(h.f)(r,e),n=Object(h.f)(s,e);return w(t,n).find((function(t){return e.groups.find((function(e){return e===t}))}))}))?((n===o.a.AMAZON_COGNITO_USER_POOLS?a.filter((function(e){return"owner"===e.authStrategy&&"userPools"===e.provider})):[]).forEach((function(e){var t=r[e.identityClaim];t&&(u={authMode:o.a.AMAZON_COGNITO_USER_POOLS,isOwner:!e.areSubscriptionsPublic,ownerField:e.ownerField,ownerValue:t})})),u||((n===o.a.OPENID_CONNECT?a.filter((function(e){return"owner"===e.authStrategy&&"oidc"===e.provider})):[]).forEach((function(e){var t=s[e.identityClaim];t&&(u={authMode:o.a.OPENID_CONNECT,isOwner:!e.areSubscriptionsPublic,ownerField:e.ownerField,ownerValue:t})})),u||{authMode:n,isOwner:!1})):{authMode:n,isOwner:!1}},t.prototype.hubQueryCompletionListener=function(e,t){t.payload.event===l.a.SUBSCRIPTION_ACK&&e()},t.prototype.start=function(){var t=this;return[new d.a((function(n){var o,c,l=[],d=[],v=i.none;return m(t,void 0,void 0,(function(){var t,w,S,E,M,A,I,k,O,x,C=this;return b(this,(function(T){switch(T.label){case 0:return T.trys.push([0,2,,3]),[4,a.a.currentCredentials()];case 1:return t=T.sent(),v=t.authenticated?i.auth:i.unauth,[3,3];case 2:return T.sent(),[3,3];case 3:return T.trys.push([3,5,,6]),[4,a.a.currentSession()];case 4:return w=T.sent(),o=w.getIdToken().decodePayload(),[3,6];case 5:return T.sent(),[3,6];case 6:if(T.trys.push([6,11,,12]),S=this.amplifyConfig,E=S.aws_cognito_region,M=S.Auth,!E||M&&!M.region)throw"Auth is not configured";return A=void 0,[4,u.a.getItem("federatedInfo")];case 7:return(I=T.sent())?(A=I.token,[3,10]):[3,8];case 8:return[4,a.a.currentAuthenticatedUser()];case 9:(k=T.sent())&&(A=k.token),T.label=10;case 10:return A&&(O=A.split(".")[1],c=JSON.parse(e.from(O,"base64").toString("utf8"))),[3,12];case 11:return x=T.sent(),_.debug("error getting OIDC JWT",x),[3,12];case 12:return Object.values(this.schema.namespaces).forEach((function(e){Object.values(e.models).filter((function(e){return e.syncable})).forEach((function(t){return m(C,void 0,void 0,(function(){var r=this;return b(this,(function(i){return[h.a.CREATE,h.a.UPDATE,h.a.DELETE].map((function(n){return r.buildSubscription(e,t,n,v,o,c)})).forEach((function(e){var i=e.opType,o=e.opName,a=e.query,u=e.isOwner,c=e.ownerField,h=e.ownerValue,v=e.authMode;return m(r,void 0,void 0,(function(){var e,r,w,S=this;return b(this,(function(E){if(e={},u){if(!h)return n.error("Owner field required, sign in is needed in order to perform this operation"),[2];e[c]=h}return r=s.a.graphql(g({query:a,variables:e},{authMode:v})),d.push(r.map((function(e){return e.value})).subscribe({next:function(e){var n=e.data,r=e.errors;if(Array.isArray(r)&&r.length>0){var s=r.map((function(e){return e.message}));return _.warn("Skipping incoming subscription. Messages: "+s.join("\n")),void S.drainBuffer()}var a=p.a.getPredicates(S.syncPredicates.get(t),!1),u=n[o];S.passesPredicateValidation(u,a)&&S.pushToBuffer(i,t,u),S.drainBuffer()},error:function(e){var t=e.error,r=y((void 0===t?{errors:[]}:t).errors,1)[0],i=(void 0===r?{}:r).message,o=void 0===i?"":i;_.warn("subscriptionError",o),"function"==typeof w&&w(),o.includes('"errorType":"Unauthorized"')||n.error(o)}})),l.push(m(S,void 0,void 0,(function(){var e,t=this;return b(this,(function(n){switch(n.label){case 0:return[4,new Promise((function(n){w=n,e=t.hubQueryCompletionListener.bind(t,n),f.a.listen("api",e)}))];case 1:return n.sent(),f.a.remove("api",e),[2]}}))}))),[2]}))}))})),[2]}))}))}))})),Promise.all(l).then((function(){return n.next(r.CONNECTED)})),[2]}}))})),function(){d.forEach((function(e){return e.unsubscribe()}))}})),new d.a((function(e){return t.dataObserver=e,t.drainBuffer(),function(){t.dataObserver=null}}))]},t.prototype.passesPredicateValidation=function(e,t){if(!t)return!0;var n=t.predicates,r=t.type;return Object(v.y)(e,r,n)},t.prototype.pushToBuffer=function(e,t,n){this.buffer.push([e,t,n])},t.prototype.drainBuffer=function(){var e=this;this.dataObserver&&(this.buffer.forEach((function(t){return e.dataObserver.next(t)})),this.buffer=[])},t}()}).call(this,n(6).Buffer)},function(e){e.exports=JSON.parse('{"name":"@aws-sdk/client-lex-runtime-service","description":"AWS SDK for JavaScript Lex Runtime Service Client for Node.js, Browser and React Native","version":"1.0.0-rc.4","scripts":{"clean":"npm run remove-definitions && npm run remove-dist","build-documentation":"npm run clean && typedoc ./","prepublishOnly":"yarn build","pretest":"yarn build:cjs","remove-definitions":"rimraf ./types","remove-dist":"rimraf ./dist","remove-documentation":"rimraf ./docs","test:unit":"mocha **/cjs/**/*.spec.js","test":"yarn test:unit","build:cjs":"tsc -p tsconfig.json","build:es":"tsc -p tsconfig.es.json","build":"yarn build:cjs && yarn build:es"},"main":"./dist/cjs/index.js","types":"./types/index.d.ts","module":"./dist/es/index.js","browser":{"./runtimeConfig":"./runtimeConfig.browser"},"react-native":{"./runtimeConfig":"./runtimeConfig.native"},"sideEffects":false,"dependencies":{"@aws-crypto/sha256-browser":"^1.0.0","@aws-crypto/sha256-js":"^1.0.0","@aws-sdk/config-resolver":"1.0.0-rc.3","@aws-sdk/credential-provider-node":"1.0.0-rc.3","@aws-sdk/fetch-http-handler":"1.0.0-rc.3","@aws-sdk/hash-node":"1.0.0-rc.3","@aws-sdk/invalid-dependency":"1.0.0-rc.3","@aws-sdk/middleware-content-length":"1.0.0-rc.3","@aws-sdk/middleware-host-header":"1.0.0-rc.3","@aws-sdk/middleware-logger":"1.0.0-rc.4","@aws-sdk/middleware-retry":"1.0.0-rc.4","@aws-sdk/middleware-serde":"1.0.0-rc.3","@aws-sdk/middleware-signing":"1.0.0-rc.3","@aws-sdk/middleware-stack":"1.0.0-rc.4","@aws-sdk/middleware-user-agent":"1.0.0-rc.3","@aws-sdk/node-config-provider":"1.0.0-rc.3","@aws-sdk/node-http-handler":"1.0.0-rc.3","@aws-sdk/protocol-http":"1.0.0-rc.3","@aws-sdk/smithy-client":"1.0.0-rc.4","@aws-sdk/types":"1.0.0-rc.3","@aws-sdk/url-parser-browser":"1.0.0-rc.3","@aws-sdk/url-parser-node":"1.0.0-rc.3","@aws-sdk/util-base64-browser":"1.0.0-rc.3","@aws-sdk/util-base64-node":"1.0.0-rc.3","@aws-sdk/util-body-length-browser":"1.0.0-rc.3","@aws-sdk/util-body-length-node":"1.0.0-rc.3","@aws-sdk/util-user-agent-browser":"1.0.0-rc.3","@aws-sdk/util-user-agent-node":"1.0.0-rc.3","@aws-sdk/util-utf8-browser":"1.0.0-rc.3","@aws-sdk/util-utf8-node":"1.0.0-rc.3","tslib":"^2.0.0"},"devDependencies":{"@aws-sdk/client-documentation-generator":"1.0.0-rc.3","@types/chai":"^4.2.11","@types/mocha":"^7.0.2","@types/node":"^12.7.5","jest":"^26.1.0","rimraf":"^3.0.0","typedoc":"^0.17.8","typescript":"~4.0.2"},"engines":{"node":">=10.0.0"},"author":{"name":"AWS SDK for JavaScript Team","url":"https://aws.amazon.com/javascript/"},"license":"Apache-2.0","homepage":"https://github.com/aws/aws-sdk-js-v3/tree/master/clients/client-lex-runtime-service","repository":{"type":"git","url":"https://github.com/aws/aws-sdk-js-v3.git","directory":"clients/client-lex-runtime-service"}}')},,function(e,t,n){"use strict";n.r(t);var r=n(19),i=n(143);n.d(t,"Amplify",(function(){return r.a}));var o=n(63),s=n(26),a=n(491);n.d(t,"Analytics",(function(){return a.a}));var u=n(144);n.d(t,"AWSPinpointProvider",(function(){return u.a}));var c=n(145);n.d(t,"AWSKinesisProvider",(function(){return c.a}));var f=n(492);n.d(t,"AWSKinesisFirehoseProvider",(function(){return f.a}));var l=n(490);n.d(t,"AmazonPersonalizeProvider",(function(){return l.a})),n.d(t,"Auth",(function(){return o.a}));var d=n(140);n.d(t,"Storage",(function(){return d.a})),n.d(t,"StorageClass",(function(){return d.b}));var h=n(62);n.d(t,"API",(function(){return h.a})),n.d(t,"APIClass",(function(){return h.b}));var p=n(248);n.d(t,"graphqlOperation",(function(){return p.b}));var v=n(258);n.d(t,"DataStore",(function(){return v.a}));var g=n(9);n.d(t,"Predicates",(function(){return g.b}));var m=n(4);n.d(t,"SortDirection",(function(){return m.e})),n.d(t,"syncExpression",(function(){return m.n}));var b=n(105);n.d(t,"PubSub",(function(){return b.a})),n.d(t,"Cache",(function(){return s.a}));var y=n(489);n.d(t,"Interactions",(function(){return y.a}));var w=n(246);for(var _ in w)["default","Analytics","AWSPinpointProvider","AWSKinesisProvider","AWSKinesisFirehoseProvider","AmazonPersonalizeProvider","Auth","Storage","StorageClass","API","APIClass","graphqlOperation","DataStore","Predicates","SortDirection","syncExpression","PubSub","Cache","Interactions","XR","Predictions","Logger","Hub","JS","ClientDevice","Signer","I18n","ServiceWorker","withSSRContext","Amplify"].indexOf(_)<0&&function(e){n.d(t,e,(function(){return w[e]}))}(_);var S=n(493);n.d(t,"XR",(function(){return S.a}));var E=n(488);n.d(t,"Predictions",(function(){return E.a}));var M=n(44);n.d(t,"Logger",(function(){return M.a}));var A=n(88);n.d(t,"Hub",(function(){return A.a}));var I=n(33);n.d(t,"JS",(function(){return I.a}));var k=n(141);n.d(t,"ClientDevice",(function(){return k.a}));var O=n(104);n.d(t,"Signer",(function(){return O.a}));var x=n(142);n.d(t,"I18n",(function(){return x.a})),n.d(t,"ServiceWorker",(function(){return i.a}));var C=n(247);n.d(t,"withSSRContext",(function(){return C.a})),r.a.Auth=o.a,r.a.Cache=s.a,r.a.ServiceWorker=i.a,t.default=r.a},,,function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t,n){var r;e.exports=(r=n(32),function(){if("function"==typeof ArrayBuffer){var e=r.lib.WordArray,t=e.init;(e.init=function(e){if(e instanceof ArrayBuffer&&(e=new Uint8Array(e)),(e instanceof Int8Array||"undefined"!=typeof Uint8ClampedArray&&e instanceof Uint8ClampedArray||e instanceof Int16Array||e instanceof Uint16Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Float32Array||e instanceof Float64Array)&&(e=new Uint8Array(e.buffer,e.byteOffset,e.byteLength)),e instanceof Uint8Array){for(var n=e.byteLength,r=[],i=0;i<n;i++)r[i>>>2]|=e[i]<<24-i%4*8;t.call(this,r,n)}else t.apply(this,arguments)}).prototype=e}}(),r.lib.WordArray)},function(e,t,n){"use strict";var r=n(8).Buffer,i=n(273).Transform;function o(e){i.call(this),this._block=r.allocUnsafe(e),this._blockSize=e,this._blockOffset=0,this._length=[0,0,0,0],this._finalized=!1}n(7)(o,i),o.prototype._transform=function(e,t,n){var r=null;try{this.update(e,t)}catch(e){r=e}n(r)},o.prototype._flush=function(e){var t=null;try{this.push(this.digest())}catch(e){t=e}e(t)},o.prototype.update=function(e,t){if(function(e,t){if(!r.isBuffer(e)&&"string"!=typeof e)throw new TypeError(t+" must be a string or a buffer")}(e,"Data"),this._finalized)throw new Error("Digest already called");r.isBuffer(e)||(e=r.from(e,t));for(var n=this._block,i=0;this._blockOffset+e.length-i>=this._blockSize;){for(var o=this._blockOffset;o<this._blockSize;)n[o++]=e[i++];this._update(),this._blockOffset=0}for(;i<e.length;)n[this._blockOffset++]=e[i++];for(var s=0,a=8*e.length;a>0;++s)this._length[s]+=a,(a=this._length[s]/4294967296|0)>0&&(this._length[s]-=4294967296*a);return this},o.prototype._update=function(){throw new Error("_update is not implemented")},o.prototype.digest=function(e){if(this._finalized)throw new Error("Digest already called");this._finalized=!0;var t=this._digest();void 0!==e&&(t=t.toString(e)),this._block.fill(0),this._blockOffset=0;for(var n=0;n<4;++n)this._length[n]=0;return t},o.prototype._digest=function(){throw new Error("_digest is not implemented")},e.exports=o},function(e,t,n){"use strict";(function(t,r){var i;e.exports=A,A.ReadableState=M;n(49).EventEmitter;var o=function(e,t){return e.listeners(t).length},s=n(164),a=n(6).Buffer,u=t.Uint8Array||function(){};var c,f=n(274);c=f&&f.debuglog?f.debuglog("stream"):function(){};var l,d,h,p=n(275),v=n(165),g=n(166).getHighWaterMark,m=n(67).codes,b=m.ERR_INVALID_ARG_TYPE,y=m.ERR_STREAM_PUSH_AFTER_EOF,w=m.ERR_METHOD_NOT_IMPLEMENTED,_=m.ERR_STREAM_UNSHIFT_AFTER_END_EVENT;n(7)(A,s);var S=v.errorOrDestroy,E=["error","close","destroy","pause","resume"];function M(e,t,r){i=i||n(68),e=e||{},"boolean"!=typeof r&&(r=t instanceof i),this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.readableObjectMode),this.highWaterMark=g(this,e,"readableHighWaterMark",r),this.buffer=new p,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.paused=!0,this.emitClose=!1!==e.emitClose,this.autoDestroy=!!e.autoDestroy,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(l||(l=n(59).StringDecoder),this.decoder=new l(e.encoding),this.encoding=e.encoding)}function A(e){if(i=i||n(68),!(this instanceof A))return new A(e);var t=this instanceof i;this._readableState=new M(e,this,t),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),s.call(this)}function I(e,t,n,r,i){c("readableAddChunk",t);var o,s=e._readableState;if(null===t)s.reading=!1,function(e,t){if(c("onEofChunk"),t.ended)return;if(t.decoder){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.sync?x(e):(t.needReadable=!1,t.emittedReadable||(t.emittedReadable=!0,C(e)))}(e,s);else if(i||(o=function(e,t){var n;r=t,a.isBuffer(r)||r instanceof u||"string"==typeof t||void 0===t||e.objectMode||(n=new b("chunk",["string","Buffer","Uint8Array"],t));var r;return n}(s,t)),o)S(e,o);else if(s.objectMode||t&&t.length>0)if("string"==typeof t||s.objectMode||Object.getPrototypeOf(t)===a.prototype||(t=function(e){return a.from(e)}(t)),r)s.endEmitted?S(e,new _):k(e,s,t,!0);else if(s.ended)S(e,new y);else{if(s.destroyed)return!1;s.reading=!1,s.decoder&&!n?(t=s.decoder.write(t),s.objectMode||0!==t.length?k(e,s,t,!1):T(e,s)):k(e,s,t,!1)}else r||(s.reading=!1,T(e,s));return!s.ended&&(s.length<s.highWaterMark||0===s.length)}function k(e,t,n,r){t.flowing&&0===t.length&&!t.sync?(t.awaitDrain=0,e.emit("data",n)):(t.length+=t.objectMode?1:n.length,r?t.buffer.unshift(n):t.buffer.push(n),t.needReadable&&x(e)),T(e,t)}Object.defineProperty(A.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._readableState&&this._readableState.destroyed},set:function(e){this._readableState&&(this._readableState.destroyed=e)}}),A.prototype.destroy=v.destroy,A.prototype._undestroy=v.undestroy,A.prototype._destroy=function(e,t){t(e)},A.prototype.push=function(e,t){var n,r=this._readableState;return r.objectMode?n=!0:"string"==typeof e&&((t=t||r.defaultEncoding)!==r.encoding&&(e=a.from(e,t),t=""),n=!0),I(this,e,t,!1,n)},A.prototype.unshift=function(e){return I(this,e,null,!0,!1)},A.prototype.isPaused=function(){return!1===this._readableState.flowing},A.prototype.setEncoding=function(e){l||(l=n(59).StringDecoder);var t=new l(e);this._readableState.decoder=t,this._readableState.encoding=this._readableState.decoder.encoding;for(var r=this._readableState.buffer.head,i="";null!==r;)i+=t.write(r.data),r=r.next;return this._readableState.buffer.clear(),""!==i&&this._readableState.buffer.push(i),this._readableState.length=i.length,this};function O(e,t){return e<=0||0===t.length&&t.ended?0:t.objectMode?1:e!=e?t.flowing&&t.length?t.buffer.head.data.length:t.length:(e>t.highWaterMark&&(t.highWaterMark=function(e){return e>=1073741824?e=1073741824:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function x(e){var t=e._readableState;c("emitReadable",t.needReadable,t.emittedReadable),t.needReadable=!1,t.emittedReadable||(c("emitReadable",t.flowing),t.emittedReadable=!0,r.nextTick(C,e))}function C(e){var t=e._readableState;c("emitReadable_",t.destroyed,t.length,t.ended),t.destroyed||!t.length&&!t.ended||(e.emit("readable"),t.emittedReadable=!1),t.needReadable=!t.flowing&&!t.ended&&t.length<=t.highWaterMark,j(e)}function T(e,t){t.readingMore||(t.readingMore=!0,r.nextTick(P,e,t))}function P(e,t){for(;!t.reading&&!t.ended&&(t.length<t.highWaterMark||t.flowing&&0===t.length);){var n=t.length;if(c("maybeReadMore read 0"),e.read(0),n===t.length)break}t.readingMore=!1}function N(e){var t=e._readableState;t.readableListening=e.listenerCount("readable")>0,t.resumeScheduled&&!t.paused?t.flowing=!0:e.listenerCount("data")>0&&e.resume()}function R(e){c("readable nexttick read 0"),e.read(0)}function L(e,t){c("resume",t.reading),t.reading||e.read(0),t.resumeScheduled=!1,e.emit("resume"),j(e),t.flowing&&!t.reading&&e.read(0)}function j(e){var t=e._readableState;for(c("flow",t.flowing);t.flowing&&null!==e.read(););}function D(e,t){return 0===t.length?null:(t.objectMode?n=t.buffer.shift():!e||e>=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.first():t.buffer.concat(t.length),t.buffer.clear()):n=t.buffer.consume(e,t.decoder),n);var n}function U(e){var t=e._readableState;c("endReadable",t.endEmitted),t.endEmitted||(t.ended=!0,r.nextTick(B,t,e))}function B(e,t){if(c("endReadableNT",e.endEmitted,e.length),!e.endEmitted&&0===e.length&&(e.endEmitted=!0,t.readable=!1,t.emit("end"),e.autoDestroy)){var n=t._writableState;(!n||n.autoDestroy&&n.finished)&&t.destroy()}}function F(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1}A.prototype.read=function(e){c("read",e),e=parseInt(e,10);var t=this._readableState,n=e;if(0!==e&&(t.emittedReadable=!1),0===e&&t.needReadable&&((0!==t.highWaterMark?t.length>=t.highWaterMark:t.length>0)||t.ended))return c("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?U(this):x(this),null;if(0===(e=O(e,t))&&t.ended)return 0===t.length&&U(this),null;var r,i=t.needReadable;return c("need readable",i),(0===t.length||t.length-e<t.highWaterMark)&&c("length less than watermark",i=!0),t.ended||t.reading?c("reading or ended",i=!1):i&&(c("do read"),t.reading=!0,t.sync=!0,0===t.length&&(t.needReadable=!0),this._read(t.highWaterMark),t.sync=!1,t.reading||(e=O(n,t))),null===(r=e>0?D(e,t):null)?(t.needReadable=t.length<=t.highWaterMark,e=0):(t.length-=e,t.awaitDrain=0),0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&U(this)),null!==r&&this.emit("data",r),r},A.prototype._read=function(e){S(this,new w("_read()"))},A.prototype.pipe=function(e,t){var n=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=e;break;case 1:i.pipes=[i.pipes,e];break;default:i.pipes.push(e)}i.pipesCount+=1,c("pipe count=%d opts=%j",i.pipesCount,t);var s=(!t||!1!==t.end)&&e!==r.stdout&&e!==r.stderr?u:g;function a(t,r){c("onunpipe"),t===n&&r&&!1===r.hasUnpiped&&(r.hasUnpiped=!0,c("cleanup"),e.removeListener("close",p),e.removeListener("finish",v),e.removeListener("drain",f),e.removeListener("error",h),e.removeListener("unpipe",a),n.removeListener("end",u),n.removeListener("end",g),n.removeListener("data",d),l=!0,!i.awaitDrain||e._writableState&&!e._writableState.needDrain||f())}function u(){c("onend"),e.end()}i.endEmitted?r.nextTick(s):n.once("end",s),e.on("unpipe",a);var f=function(e){return function(){var t=e._readableState;c("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&o(e,"data")&&(t.flowing=!0,j(e))}}(n);e.on("drain",f);var l=!1;function d(t){c("ondata");var r=e.write(t);c("dest.write",r),!1===r&&((1===i.pipesCount&&i.pipes===e||i.pipesCount>1&&-1!==F(i.pipes,e))&&!l&&(c("false write response, pause",i.awaitDrain),i.awaitDrain++),n.pause())}function h(t){c("onerror",t),g(),e.removeListener("error",h),0===o(e,"error")&&S(e,t)}function p(){e.removeListener("finish",v),g()}function v(){c("onfinish"),e.removeListener("close",p),g()}function g(){c("unpipe"),n.unpipe(e)}return n.on("data",d),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?Array.isArray(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",h),e.once("close",p),e.once("finish",v),e.emit("pipe",n),i.flowing||(c("pipe resume"),n.resume()),e},A.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes||(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n)),this;if(!e){var r=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var o=0;o<i;o++)r[o].emit("unpipe",this,{hasUnpiped:!1});return this}var s=F(t.pipes,e);return-1===s||(t.pipes.splice(s,1),t.pipesCount-=1,1===t.pipesCount&&(t.pipes=t.pipes[0]),e.emit("unpipe",this,n)),this},A.prototype.on=function(e,t){var n=s.prototype.on.call(this,e,t),i=this._readableState;return"data"===e?(i.readableListening=this.listenerCount("readable")>0,!1!==i.flowing&&this.resume()):"readable"===e&&(i.endEmitted||i.readableListening||(i.readableListening=i.needReadable=!0,i.flowing=!1,i.emittedReadable=!1,c("on readable",i.length,i.reading),i.length?x(this):i.reading||r.nextTick(R,this))),n},A.prototype.addListener=A.prototype.on,A.prototype.removeListener=function(e,t){var n=s.prototype.removeListener.call(this,e,t);return"readable"===e&&r.nextTick(N,this),n},A.prototype.removeAllListeners=function(e){var t=s.prototype.removeAllListeners.apply(this,arguments);return"readable"!==e&&void 0!==e||r.nextTick(N,this),t},A.prototype.resume=function(){var e=this._readableState;return e.flowing||(c("resume"),e.flowing=!e.readableListening,function(e,t){t.resumeScheduled||(t.resumeScheduled=!0,r.nextTick(L,e,t))}(this,e)),e.paused=!1,this},A.prototype.pause=function(){return c("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(c("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this},A.prototype.wrap=function(e){var t=this,n=this._readableState,r=!1;for(var i in e.on("end",(function(){if(c("wrapped end"),n.decoder&&!n.ended){var e=n.decoder.end();e&&e.length&&t.push(e)}t.push(null)})),e.on("data",(function(i){(c("wrapped data"),n.decoder&&(i=n.decoder.write(i)),n.objectMode&&null==i)||(n.objectMode||i&&i.length)&&(t.push(i)||(r=!0,e.pause()))})),e)void 0===this[i]&&"function"==typeof e[i]&&(this[i]=function(t){return function(){return e[t].apply(e,arguments)}}(i));for(var o=0;o<E.length;o++)e.on(E[o],this.emit.bind(this,E[o]));return this._read=function(t){c("wrapped _read",t),r&&(r=!1,e.resume())},this},"function"==typeof Symbol&&(A.prototype[Symbol.asyncIterator]=function(){return void 0===d&&(d=n(277)),d(this)}),Object.defineProperty(A.prototype,"readableHighWaterMark",{enumerable:!1,get:function(){return this._readableState.highWaterMark}}),Object.defineProperty(A.prototype,"readableBuffer",{enumerable:!1,get:function(){return this._readableState&&this._readableState.buffer}}),Object.defineProperty(A.prototype,"readableFlowing",{enumerable:!1,get:function(){return this._readableState.flowing},set:function(e){this._readableState&&(this._readableState.flowing=e)}}),A._fromList=D,Object.defineProperty(A.prototype,"readableLength",{enumerable:!1,get:function(){return this._readableState.length}}),"function"==typeof Symbol&&(A.from=function(e,t){return void 0===h&&(h=n(278)),h(A,e,t)})}).call(this,n(31),n(20))},function(e,t,n){e.exports=n(49).EventEmitter},function(e,t,n){"use strict";(function(t){function n(e,t){i(e,t),r(e)}function r(e){e._writableState&&!e._writableState.emitClose||e._readableState&&!e._readableState.emitClose||e.emit("close")}function i(e,t){e.emit("error",t)}e.exports={destroy:function(e,o){var s=this,a=this._readableState&&this._readableState.destroyed,u=this._writableState&&this._writableState.destroyed;return a||u?(o?o(e):e&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,t.nextTick(i,this,e)):t.nextTick(i,this,e)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,(function(e){!o&&e?s._writableState?s._writableState.errorEmitted?t.nextTick(r,s):(s._writableState.errorEmitted=!0,t.nextTick(n,s,e)):t.nextTick(n,s,e):o?(t.nextTick(r,s),o(e)):t.nextTick(r,s)})),this)},undestroy:function(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)},errorOrDestroy:function(e,t){var n=e._readableState,r=e._writableState;n&&n.autoDestroy||r&&r.autoDestroy?e.destroy(t):e.emit("error",t)}}}).call(this,n(20))},function(e,t,n){"use strict";var r=n(67).codes.ERR_INVALID_OPT_VALUE;e.exports={getHighWaterMark:function(e,t,n,i){var o=function(e,t,n){return null!=e.highWaterMark?e.highWaterMark:t?e[n]:null}(t,i,n);if(null!=o){if(!isFinite(o)||Math.floor(o)!==o||o<0)throw new r(i?n:"highWaterMark",o);return Math.floor(o)}return e.objectMode?16:16384}}},function(e,t,n){"use strict";(function(t,r){function i(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var r=e.entry;e.entry=null;for(;r;){var i=r.callback;t.pendingcb--,i(n),r=r.next}t.corkedRequestsFree.next=e}(t,e)}}var o;e.exports=A,A.WritableState=M;var s={deprecate:n(114)},a=n(164),u=n(6).Buffer,c=t.Uint8Array||function(){};var f,l=n(165),d=n(166).getHighWaterMark,h=n(67).codes,p=h.ERR_INVALID_ARG_TYPE,v=h.ERR_METHOD_NOT_IMPLEMENTED,g=h.ERR_MULTIPLE_CALLBACK,m=h.ERR_STREAM_CANNOT_PIPE,b=h.ERR_STREAM_DESTROYED,y=h.ERR_STREAM_NULL_VALUES,w=h.ERR_STREAM_WRITE_AFTER_END,_=h.ERR_UNKNOWN_ENCODING,S=l.errorOrDestroy;function E(){}function M(e,t,s){o=o||n(68),e=e||{},"boolean"!=typeof s&&(s=t instanceof o),this.objectMode=!!e.objectMode,s&&(this.objectMode=this.objectMode||!!e.writableObjectMode),this.highWaterMark=d(this,e,"writableHighWaterMark",s),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var a=!1===e.decodeStrings;this.decodeStrings=!a,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,i=n.sync,o=n.writecb;if("function"!=typeof o)throw new g;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,i,o){--t.pendingcb,n?(r.nextTick(o,i),r.nextTick(T,e,t),e._writableState.errorEmitted=!0,S(e,i)):(o(i),e._writableState.errorEmitted=!0,S(e,i),T(e,t))}(e,n,i,t,o);else{var s=x(n)||e.destroyed;s||n.corked||n.bufferProcessing||!n.bufferedRequest||O(e,n),i?r.nextTick(k,e,n,s,o):k(e,n,s,o)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!1!==e.emitClose,this.autoDestroy=!!e.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new i(this)}function A(e){var t=this instanceof(o=o||n(68));if(!t&&!f.call(A,this))return new A(e);this._writableState=new M(e,this,t),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),a.call(this)}function I(e,t,n,r,i,o,s){t.writelen=r,t.writecb=s,t.writing=!0,t.sync=!0,t.destroyed?t.onwrite(new b("write")):n?e._writev(i,t.onwrite):e._write(i,o,t.onwrite),t.sync=!1}function k(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),T(e,t)}function O(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,o=new Array(r),s=t.corkedRequestsFree;s.entry=n;for(var a=0,u=!0;n;)o[a]=n,n.isBuf||(u=!1),n=n.next,a+=1;o.allBuffers=u,I(e,t,!0,t.length,o,"",s.finish),t.pendingcb++,t.lastBufferedRequest=null,s.next?(t.corkedRequestsFree=s.next,s.next=null):t.corkedRequestsFree=new i(t),t.bufferedRequestCount=0}else{for(;n;){var c=n.chunk,f=n.encoding,l=n.callback;if(I(e,t,!1,t.objectMode?1:c.length,c,f,l),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function x(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function C(e,t){e._final((function(n){t.pendingcb--,n&&S(e,n),t.prefinished=!0,e.emit("prefinish"),T(e,t)}))}function T(e,t){var n=x(t);if(n&&(function(e,t){t.prefinished||t.finalCalled||("function"!=typeof e._final||t.destroyed?(t.prefinished=!0,e.emit("prefinish")):(t.pendingcb++,t.finalCalled=!0,r.nextTick(C,e,t)))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"),t.autoDestroy))){var i=e._readableState;(!i||i.autoDestroy&&i.endEmitted)&&e.destroy()}return n}n(7)(A,a),M.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(M.prototype,"buffer",{get:s.deprecate((function(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(f=Function.prototype[Symbol.hasInstance],Object.defineProperty(A,Symbol.hasInstance,{value:function(e){return!!f.call(this,e)||this===A&&(e&&e._writableState instanceof M)}})):f=function(e){return e instanceof this},A.prototype.pipe=function(){S(this,new m)},A.prototype.write=function(e,t,n){var i,o=this._writableState,s=!1,a=!o.objectMode&&(i=e,u.isBuffer(i)||i instanceof c);return a&&!u.isBuffer(e)&&(e=function(e){return u.from(e)}(e)),"function"==typeof t&&(n=t,t=null),a?t="buffer":t||(t=o.defaultEncoding),"function"!=typeof n&&(n=E),o.ending?function(e,t){var n=new w;S(e,n),r.nextTick(t,n)}(this,n):(a||function(e,t,n,i){var o;return null===n?o=new y:"string"==typeof n||t.objectMode||(o=new p("chunk",["string","Buffer"],n)),!o||(S(e,o),r.nextTick(i,o),!1)}(this,o,e,n))&&(o.pendingcb++,s=function(e,t,n,r,i,o){if(!n){var s=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=u.from(t,n));return t}(t,r,i);r!==s&&(n=!0,i="buffer",r=s)}var a=t.objectMode?1:r.length;t.length+=a;var c=t.length<t.highWaterMark;c||(t.needDrain=!0);if(t.writing||t.corked){var f=t.lastBufferedRequest;t.lastBufferedRequest={chunk:r,encoding:i,isBuf:n,callback:o,next:null},f?f.next=t.lastBufferedRequest:t.bufferedRequest=t.lastBufferedRequest,t.bufferedRequestCount+=1}else I(e,t,!1,a,r,i,o);return c}(this,o,a,e,t,n)),s},A.prototype.cork=function(){this._writableState.corked++},A.prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--,e.writing||e.corked||e.bufferProcessing||!e.bufferedRequest||O(this,e))},A.prototype.setDefaultEncoding=function(e){if("string"==typeof e&&(e=e.toLowerCase()),!(["hex","utf8","utf-8","ascii","binary","base64","ucs2","ucs-2","utf16le","utf-16le","raw"].indexOf((e+"").toLowerCase())>-1))throw new _(e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(A.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(A.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),A.prototype._write=function(e,t,n){n(new v("_write()"))},A.prototype._writev=null,A.prototype.end=function(e,t,n){var i=this._writableState;return"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!=e&&this.write(e,t),i.corked&&(i.corked=1,this.uncork()),i.ending||function(e,t,n){t.ending=!0,T(e,t),n&&(t.finished?r.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,i,n),this},Object.defineProperty(A.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(A.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),A.prototype.destroy=l.destroy,A.prototype._undestroy=l.undestroy,A.prototype._destroy=function(e,t){t(e)}}).call(this,n(31),n(20))},function(e,t,n){"use strict";e.exports=f;var r=n(67).codes,i=r.ERR_METHOD_NOT_IMPLEMENTED,o=r.ERR_MULTIPLE_CALLBACK,s=r.ERR_TRANSFORM_ALREADY_TRANSFORMING,a=r.ERR_TRANSFORM_WITH_LENGTH_0,u=n(68);function c(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(null===r)return this.emit("error",new o);n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length<i.highWaterMark)&&this._read(i.highWaterMark)}function f(e){if(!(this instanceof f))return new f(e);u.call(this,e),this._transformState={afterTransform:c.bind(this),needTransform:!1,transforming:!1,writecb:null,writechunk:null,writeencoding:null},this._readableState.needReadable=!0,this._readableState.sync=!1,e&&("function"==typeof e.transform&&(this._transform=e.transform),"function"==typeof e.flush&&(this._flush=e.flush)),this.on("prefinish",l)}function l(){var e=this;"function"!=typeof this._flush||this._readableState.destroyed?d(this,null,null):this._flush((function(t,n){d(e,t,n)}))}function d(e,t,n){if(t)return e.emit("error",t);if(null!=n&&e.push(n),e._writableState.length)throw new a;if(e._transformState.transforming)throw new s;return e.push(null)}n(7)(f,u),f.prototype.push=function(e,t){return this._transformState.needTransform=!1,u.prototype.push.call(this,e,t)},f.prototype._transform=function(e,t,n){n(new i("_transform()"))},f.prototype._write=function(e,t,n){var r=this._transformState;if(r.writecb=n,r.writechunk=e,r.writeencoding=t,!r.transforming){var i=this._readableState;(r.needTransform||i.needReadable||i.length<i.highWaterMark)&&this._read(i.highWaterMark)}},f.prototype._read=function(e){var t=this._transformState;null===t.writechunk||t.transforming?t.needTransform=!0:(t.transforming=!0,this._transform(t.writechunk,t.writeencoding,t.afterTransform))},f.prototype._destroy=function(e,t){u.prototype._destroy.call(this,e,(function(e){t(e)}))}},function(e,t,n){var r=n(7),i=n(69),o=n(8).Buffer,s=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],a=new Array(64);function u(){this.init(),this._w=a,i.call(this,64,56)}function c(e,t,n){return n^e&(t^n)}function f(e,t,n){return e&t|n&(e|t)}function l(e){return(e>>>2|e<<30)^(e>>>13|e<<19)^(e>>>22|e<<10)}function d(e){return(e>>>6|e<<26)^(e>>>11|e<<21)^(e>>>25|e<<7)}function h(e){return(e>>>7|e<<25)^(e>>>18|e<<14)^e>>>3}r(u,i),u.prototype.init=function(){return this._a=1779033703,this._b=3144134277,this._c=1013904242,this._d=2773480762,this._e=1359893119,this._f=2600822924,this._g=528734635,this._h=1541459225,this},u.prototype._update=function(e){for(var t,n=this._w,r=0|this._a,i=0|this._b,o=0|this._c,a=0|this._d,u=0|this._e,p=0|this._f,v=0|this._g,g=0|this._h,m=0;m<16;++m)n[m]=e.readInt32BE(4*m);for(;m<64;++m)n[m]=0|(((t=n[m-2])>>>17|t<<15)^(t>>>19|t<<13)^t>>>10)+n[m-7]+h(n[m-15])+n[m-16];for(var b=0;b<64;++b){var y=g+d(u)+c(u,p,v)+s[b]+n[b]|0,w=l(r)+f(r,i,o)|0;g=v,v=p,p=u,u=a+y|0,a=o,o=i,i=r,r=y+w|0}this._a=r+this._a|0,this._b=i+this._b|0,this._c=o+this._c|0,this._d=a+this._d|0,this._e=u+this._e|0,this._f=p+this._f|0,this._g=v+this._g|0,this._h=g+this._h|0},u.prototype._hash=function(){var e=o.allocUnsafe(32);return e.writeInt32BE(this._a,0),e.writeInt32BE(this._b,4),e.writeInt32BE(this._c,8),e.writeInt32BE(this._d,12),e.writeInt32BE(this._e,16),e.writeInt32BE(this._f,20),e.writeInt32BE(this._g,24),e.writeInt32BE(this._h,28),e},e.exports=u},function(e,t,n){var r=n(7),i=n(69),o=n(8).Buffer,s=[1116352408,3609767458,1899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556,3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403,1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344,2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,685471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816402316,1246189591],a=new Array(160);function u(){this.init(),this._w=a,i.call(this,128,112)}function c(e,t,n){return n^e&(t^n)}function f(e,t,n){return e&t|n&(e|t)}function l(e,t){return(e>>>28|t<<4)^(t>>>2|e<<30)^(t>>>7|e<<25)}function d(e,t){return(e>>>14|t<<18)^(e>>>18|t<<14)^(t>>>9|e<<23)}function h(e,t){return(e>>>1|t<<31)^(e>>>8|t<<24)^e>>>7}function p(e,t){return(e>>>1|t<<31)^(e>>>8|t<<24)^(e>>>7|t<<25)}function v(e,t){return(e>>>19|t<<13)^(t>>>29|e<<3)^e>>>6}function g(e,t){return(e>>>19|t<<13)^(t>>>29|e<<3)^(e>>>6|t<<26)}function m(e,t){return e>>>0<t>>>0?1:0}r(u,i),u.prototype.init=function(){return this._ah=1779033703,this._bh=3144134277,this._ch=1013904242,this._dh=2773480762,this._eh=1359893119,this._fh=2600822924,this._gh=528734635,this._hh=1541459225,this._al=4089235720,this._bl=2227873595,this._cl=4271175723,this._dl=1595750129,this._el=2917565137,this._fl=725511199,this._gl=4215389547,this._hl=327033209,this},u.prototype._update=function(e){for(var t=this._w,n=0|this._ah,r=0|this._bh,i=0|this._ch,o=0|this._dh,a=0|this._eh,u=0|this._fh,b=0|this._gh,y=0|this._hh,w=0|this._al,_=0|this._bl,S=0|this._cl,E=0|this._dl,M=0|this._el,A=0|this._fl,I=0|this._gl,k=0|this._hl,O=0;O<32;O+=2)t[O]=e.readInt32BE(4*O),t[O+1]=e.readInt32BE(4*O+4);for(;O<160;O+=2){var x=t[O-30],C=t[O-30+1],T=h(x,C),P=p(C,x),N=v(x=t[O-4],C=t[O-4+1]),R=g(C,x),L=t[O-14],j=t[O-14+1],D=t[O-32],U=t[O-32+1],B=P+j|0,F=T+L+m(B,P)|0;F=(F=F+N+m(B=B+R|0,R)|0)+D+m(B=B+U|0,U)|0,t[O]=F,t[O+1]=B}for(var z=0;z<160;z+=2){F=t[z],B=t[z+1];var q=f(n,r,i),K=f(w,_,S),H=l(n,w),V=l(w,n),G=d(a,M),W=d(M,a),$=s[z],Y=s[z+1],J=c(a,u,b),Z=c(M,A,I),X=k+W|0,Q=y+G+m(X,k)|0;Q=(Q=(Q=Q+J+m(X=X+Z|0,Z)|0)+$+m(X=X+Y|0,Y)|0)+F+m(X=X+B|0,B)|0;var ee=V+K|0,te=H+q+m(ee,V)|0;y=b,k=I,b=u,I=A,u=a,A=M,a=o+Q+m(M=E+X|0,E)|0,o=i,E=S,i=r,S=_,r=n,_=w,n=Q+te+m(w=X+ee|0,X)|0}this._al=this._al+w|0,this._bl=this._bl+_|0,this._cl=this._cl+S|0,this._dl=this._dl+E|0,this._el=this._el+M|0,this._fl=this._fl+A|0,this._gl=this._gl+I|0,this._hl=this._hl+k|0,this._ah=this._ah+n+m(this._al,w)|0,this._bh=this._bh+r+m(this._bl,_)|0,this._ch=this._ch+i+m(this._cl,S)|0,this._dh=this._dh+o+m(this._dl,E)|0,this._eh=this._eh+a+m(this._el,M)|0,this._fh=this._fh+u+m(this._fl,A)|0,this._gh=this._gh+b+m(this._gl,I)|0,this._hh=this._hh+y+m(this._hl,k)|0},u.prototype._hash=function(){var e=o.allocUnsafe(64);function t(t,n,r){e.writeInt32BE(t,r),e.writeInt32BE(n,r+4)}return t(this._ah,this._al,0),t(this._bh,this._bl,8),t(this._ch,this._cl,16),t(this._dh,this._dl,24),t(this._eh,this._el,32),t(this._fh,this._fl,40),t(this._gh,this._gl,48),t(this._hh,this._hl,56),e},e.exports=u},function(e,t,n){"use strict";(function(t,r){var i=n(92);e.exports=y;var o,s=n(160);y.ReadableState=b;n(49).EventEmitter;var a=function(e,t){return e.listeners(t).length},u=n(172),c=n(119).Buffer,f=t.Uint8Array||function(){};var l=Object.create(n(80));l.inherits=n(7);var d=n(286),h=void 0;h=d&&d.debuglog?d.debuglog("stream"):function(){};var p,v=n(287),g=n(173);l.inherits(y,u);var m=["error","close","destroy","pause","resume"];function b(e,t){e=e||{};var r=t instanceof(o=o||n(60));this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.readableObjectMode);var i=e.highWaterMark,s=e.readableHighWaterMark,a=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:r&&(s||0===s)?s:a,this.highWaterMark=Math.floor(this.highWaterMark),this.buffer=new v,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(p||(p=n(59).StringDecoder),this.decoder=new p(e.encoding),this.encoding=e.encoding)}function y(e){if(o=o||n(60),!(this instanceof y))return new y(e);this._readableState=new b(e,this),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),u.call(this)}function w(e,t,n,r,i){var o,s=e._readableState;null===t?(s.reading=!1,function(e,t){if(t.ended)return;if(t.decoder){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,E(e)}(e,s)):(i||(o=function(e,t){var n;r=t,c.isBuffer(r)||r instanceof f||"string"==typeof t||void 0===t||e.objectMode||(n=new TypeError("Invalid non-string/buffer chunk"));var r;return n}(s,t)),o?e.emit("error",o):s.objectMode||t&&t.length>0?("string"==typeof t||s.objectMode||Object.getPrototypeOf(t)===c.prototype||(t=function(e){return c.from(e)}(t)),r?s.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):_(e,s,t,!0):s.ended?e.emit("error",new Error("stream.push() after EOF")):(s.reading=!1,s.decoder&&!n?(t=s.decoder.write(t),s.objectMode||0!==t.length?_(e,s,t,!1):A(e,s)):_(e,s,t,!1))):r||(s.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.length<e.highWaterMark||0===e.length)}(s)}function _(e,t,n,r){t.flowing&&0===t.length&&!t.sync?(e.emit("data",n),e.read(0)):(t.length+=t.objectMode?1:n.length,r?t.buffer.unshift(n):t.buffer.push(n),t.needReadable&&E(e)),A(e,t)}Object.defineProperty(y.prototype,"destroyed",{get:function(){return void 0!==this._readableState&&this._readableState.destroyed},set:function(e){this._readableState&&(this._readableState.destroyed=e)}}),y.prototype.destroy=g.destroy,y.prototype._undestroy=g.undestroy,y.prototype._destroy=function(e,t){this.push(null),t(e)},y.prototype.push=function(e,t){var n,r=this._readableState;return r.objectMode?n=!0:"string"==typeof e&&((t=t||r.defaultEncoding)!==r.encoding&&(e=c.from(e,t),t=""),n=!0),w(this,e,t,!1,n)},y.prototype.unshift=function(e){return w(this,e,null,!0,!1)},y.prototype.isPaused=function(){return!1===this._readableState.flowing},y.prototype.setEncoding=function(e){return p||(p=n(59).StringDecoder),this._readableState.decoder=new p(e),this._readableState.encoding=e,this};function S(e,t){return e<=0||0===t.length&&t.ended?0:t.objectMode?1:e!=e?t.flowing&&t.length?t.buffer.head.data.length:t.length:(e>t.highWaterMark&&(t.highWaterMark=function(e){return e>=8388608?e=8388608:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function E(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(h("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?i.nextTick(M,e):M(e))}function M(e){h("emit readable"),e.emit("readable"),x(e)}function A(e,t){t.readingMore||(t.readingMore=!0,i.nextTick(I,e,t))}function I(e,t){for(var n=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length<t.highWaterMark&&(h("maybeReadMore read 0"),e.read(0),n!==t.length);)n=t.length;t.readingMore=!1}function k(e){h("readable nexttick read 0"),e.read(0)}function O(e,t){t.reading||(h("resume read 0"),e.read(0)),t.resumeScheduled=!1,t.awaitDrain=0,e.emit("resume"),x(e),t.flowing&&!t.reading&&e.read(0)}function x(e){var t=e._readableState;for(h("flow",t.flowing);t.flowing&&null!==e.read(););}function C(e,t){return 0===t.length?null:(t.objectMode?n=t.buffer.shift():!e||e>=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):n=function(e,t,n){var r;e<t.head.data.length?(r=t.head.data.slice(0,e),t.head.data=t.head.data.slice(e)):r=e===t.head.data.length?t.shift():n?function(e,t){var n=t.head,r=1,i=n.data;e-=i.length;for(;n=n.next;){var o=n.data,s=e>o.length?o.length:e;if(s===o.length?i+=o:i+=o.slice(0,e),0===(e-=s)){s===o.length?(++r,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=o.slice(s));break}++r}return t.length-=r,i}(e,t):function(e,t){var n=c.allocUnsafe(e),r=t.head,i=1;r.data.copy(n),e-=r.data.length;for(;r=r.next;){var o=r.data,s=e>o.length?o.length:e;if(o.copy(n,n.length-e,0,s),0===(e-=s)){s===o.length?(++i,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=o.slice(s));break}++i}return t.length-=i,n}(e,t);return r}(e,t.buffer,t.decoder),n);var n}function T(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,i.nextTick(P,t,e))}function P(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function N(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1}y.prototype.read=function(e){h("read",e),e=parseInt(e,10);var t=this._readableState,n=e;if(0!==e&&(t.emittedReadable=!1),0===e&&t.needReadable&&(t.length>=t.highWaterMark||t.ended))return h("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?T(this):E(this),null;if(0===(e=S(e,t))&&t.ended)return 0===t.length&&T(this),null;var r,i=t.needReadable;return h("need readable",i),(0===t.length||t.length-e<t.highWaterMark)&&h("length less than watermark",i=!0),t.ended||t.reading?h("reading or ended",i=!1):i&&(h("do read"),t.reading=!0,t.sync=!0,0===t.length&&(t.needReadable=!0),this._read(t.highWaterMark),t.sync=!1,t.reading||(e=S(n,t))),null===(r=e>0?C(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&T(this)),null!==r&&this.emit("data",r),r},y.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},y.prototype.pipe=function(e,t){var n=this,o=this._readableState;switch(o.pipesCount){case 0:o.pipes=e;break;case 1:o.pipes=[o.pipes,e];break;default:o.pipes.push(e)}o.pipesCount+=1,h("pipe count=%d opts=%j",o.pipesCount,t);var u=(!t||!1!==t.end)&&e!==r.stdout&&e!==r.stderr?f:y;function c(t,r){h("onunpipe"),t===n&&r&&!1===r.hasUnpiped&&(r.hasUnpiped=!0,h("cleanup"),e.removeListener("close",m),e.removeListener("finish",b),e.removeListener("drain",l),e.removeListener("error",g),e.removeListener("unpipe",c),n.removeListener("end",f),n.removeListener("end",y),n.removeListener("data",v),d=!0,!o.awaitDrain||e._writableState&&!e._writableState.needDrain||l())}function f(){h("onend"),e.end()}o.endEmitted?i.nextTick(u):n.once("end",u),e.on("unpipe",c);var l=function(e){return function(){var t=e._readableState;h("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&a(e,"data")&&(t.flowing=!0,x(e))}}(n);e.on("drain",l);var d=!1;var p=!1;function v(t){h("ondata"),p=!1,!1!==e.write(t)||p||((1===o.pipesCount&&o.pipes===e||o.pipesCount>1&&-1!==N(o.pipes,e))&&!d&&(h("false write response, pause",n._readableState.awaitDrain),n._readableState.awaitDrain++,p=!0),n.pause())}function g(t){h("onerror",t),y(),e.removeListener("error",g),0===a(e,"error")&&e.emit("error",t)}function m(){e.removeListener("finish",b),y()}function b(){h("onfinish"),e.removeListener("close",m),y()}function y(){h("unpipe"),n.unpipe(e)}return n.on("data",v),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?s(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",g),e.once("close",m),e.once("finish",b),e.emit("pipe",n),o.flowing||(h("pipe resume"),n.resume()),e},y.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes||(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n)),this;if(!e){var r=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var o=0;o<i;o++)r[o].emit("unpipe",this,n);return this}var s=N(t.pipes,e);return-1===s||(t.pipes.splice(s,1),t.pipesCount-=1,1===t.pipesCount&&(t.pipes=t.pipes[0]),e.emit("unpipe",this,n)),this},y.prototype.on=function(e,t){var n=u.prototype.on.call(this,e,t);if("data"===e)!1!==this._readableState.flowing&&this.resume();else if("readable"===e){var r=this._readableState;r.endEmitted||r.readableListening||(r.readableListening=r.needReadable=!0,r.emittedReadable=!1,r.reading?r.length&&E(this):i.nextTick(k,this))}return n},y.prototype.addListener=y.prototype.on,y.prototype.resume=function(){var e=this._readableState;return e.flowing||(h("resume"),e.flowing=!0,function(e,t){t.resumeScheduled||(t.resumeScheduled=!0,i.nextTick(O,e,t))}(this,e)),this},y.prototype.pause=function(){return h("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(h("pause"),this._readableState.flowing=!1,this.emit("pause")),this},y.prototype.wrap=function(e){var t=this,n=this._readableState,r=!1;for(var i in e.on("end",(function(){if(h("wrapped end"),n.decoder&&!n.ended){var e=n.decoder.end();e&&e.length&&t.push(e)}t.push(null)})),e.on("data",(function(i){(h("wrapped data"),n.decoder&&(i=n.decoder.write(i)),n.objectMode&&null==i)||(n.objectMode||i&&i.length)&&(t.push(i)||(r=!0,e.pause()))})),e)void 0===this[i]&&"function"==typeof e[i]&&(this[i]=function(t){return function(){return e[t].apply(e,arguments)}}(i));for(var o=0;o<m.length;o++)e.on(m[o],this.emit.bind(this,m[o]));return this._read=function(t){h("wrapped _read",t),r&&(r=!1,e.resume())},this},Object.defineProperty(y.prototype,"readableHighWaterMark",{enumerable:!1,get:function(){return this._readableState.highWaterMark}}),y._fromList=C}).call(this,n(31),n(20))},function(e,t,n){e.exports=n(49).EventEmitter},function(e,t,n){"use strict";var r=n(92);function i(e,t){e.emit("error",t)}e.exports={destroy:function(e,t){var n=this,o=this._readableState&&this._readableState.destroyed,s=this._writableState&&this._writableState.destroyed;return o||s?(t?t(e):!e||this._writableState&&this._writableState.errorEmitted||r.nextTick(i,this,e),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,(function(e){!t&&e?(r.nextTick(i,n,e),n._writableState&&(n._writableState.errorEmitted=!0)):t&&t(e)})),this)},undestroy:function(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)}}},function(e,t,n){"use strict";e.exports=s;var r=n(60),i=Object.create(n(80));function o(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(!r)return this.emit("error",new Error("write callback called multiple times"));n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length<i.highWaterMark)&&this._read(i.highWaterMark)}function s(e){if(!(this instanceof s))return new s(e);r.call(this,e),this._transformState={afterTransform:o.bind(this),needTransform:!1,transforming:!1,writecb:null,writechunk:null,writeencoding:null},this._readableState.needReadable=!0,this._readableState.sync=!1,e&&("function"==typeof e.transform&&(this._transform=e.transform),"function"==typeof e.flush&&(this._flush=e.flush)),this.on("prefinish",a)}function a(){var e=this;"function"==typeof this._flush?this._flush((function(t,n){u(e,t,n)})):u(this,null,null)}function u(e,t,n){if(t)return e.emit("error",t);if(null!=n&&e.push(n),e._writableState.length)throw new Error("Calling transform done when ws.length != 0");if(e._transformState.transforming)throw new Error("Calling transform done when still transforming");return e.push(null)}i.inherits=n(7),i.inherits(s,r),s.prototype.push=function(e,t){return this._transformState.needTransform=!1,r.prototype.push.call(this,e,t)},s.prototype._transform=function(e,t,n){throw new Error("_transform() is not implemented")},s.prototype._write=function(e,t,n){var r=this._transformState;if(r.writecb=n,r.writechunk=e,r.writeencoding=t,!r.transforming){var i=this._readableState;(r.needTransform||i.needReadable||i.length<i.highWaterMark)&&this._read(i.highWaterMark)}},s.prototype._read=function(e){var t=this._transformState;null!==t.writechunk&&t.writecb&&!t.transforming?(t.transforming=!0,this._transform(t.writechunk,t.writeencoding,t.afterTransform)):t.needTransform=!0},s.prototype._destroy=function(e,t){var n=this;r.prototype._destroy.call(this,e,(function(e){t(e),n.emit("close")}))}},function(e,t,n){"use strict";var r=n(7),i=n(296),o=n(56),s=n(8).Buffer,a=n(176),u=n(116),c=n(117),f=s.alloc(128);function l(e,t){o.call(this,"digest"),"string"==typeof t&&(t=s.from(t));var n="sha512"===e||"sha384"===e?128:64;(this._alg=e,this._key=t,t.length>n)?t=("rmd160"===e?new u:c(e)).update(t).digest():t.length<n&&(t=s.concat([t,f],n));for(var r=this._ipad=s.allocUnsafe(n),i=this._opad=s.allocUnsafe(n),a=0;a<n;a++)r[a]=54^t[a],i[a]=92^t[a];this._hash="rmd160"===e?new u:c(e),this._hash.update(r)}r(l,o),l.prototype._update=function(e){this._hash.update(e)},l.prototype._final=function(){var e=this._hash.digest();return("rmd160"===this._alg?new u:c(this._alg)).update(this._opad).update(e).digest()},e.exports=function(e,t){return"rmd160"===(e=e.toLowerCase())||"ripemd160"===e?new l("rmd160",t):"md5"===e?new i(a,t):new l(e,t)}},function(e,t,n){var r=n(113);e.exports=function(e){return(new r).update(e).digest()}},function(e){e.exports=JSON.parse('{"sha224WithRSAEncryption":{"sign":"rsa","hash":"sha224","id":"302d300d06096086480165030402040500041c"},"RSA-SHA224":{"sign":"ecdsa/rsa","hash":"sha224","id":"302d300d06096086480165030402040500041c"},"sha256WithRSAEncryption":{"sign":"rsa","hash":"sha256","id":"3031300d060960864801650304020105000420"},"RSA-SHA256":{"sign":"ecdsa/rsa","hash":"sha256","id":"3031300d060960864801650304020105000420"},"sha384WithRSAEncryption":{"sign":"rsa","hash":"sha384","id":"3041300d060960864801650304020205000430"},"RSA-SHA384":{"sign":"ecdsa/rsa","hash":"sha384","id":"3041300d060960864801650304020205000430"},"sha512WithRSAEncryption":{"sign":"rsa","hash":"sha512","id":"3051300d060960864801650304020305000440"},"RSA-SHA512":{"sign":"ecdsa/rsa","hash":"sha512","id":"3051300d060960864801650304020305000440"},"RSA-SHA1":{"sign":"rsa","hash":"sha1","id":"3021300906052b0e03021a05000414"},"ecdsa-with-SHA1":{"sign":"ecdsa","hash":"sha1","id":""},"sha256":{"sign":"ecdsa","hash":"sha256","id":""},"sha224":{"sign":"ecdsa","hash":"sha224","id":""},"sha384":{"sign":"ecdsa","hash":"sha384","id":""},"sha512":{"sign":"ecdsa","hash":"sha512","id":""},"DSA-SHA":{"sign":"dsa","hash":"sha1","id":""},"DSA-SHA1":{"sign":"dsa","hash":"sha1","id":""},"DSA":{"sign":"dsa","hash":"sha1","id":""},"DSA-WITH-SHA224":{"sign":"dsa","hash":"sha224","id":""},"DSA-SHA224":{"sign":"dsa","hash":"sha224","id":""},"DSA-WITH-SHA256":{"sign":"dsa","hash":"sha256","id":""},"DSA-SHA256":{"sign":"dsa","hash":"sha256","id":""},"DSA-WITH-SHA384":{"sign":"dsa","hash":"sha384","id":""},"DSA-SHA384":{"sign":"dsa","hash":"sha384","id":""},"DSA-WITH-SHA512":{"sign":"dsa","hash":"sha512","id":""},"DSA-SHA512":{"sign":"dsa","hash":"sha512","id":""},"DSA-RIPEMD160":{"sign":"dsa","hash":"rmd160","id":""},"ripemd160WithRSA":{"sign":"rsa","hash":"rmd160","id":"3021300906052b2403020105000414"},"RSA-RIPEMD160":{"sign":"rsa","hash":"rmd160","id":"3021300906052b2403020105000414"},"md5WithRSAEncryption":{"sign":"rsa","hash":"md5","id":"3020300c06082a864886f70d020505000410"},"RSA-MD5":{"sign":"rsa","hash":"md5","id":"3020300c06082a864886f70d020505000410"}}')},function(e,t,n){t.pbkdf2=n(298),t.pbkdf2Sync=n(181)},function(e,t){var n=Math.pow(2,30)-1;e.exports=function(e,t){if("number"!=typeof e)throw new TypeError("Iterations not a number");if(e<0)throw new TypeError("Bad iterations");if("number"!=typeof t)throw new TypeError("Key length not a number");if(t<0||t>n||t!=t)throw new TypeError("Bad key length")}},function(e,t,n){(function(t){var n;if(t.browser)n="utf-8";else if(t.version){n=parseInt(t.version.split(".")[0].slice(1),10)>=6?"utf-8":"binary"}else n="utf-8";e.exports=n}).call(this,n(20))},function(e,t,n){var r=n(176),i=n(116),o=n(117),s=n(8).Buffer,a=n(179),u=n(180),c=n(182),f=s.alloc(128),l={md5:16,sha1:20,sha224:28,sha256:32,sha384:48,sha512:64,rmd160:20,ripemd160:20};function d(e,t,n){var a=function(e){function t(t){return o(e).update(t).digest()}return"rmd160"===e||"ripemd160"===e?function(e){return(new i).update(e).digest()}:"md5"===e?r:t}(e),u="sha512"===e||"sha384"===e?128:64;t.length>u?t=a(t):t.length<u&&(t=s.concat([t,f],u));for(var c=s.allocUnsafe(u+l[e]),d=s.allocUnsafe(u+l[e]),h=0;h<u;h++)c[h]=54^t[h],d[h]=92^t[h];var p=s.allocUnsafe(u+n+4);c.copy(p,0,0,u),this.ipad1=p,this.ipad2=c,this.opad=d,this.alg=e,this.blocksize=u,this.hash=a,this.size=l[e]}d.prototype.run=function(e,t){return e.copy(t,this.blocksize),this.hash(t).copy(this.opad,this.blocksize),this.hash(this.opad)},e.exports=function(e,t,n,r,i){a(n,r);var o=new d(i=i||"sha1",e=c(e,u,"Password"),(t=c(t,u,"Salt")).length),f=s.allocUnsafe(r),h=s.allocUnsafe(t.length+4);t.copy(h,0,0,t.length);for(var p=0,v=l[i],g=Math.ceil(r/v),m=1;m<=g;m++){h.writeUInt32BE(m,t.length);for(var b=o.run(h,o.ipad1),y=b,w=1;w<n;w++){y=o.run(y,o.ipad2);for(var _=0;_<v;_++)b[_]^=y[_]}b.copy(f,p),p+=v}return f}},function(e,t,n){var r=n(8).Buffer;e.exports=function(e,t,n){if(r.isBuffer(e))return e;if("string"==typeof e)return r.from(e,t);if(ArrayBuffer.isView(e))return r.from(e.buffer);throw new TypeError(n+" must be a string, a Buffer, a typed array or a DataView")}},function(e,t,n){"use strict";t.readUInt32BE=function(e,t){return(e[0+t]<<24|e[1+t]<<16|e[2+t]<<8|e[3+t])>>>0},t.writeUInt32BE=function(e,t,n){e[0+n]=t>>>24,e[1+n]=t>>>16&255,e[2+n]=t>>>8&255,e[3+n]=255&t},t.ip=function(e,t,n,r){for(var i=0,o=0,s=6;s>=0;s-=2){for(var a=0;a<=24;a+=8)i<<=1,i|=t>>>a+s&1;for(a=0;a<=24;a+=8)i<<=1,i|=e>>>a+s&1}for(s=6;s>=0;s-=2){for(a=1;a<=25;a+=8)o<<=1,o|=t>>>a+s&1;for(a=1;a<=25;a+=8)o<<=1,o|=e>>>a+s&1}n[r+0]=i>>>0,n[r+1]=o>>>0},t.rip=function(e,t,n,r){for(var i=0,o=0,s=0;s<4;s++)for(var a=24;a>=0;a-=8)i<<=1,i|=t>>>a+s&1,i<<=1,i|=e>>>a+s&1;for(s=4;s<8;s++)for(a=24;a>=0;a-=8)o<<=1,o|=t>>>a+s&1,o<<=1,o|=e>>>a+s&1;n[r+0]=i>>>0,n[r+1]=o>>>0},t.pc1=function(e,t,n,r){for(var i=0,o=0,s=7;s>=5;s--){for(var a=0;a<=24;a+=8)i<<=1,i|=t>>a+s&1;for(a=0;a<=24;a+=8)i<<=1,i|=e>>a+s&1}for(a=0;a<=24;a+=8)i<<=1,i|=t>>a+s&1;for(s=1;s<=3;s++){for(a=0;a<=24;a+=8)o<<=1,o|=t>>a+s&1;for(a=0;a<=24;a+=8)o<<=1,o|=e>>a+s&1}for(a=0;a<=24;a+=8)o<<=1,o|=e>>a+s&1;n[r+0]=i>>>0,n[r+1]=o>>>0},t.r28shl=function(e,t){return e<<t&268435455|e>>>28-t};var r=[14,11,17,4,27,23,25,0,13,22,7,18,5,9,16,24,2,20,12,21,1,8,15,26,15,4,25,19,9,1,26,16,5,11,23,8,12,7,17,0,22,3,10,14,6,20,27,24];t.pc2=function(e,t,n,i){for(var o=0,s=0,a=r.length>>>1,u=0;u<a;u++)o<<=1,o|=e>>>r[u]&1;for(u=a;u<r.length;u++)s<<=1,s|=t>>>r[u]&1;n[i+0]=o>>>0,n[i+1]=s>>>0},t.expand=function(e,t,n){var r=0,i=0;r=(1&e)<<5|e>>>27;for(var o=23;o>=15;o-=4)r<<=6,r|=e>>>o&63;for(o=11;o>=3;o-=4)i|=e>>>o&63,i<<=6;i|=(31&e)<<1|e>>>31,t[n+0]=r>>>0,t[n+1]=i>>>0};var i=[14,0,4,15,13,7,1,4,2,14,15,2,11,13,8,1,3,10,10,6,6,12,12,11,5,9,9,5,0,3,7,8,4,15,1,12,14,8,8,2,13,4,6,9,2,1,11,7,15,5,12,11,9,3,7,14,3,10,10,0,5,6,0,13,15,3,1,13,8,4,14,7,6,15,11,2,3,8,4,14,9,12,7,0,2,1,13,10,12,6,0,9,5,11,10,5,0,13,14,8,7,10,11,1,10,3,4,15,13,4,1,2,5,11,8,6,12,7,6,12,9,0,3,5,2,14,15,9,10,13,0,7,9,0,14,9,6,3,3,4,15,6,5,10,1,2,13,8,12,5,7,14,11,12,4,11,2,15,8,1,13,1,6,10,4,13,9,0,8,6,15,9,3,8,0,7,11,4,1,15,2,14,12,3,5,11,10,5,14,2,7,12,7,13,13,8,14,11,3,5,0,6,6,15,9,0,10,3,1,4,2,7,8,2,5,12,11,1,12,10,4,14,15,9,10,3,6,15,9,0,0,6,12,10,11,1,7,13,13,8,15,9,1,4,3,5,14,11,5,12,2,7,8,2,4,14,2,14,12,11,4,2,1,12,7,4,10,7,11,13,6,1,8,5,5,0,3,15,15,10,13,3,0,9,14,8,9,6,4,11,2,8,1,12,11,7,10,1,13,14,7,2,8,13,15,6,9,15,12,0,5,9,6,10,3,4,0,5,14,3,12,10,1,15,10,4,15,2,9,7,2,12,6,9,8,5,0,6,13,1,3,13,4,14,14,0,7,11,5,3,11,8,9,4,14,3,15,2,5,12,2,9,8,5,12,15,3,10,7,11,0,14,4,1,10,7,1,6,13,0,11,8,6,13,4,13,11,0,2,11,14,7,15,4,0,9,8,1,13,10,3,14,12,3,9,5,7,12,5,2,10,15,6,8,1,6,1,6,4,11,11,13,13,8,12,1,3,4,7,10,14,7,10,9,15,5,6,0,8,15,0,14,5,2,9,3,2,12,13,1,2,15,8,13,4,8,6,10,15,3,11,7,1,4,10,12,9,5,3,6,14,11,5,0,0,14,12,9,7,2,7,2,11,1,4,14,1,7,9,4,12,10,14,8,2,13,0,15,6,12,10,9,13,0,15,3,3,5,5,6,8,11];t.substitute=function(e,t){for(var n=0,r=0;r<4;r++){n<<=4,n|=i[64*r+(e>>>18-6*r&63)]}for(r=0;r<4;r++){n<<=4,n|=i[256+64*r+(t>>>18-6*r&63)]}return n>>>0};var o=[16,25,12,11,3,20,4,15,31,17,9,6,27,14,1,22,30,24,8,18,0,5,29,23,13,19,2,26,10,21,28,7];t.permute=function(e){for(var t=0,n=0;n<o.length;n++)t<<=1,t|=e>>>o[n]&1;return t>>>0},t.padSplit=function(e,t,n){for(var r=e.toString(2);r.length<t;)r="0"+r;for(var i=[],o=0;o<t;o+=n)i.push(r.slice(o,o+n));return i.join(" ")}},function(e,t,n){"use strict";var r=n(46),i=n(7),o=n(183),s=n(121);function a(){this.tmp=new Array(2),this.keys=null}function u(e){s.call(this,e);var t=new a;this._desState=t,this.deriveKeys(t,e.key)}i(u,s),e.exports=u,u.create=function(e){return new u(e)};var c=[1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1];u.prototype.deriveKeys=function(e,t){e.keys=new Array(32),r.equal(t.length,this.blockSize,"Invalid key length");var n=o.readUInt32BE(t,0),i=o.readUInt32BE(t,4);o.pc1(n,i,e.tmp,0),n=e.tmp[0],i=e.tmp[1];for(var s=0;s<e.keys.length;s+=2){var a=c[s>>>1];n=o.r28shl(n,a),i=o.r28shl(i,a),o.pc2(n,i,e.keys,s)}},u.prototype._update=function(e,t,n,r){var i=this._desState,s=o.readUInt32BE(e,t),a=o.readUInt32BE(e,t+4);o.ip(s,a,i.tmp,0),s=i.tmp[0],a=i.tmp[1],"encrypt"===this.type?this._encrypt(i,s,a,i.tmp,0):this._decrypt(i,s,a,i.tmp,0),s=i.tmp[0],a=i.tmp[1],o.writeUInt32BE(n,s,r),o.writeUInt32BE(n,a,r+4)},u.prototype._pad=function(e,t){for(var n=e.length-t,r=t;r<e.length;r++)e[r]=n;return!0},u.prototype._unpad=function(e){for(var t=e[e.length-1],n=e.length-t;n<e.length;n++)r.equal(e[n],t);return e.slice(0,e.length-t)},u.prototype._encrypt=function(e,t,n,r,i){for(var s=t,a=n,u=0;u<e.keys.length;u+=2){var c=e.keys[u],f=e.keys[u+1];o.expand(a,e.tmp,0),c^=e.tmp[0],f^=e.tmp[1];var l=o.substitute(c,f),d=a;a=(s^o.permute(l))>>>0,s=d}o.rip(a,s,r,i)},u.prototype._decrypt=function(e,t,n,r,i){for(var s=n,a=t,u=e.keys.length-2;u>=0;u-=2){var c=e.keys[u],f=e.keys[u+1];o.expand(s,e.tmp,0),c^=e.tmp[0],f^=e.tmp[1];var l=o.substitute(c,f),d=s;s=(a^o.permute(l))>>>0,a=d}o.rip(s,a,r,i)}},function(e,t,n){var r=n(81),i=n(8).Buffer,o=n(186);function s(e){var t=e._cipher.encryptBlockRaw(e._prev);return o(e._prev),t}t.encrypt=function(e,t){var n=Math.ceil(t.length/16),o=e._cache.length;e._cache=i.concat([e._cache,i.allocUnsafe(16*n)]);for(var a=0;a<n;a++){var u=s(e),c=o+16*a;e._cache.writeUInt32BE(u[0],c+0),e._cache.writeUInt32BE(u[1],c+4),e._cache.writeUInt32BE(u[2],c+8),e._cache.writeUInt32BE(u[3],c+12)}var f=e._cache.slice(0,t.length);return e._cache=e._cache.slice(t.length),r(t,f)}},function(e,t){e.exports=function(e){for(var t,n=e.length;n--;){if(255!==(t=e.readUInt8(n))){t++,e.writeUInt8(t,n);break}e.writeUInt8(0,n)}}},function(e){e.exports=JSON.parse('{"aes-128-ecb":{"cipher":"AES","key":128,"iv":0,"mode":"ECB","type":"block"},"aes-192-ecb":{"cipher":"AES","key":192,"iv":0,"mode":"ECB","type":"block"},"aes-256-ecb":{"cipher":"AES","key":256,"iv":0,"mode":"ECB","type":"block"},"aes-128-cbc":{"cipher":"AES","key":128,"iv":16,"mode":"CBC","type":"block"},"aes-192-cbc":{"cipher":"AES","key":192,"iv":16,"mode":"CBC","type":"block"},"aes-256-cbc":{"cipher":"AES","key":256,"iv":16,"mode":"CBC","type":"block"},"aes128":{"cipher":"AES","key":128,"iv":16,"mode":"CBC","type":"block"},"aes192":{"cipher":"AES","key":192,"iv":16,"mode":"CBC","type":"block"},"aes256":{"cipher":"AES","key":256,"iv":16,"mode":"CBC","type":"block"},"aes-128-cfb":{"cipher":"AES","key":128,"iv":16,"mode":"CFB","type":"stream"},"aes-192-cfb":{"cipher":"AES","key":192,"iv":16,"mode":"CFB","type":"stream"},"aes-256-cfb":{"cipher":"AES","key":256,"iv":16,"mode":"CFB","type":"stream"},"aes-128-cfb8":{"cipher":"AES","key":128,"iv":16,"mode":"CFB8","type":"stream"},"aes-192-cfb8":{"cipher":"AES","key":192,"iv":16,"mode":"CFB8","type":"stream"},"aes-256-cfb8":{"cipher":"AES","key":256,"iv":16,"mode":"CFB8","type":"stream"},"aes-128-cfb1":{"cipher":"AES","key":128,"iv":16,"mode":"CFB1","type":"stream"},"aes-192-cfb1":{"cipher":"AES","key":192,"iv":16,"mode":"CFB1","type":"stream"},"aes-256-cfb1":{"cipher":"AES","key":256,"iv":16,"mode":"CFB1","type":"stream"},"aes-128-ofb":{"cipher":"AES","key":128,"iv":16,"mode":"OFB","type":"stream"},"aes-192-ofb":{"cipher":"AES","key":192,"iv":16,"mode":"OFB","type":"stream"},"aes-256-ofb":{"cipher":"AES","key":256,"iv":16,"mode":"OFB","type":"stream"},"aes-128-ctr":{"cipher":"AES","key":128,"iv":16,"mode":"CTR","type":"stream"},"aes-192-ctr":{"cipher":"AES","key":192,"iv":16,"mode":"CTR","type":"stream"},"aes-256-ctr":{"cipher":"AES","key":256,"iv":16,"mode":"CTR","type":"stream"},"aes-128-gcm":{"cipher":"AES","key":128,"iv":12,"mode":"GCM","type":"auth"},"aes-192-gcm":{"cipher":"AES","key":192,"iv":12,"mode":"GCM","type":"auth"},"aes-256-gcm":{"cipher":"AES","key":256,"iv":12,"mode":"GCM","type":"auth"}}')},function(e,t,n){var r=n(93),i=n(8).Buffer,o=n(56),s=n(7),a=n(311),u=n(81),c=n(186);function f(e,t,n,s){o.call(this);var u=i.alloc(4,0);this._cipher=new r.AES(t);var f=this._cipher.encryptBlock(u);this._ghash=new a(f),n=function(e,t,n){if(12===t.length)return e._finID=i.concat([t,i.from([0,0,0,1])]),i.concat([t,i.from([0,0,0,2])]);var r=new a(n),o=t.length,s=o%16;r.update(t),s&&(s=16-s,r.update(i.alloc(s,0))),r.update(i.alloc(8,0));var u=8*o,f=i.alloc(8);f.writeUIntBE(u,0,8),r.update(f),e._finID=r.state;var l=i.from(e._finID);return c(l),l}(this,n,f),this._prev=i.from(n),this._cache=i.allocUnsafe(0),this._secCache=i.allocUnsafe(0),this._decrypt=s,this._alen=0,this._len=0,this._mode=e,this._authTag=null,this._called=!1}s(f,o),f.prototype._update=function(e){if(!this._called&&this._alen){var t=16-this._alen%16;t<16&&(t=i.alloc(t,0),this._ghash.update(t))}this._called=!0;var n=this._mode.encrypt(this,e);return this._decrypt?this._ghash.update(e):this._ghash.update(n),this._len+=e.length,n},f.prototype._final=function(){if(this._decrypt&&!this._authTag)throw new Error("Unsupported state or unable to authenticate data");var e=u(this._ghash.final(8*this._alen,8*this._len),this._cipher.encryptBlock(this._finID));if(this._decrypt&&function(e,t){var n=0;e.length!==t.length&&n++;for(var r=Math.min(e.length,t.length),i=0;i<r;++i)n+=e[i]^t[i];return n}(e,this._authTag))throw new Error("Unsupported state or unable to authenticate data");this._authTag=e,this._cipher.scrub()},f.prototype.getAuthTag=function(){if(this._decrypt||!i.isBuffer(this._authTag))throw new Error("Attempting to get auth tag in unsupported state");return this._authTag},f.prototype.setAuthTag=function(e){if(!this._decrypt)throw new Error("Attempting to set auth tag in unsupported state");this._authTag=e},f.prototype.setAAD=function(e){if(this._called)throw new Error("Attempting to set AAD in unsupported state");this._ghash.update(e),this._alen+=e.length},e.exports=f},function(e,t,n){var r=n(93),i=n(8).Buffer,o=n(56);function s(e,t,n,s){o.call(this),this._cipher=new r.AES(t),this._prev=i.from(n),this._cache=i.allocUnsafe(0),this._secCache=i.allocUnsafe(0),this._decrypt=s,this._mode=e}n(7)(s,o),s.prototype._update=function(e){return this._mode.encrypt(this,e,this._decrypt)},s.prototype._final=function(){this._cipher.scrub()},e.exports=s},function(e,t,n){var r=n(66);e.exports=b,b.simpleSieve=g,b.fermatTest=m;var i=n(29),o=new i(24),s=new(n(191)),a=new i(1),u=new i(2),c=new i(5),f=(new i(16),new i(8),new i(10)),l=new i(3),d=(new i(7),new i(11)),h=new i(4),p=(new i(12),null);function v(){if(null!==p)return p;var e=[];e[0]=2;for(var t=1,n=3;n<1048576;n+=2){for(var r=Math.ceil(Math.sqrt(n)),i=0;i<t&&e[i]<=r&&n%e[i]!=0;i++);t!==i&&e[i]<=r||(e[t++]=n)}return p=e,e}function g(e){for(var t=v(),n=0;n<t.length;n++)if(0===e.modn(t[n]))return 0===e.cmpn(t[n]);return!0}function m(e){var t=i.mont(e);return 0===u.toRed(t).redPow(e.subn(1)).fromRed().cmpn(1)}function b(e,t){if(e<16)return new i(2===t||5===t?[140,123]:[140,39]);var n,p;for(t=new i(t);;){for(n=new i(r(Math.ceil(e/8)));n.bitLength()>e;)n.ishrn(1);if(n.isEven()&&n.iadd(a),n.testn(1)||n.iadd(u),t.cmp(u)){if(!t.cmp(c))for(;n.mod(f).cmp(l);)n.iadd(h)}else for(;n.mod(o).cmp(d);)n.iadd(h);if(g(p=n.shrn(1))&&g(n)&&m(p)&&m(n)&&s.test(p)&&s.test(n))return n}}},function(e,t,n){var r=n(29),i=n(124);function o(e){this.rand=e||new i.Rand}e.exports=o,o.create=function(e){return new o(e)},o.prototype._randbelow=function(e){var t=e.bitLength(),n=Math.ceil(t/8);do{var i=new r(this.rand.generate(n))}while(i.cmp(e)>=0);return i},o.prototype._randrange=function(e,t){var n=t.sub(e);return e.add(this._randbelow(n))},o.prototype.test=function(e,t,n){var i=e.bitLength(),o=r.mont(e),s=new r(1).toRed(o);t||(t=Math.max(1,i/48|0));for(var a=e.subn(1),u=0;!a.testn(u);u++);for(var c=e.shrn(u),f=a.toRed(o);t>0;t--){var l=this._randrange(new r(2),a);n&&n(l);var d=l.toRed(o).redPow(c);if(0!==d.cmp(s)&&0!==d.cmp(f)){for(var h=1;h<u;h++){if(0===(d=d.redSqr()).cmp(s))return!1;if(0===d.cmp(f))break}if(h===u)return!1}}return!0},o.prototype.getDivisor=function(e,t){var n=e.bitLength(),i=r.mont(e),o=new r(1).toRed(i);t||(t=Math.max(1,n/48|0));for(var s=e.subn(1),a=0;!s.testn(a);a++);for(var u=e.shrn(a),c=s.toRed(i);t>0;t--){var f=this._randrange(new r(2),s),l=e.gcd(f);if(0!==l.cmpn(1))return l;var d=f.toRed(i).redPow(u);if(0!==d.cmp(o)&&0!==d.cmp(c)){for(var h=1;h<a;h++){if(0===(d=d.redSqr()).cmp(o))return d.fromRed().subn(1).gcd(e);if(0===d.cmp(c))break}if(h===a)return(d=d.redSqr()).fromRed().subn(1).gcd(e)}}return!1}},function(e,t,n){"use strict";(function(t,r){var i;e.exports=A,A.ReadableState=M;n(49).EventEmitter;var o=function(e,t){return e.listeners(t).length},s=n(193),a=n(6).Buffer,u=t.Uint8Array||function(){};var c,f=n(321);c=f&&f.debuglog?f.debuglog("stream"):function(){};var l,d,h,p=n(322),v=n(194),g=n(195).getHighWaterMark,m=n(70).codes,b=m.ERR_INVALID_ARG_TYPE,y=m.ERR_STREAM_PUSH_AFTER_EOF,w=m.ERR_METHOD_NOT_IMPLEMENTED,_=m.ERR_STREAM_UNSHIFT_AFTER_END_EVENT;n(7)(A,s);var S=v.errorOrDestroy,E=["error","close","destroy","pause","resume"];function M(e,t,r){i=i||n(71),e=e||{},"boolean"!=typeof r&&(r=t instanceof i),this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.readableObjectMode),this.highWaterMark=g(this,e,"readableHighWaterMark",r),this.buffer=new p,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.paused=!0,this.emitClose=!1!==e.emitClose,this.autoDestroy=!!e.autoDestroy,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(l||(l=n(59).StringDecoder),this.decoder=new l(e.encoding),this.encoding=e.encoding)}function A(e){if(i=i||n(71),!(this instanceof A))return new A(e);var t=this instanceof i;this._readableState=new M(e,this,t),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),s.call(this)}function I(e,t,n,r,i){c("readableAddChunk",t);var o,s=e._readableState;if(null===t)s.reading=!1,function(e,t){if(c("onEofChunk"),t.ended)return;if(t.decoder){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.sync?x(e):(t.needReadable=!1,t.emittedReadable||(t.emittedReadable=!0,C(e)))}(e,s);else if(i||(o=function(e,t){var n;r=t,a.isBuffer(r)||r instanceof u||"string"==typeof t||void 0===t||e.objectMode||(n=new b("chunk",["string","Buffer","Uint8Array"],t));var r;return n}(s,t)),o)S(e,o);else if(s.objectMode||t&&t.length>0)if("string"==typeof t||s.objectMode||Object.getPrototypeOf(t)===a.prototype||(t=function(e){return a.from(e)}(t)),r)s.endEmitted?S(e,new _):k(e,s,t,!0);else if(s.ended)S(e,new y);else{if(s.destroyed)return!1;s.reading=!1,s.decoder&&!n?(t=s.decoder.write(t),s.objectMode||0!==t.length?k(e,s,t,!1):T(e,s)):k(e,s,t,!1)}else r||(s.reading=!1,T(e,s));return!s.ended&&(s.length<s.highWaterMark||0===s.length)}function k(e,t,n,r){t.flowing&&0===t.length&&!t.sync?(t.awaitDrain=0,e.emit("data",n)):(t.length+=t.objectMode?1:n.length,r?t.buffer.unshift(n):t.buffer.push(n),t.needReadable&&x(e)),T(e,t)}Object.defineProperty(A.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._readableState&&this._readableState.destroyed},set:function(e){this._readableState&&(this._readableState.destroyed=e)}}),A.prototype.destroy=v.destroy,A.prototype._undestroy=v.undestroy,A.prototype._destroy=function(e,t){t(e)},A.prototype.push=function(e,t){var n,r=this._readableState;return r.objectMode?n=!0:"string"==typeof e&&((t=t||r.defaultEncoding)!==r.encoding&&(e=a.from(e,t),t=""),n=!0),I(this,e,t,!1,n)},A.prototype.unshift=function(e){return I(this,e,null,!0,!1)},A.prototype.isPaused=function(){return!1===this._readableState.flowing},A.prototype.setEncoding=function(e){l||(l=n(59).StringDecoder);var t=new l(e);this._readableState.decoder=t,this._readableState.encoding=this._readableState.decoder.encoding;for(var r=this._readableState.buffer.head,i="";null!==r;)i+=t.write(r.data),r=r.next;return this._readableState.buffer.clear(),""!==i&&this._readableState.buffer.push(i),this._readableState.length=i.length,this};function O(e,t){return e<=0||0===t.length&&t.ended?0:t.objectMode?1:e!=e?t.flowing&&t.length?t.buffer.head.data.length:t.length:(e>t.highWaterMark&&(t.highWaterMark=function(e){return e>=1073741824?e=1073741824:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function x(e){var t=e._readableState;c("emitReadable",t.needReadable,t.emittedReadable),t.needReadable=!1,t.emittedReadable||(c("emitReadable",t.flowing),t.emittedReadable=!0,r.nextTick(C,e))}function C(e){var t=e._readableState;c("emitReadable_",t.destroyed,t.length,t.ended),t.destroyed||!t.length&&!t.ended||(e.emit("readable"),t.emittedReadable=!1),t.needReadable=!t.flowing&&!t.ended&&t.length<=t.highWaterMark,j(e)}function T(e,t){t.readingMore||(t.readingMore=!0,r.nextTick(P,e,t))}function P(e,t){for(;!t.reading&&!t.ended&&(t.length<t.highWaterMark||t.flowing&&0===t.length);){var n=t.length;if(c("maybeReadMore read 0"),e.read(0),n===t.length)break}t.readingMore=!1}function N(e){var t=e._readableState;t.readableListening=e.listenerCount("readable")>0,t.resumeScheduled&&!t.paused?t.flowing=!0:e.listenerCount("data")>0&&e.resume()}function R(e){c("readable nexttick read 0"),e.read(0)}function L(e,t){c("resume",t.reading),t.reading||e.read(0),t.resumeScheduled=!1,e.emit("resume"),j(e),t.flowing&&!t.reading&&e.read(0)}function j(e){var t=e._readableState;for(c("flow",t.flowing);t.flowing&&null!==e.read(););}function D(e,t){return 0===t.length?null:(t.objectMode?n=t.buffer.shift():!e||e>=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.first():t.buffer.concat(t.length),t.buffer.clear()):n=t.buffer.consume(e,t.decoder),n);var n}function U(e){var t=e._readableState;c("endReadable",t.endEmitted),t.endEmitted||(t.ended=!0,r.nextTick(B,t,e))}function B(e,t){if(c("endReadableNT",e.endEmitted,e.length),!e.endEmitted&&0===e.length&&(e.endEmitted=!0,t.readable=!1,t.emit("end"),e.autoDestroy)){var n=t._writableState;(!n||n.autoDestroy&&n.finished)&&t.destroy()}}function F(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1}A.prototype.read=function(e){c("read",e),e=parseInt(e,10);var t=this._readableState,n=e;if(0!==e&&(t.emittedReadable=!1),0===e&&t.needReadable&&((0!==t.highWaterMark?t.length>=t.highWaterMark:t.length>0)||t.ended))return c("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?U(this):x(this),null;if(0===(e=O(e,t))&&t.ended)return 0===t.length&&U(this),null;var r,i=t.needReadable;return c("need readable",i),(0===t.length||t.length-e<t.highWaterMark)&&c("length less than watermark",i=!0),t.ended||t.reading?c("reading or ended",i=!1):i&&(c("do read"),t.reading=!0,t.sync=!0,0===t.length&&(t.needReadable=!0),this._read(t.highWaterMark),t.sync=!1,t.reading||(e=O(n,t))),null===(r=e>0?D(e,t):null)?(t.needReadable=t.length<=t.highWaterMark,e=0):(t.length-=e,t.awaitDrain=0),0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&U(this)),null!==r&&this.emit("data",r),r},A.prototype._read=function(e){S(this,new w("_read()"))},A.prototype.pipe=function(e,t){var n=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=e;break;case 1:i.pipes=[i.pipes,e];break;default:i.pipes.push(e)}i.pipesCount+=1,c("pipe count=%d opts=%j",i.pipesCount,t);var s=(!t||!1!==t.end)&&e!==r.stdout&&e!==r.stderr?u:g;function a(t,r){c("onunpipe"),t===n&&r&&!1===r.hasUnpiped&&(r.hasUnpiped=!0,c("cleanup"),e.removeListener("close",p),e.removeListener("finish",v),e.removeListener("drain",f),e.removeListener("error",h),e.removeListener("unpipe",a),n.removeListener("end",u),n.removeListener("end",g),n.removeListener("data",d),l=!0,!i.awaitDrain||e._writableState&&!e._writableState.needDrain||f())}function u(){c("onend"),e.end()}i.endEmitted?r.nextTick(s):n.once("end",s),e.on("unpipe",a);var f=function(e){return function(){var t=e._readableState;c("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&o(e,"data")&&(t.flowing=!0,j(e))}}(n);e.on("drain",f);var l=!1;function d(t){c("ondata");var r=e.write(t);c("dest.write",r),!1===r&&((1===i.pipesCount&&i.pipes===e||i.pipesCount>1&&-1!==F(i.pipes,e))&&!l&&(c("false write response, pause",i.awaitDrain),i.awaitDrain++),n.pause())}function h(t){c("onerror",t),g(),e.removeListener("error",h),0===o(e,"error")&&S(e,t)}function p(){e.removeListener("finish",v),g()}function v(){c("onfinish"),e.removeListener("close",p),g()}function g(){c("unpipe"),n.unpipe(e)}return n.on("data",d),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?Array.isArray(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",h),e.once("close",p),e.once("finish",v),e.emit("pipe",n),i.flowing||(c("pipe resume"),n.resume()),e},A.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes||(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n)),this;if(!e){var r=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var o=0;o<i;o++)r[o].emit("unpipe",this,{hasUnpiped:!1});return this}var s=F(t.pipes,e);return-1===s||(t.pipes.splice(s,1),t.pipesCount-=1,1===t.pipesCount&&(t.pipes=t.pipes[0]),e.emit("unpipe",this,n)),this},A.prototype.on=function(e,t){var n=s.prototype.on.call(this,e,t),i=this._readableState;return"data"===e?(i.readableListening=this.listenerCount("readable")>0,!1!==i.flowing&&this.resume()):"readable"===e&&(i.endEmitted||i.readableListening||(i.readableListening=i.needReadable=!0,i.flowing=!1,i.emittedReadable=!1,c("on readable",i.length,i.reading),i.length?x(this):i.reading||r.nextTick(R,this))),n},A.prototype.addListener=A.prototype.on,A.prototype.removeListener=function(e,t){var n=s.prototype.removeListener.call(this,e,t);return"readable"===e&&r.nextTick(N,this),n},A.prototype.removeAllListeners=function(e){var t=s.prototype.removeAllListeners.apply(this,arguments);return"readable"!==e&&void 0!==e||r.nextTick(N,this),t},A.prototype.resume=function(){var e=this._readableState;return e.flowing||(c("resume"),e.flowing=!e.readableListening,function(e,t){t.resumeScheduled||(t.resumeScheduled=!0,r.nextTick(L,e,t))}(this,e)),e.paused=!1,this},A.prototype.pause=function(){return c("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(c("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this},A.prototype.wrap=function(e){var t=this,n=this._readableState,r=!1;for(var i in e.on("end",(function(){if(c("wrapped end"),n.decoder&&!n.ended){var e=n.decoder.end();e&&e.length&&t.push(e)}t.push(null)})),e.on("data",(function(i){(c("wrapped data"),n.decoder&&(i=n.decoder.write(i)),n.objectMode&&null==i)||(n.objectMode||i&&i.length)&&(t.push(i)||(r=!0,e.pause()))})),e)void 0===this[i]&&"function"==typeof e[i]&&(this[i]=function(t){return function(){return e[t].apply(e,arguments)}}(i));for(var o=0;o<E.length;o++)e.on(E[o],this.emit.bind(this,E[o]));return this._read=function(t){c("wrapped _read",t),r&&(r=!1,e.resume())},this},"function"==typeof Symbol&&(A.prototype[Symbol.asyncIterator]=function(){return void 0===d&&(d=n(324)),d(this)}),Object.defineProperty(A.prototype,"readableHighWaterMark",{enumerable:!1,get:function(){return this._readableState.highWaterMark}}),Object.defineProperty(A.prototype,"readableBuffer",{enumerable:!1,get:function(){return this._readableState&&this._readableState.buffer}}),Object.defineProperty(A.prototype,"readableFlowing",{enumerable:!1,get:function(){return this._readableState.flowing},set:function(e){this._readableState&&(this._readableState.flowing=e)}}),A._fromList=D,Object.defineProperty(A.prototype,"readableLength",{enumerable:!1,get:function(){return this._readableState.length}}),"function"==typeof Symbol&&(A.from=function(e,t){return void 0===h&&(h=n(325)),h(A,e,t)})}).call(this,n(31),n(20))},function(e,t,n){e.exports=n(49).EventEmitter},function(e,t,n){"use strict";(function(t){function n(e,t){i(e,t),r(e)}function r(e){e._writableState&&!e._writableState.emitClose||e._readableState&&!e._readableState.emitClose||e.emit("close")}function i(e,t){e.emit("error",t)}e.exports={destroy:function(e,o){var s=this,a=this._readableState&&this._readableState.destroyed,u=this._writableState&&this._writableState.destroyed;return a||u?(o?o(e):e&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,t.nextTick(i,this,e)):t.nextTick(i,this,e)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,(function(e){!o&&e?s._writableState?s._writableState.errorEmitted?t.nextTick(r,s):(s._writableState.errorEmitted=!0,t.nextTick(n,s,e)):t.nextTick(n,s,e):o?(t.nextTick(r,s),o(e)):t.nextTick(r,s)})),this)},undestroy:function(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)},errorOrDestroy:function(e,t){var n=e._readableState,r=e._writableState;n&&n.autoDestroy||r&&r.autoDestroy?e.destroy(t):e.emit("error",t)}}}).call(this,n(20))},function(e,t,n){"use strict";var r=n(70).codes.ERR_INVALID_OPT_VALUE;e.exports={getHighWaterMark:function(e,t,n,i){var o=function(e,t,n){return null!=e.highWaterMark?e.highWaterMark:t?e[n]:null}(t,i,n);if(null!=o){if(!isFinite(o)||Math.floor(o)!==o||o<0)throw new r(i?n:"highWaterMark",o);return Math.floor(o)}return e.objectMode?16:16384}}},function(e,t,n){"use strict";(function(t,r){function i(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var r=e.entry;e.entry=null;for(;r;){var i=r.callback;t.pendingcb--,i(n),r=r.next}t.corkedRequestsFree.next=e}(t,e)}}var o;e.exports=A,A.WritableState=M;var s={deprecate:n(114)},a=n(193),u=n(6).Buffer,c=t.Uint8Array||function(){};var f,l=n(194),d=n(195).getHighWaterMark,h=n(70).codes,p=h.ERR_INVALID_ARG_TYPE,v=h.ERR_METHOD_NOT_IMPLEMENTED,g=h.ERR_MULTIPLE_CALLBACK,m=h.ERR_STREAM_CANNOT_PIPE,b=h.ERR_STREAM_DESTROYED,y=h.ERR_STREAM_NULL_VALUES,w=h.ERR_STREAM_WRITE_AFTER_END,_=h.ERR_UNKNOWN_ENCODING,S=l.errorOrDestroy;function E(){}function M(e,t,s){o=o||n(71),e=e||{},"boolean"!=typeof s&&(s=t instanceof o),this.objectMode=!!e.objectMode,s&&(this.objectMode=this.objectMode||!!e.writableObjectMode),this.highWaterMark=d(this,e,"writableHighWaterMark",s),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var a=!1===e.decodeStrings;this.decodeStrings=!a,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,i=n.sync,o=n.writecb;if("function"!=typeof o)throw new g;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,i,o){--t.pendingcb,n?(r.nextTick(o,i),r.nextTick(T,e,t),e._writableState.errorEmitted=!0,S(e,i)):(o(i),e._writableState.errorEmitted=!0,S(e,i),T(e,t))}(e,n,i,t,o);else{var s=x(n)||e.destroyed;s||n.corked||n.bufferProcessing||!n.bufferedRequest||O(e,n),i?r.nextTick(k,e,n,s,o):k(e,n,s,o)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!1!==e.emitClose,this.autoDestroy=!!e.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new i(this)}function A(e){var t=this instanceof(o=o||n(71));if(!t&&!f.call(A,this))return new A(e);this._writableState=new M(e,this,t),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),a.call(this)}function I(e,t,n,r,i,o,s){t.writelen=r,t.writecb=s,t.writing=!0,t.sync=!0,t.destroyed?t.onwrite(new b("write")):n?e._writev(i,t.onwrite):e._write(i,o,t.onwrite),t.sync=!1}function k(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),T(e,t)}function O(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,o=new Array(r),s=t.corkedRequestsFree;s.entry=n;for(var a=0,u=!0;n;)o[a]=n,n.isBuf||(u=!1),n=n.next,a+=1;o.allBuffers=u,I(e,t,!0,t.length,o,"",s.finish),t.pendingcb++,t.lastBufferedRequest=null,s.next?(t.corkedRequestsFree=s.next,s.next=null):t.corkedRequestsFree=new i(t),t.bufferedRequestCount=0}else{for(;n;){var c=n.chunk,f=n.encoding,l=n.callback;if(I(e,t,!1,t.objectMode?1:c.length,c,f,l),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function x(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function C(e,t){e._final((function(n){t.pendingcb--,n&&S(e,n),t.prefinished=!0,e.emit("prefinish"),T(e,t)}))}function T(e,t){var n=x(t);if(n&&(function(e,t){t.prefinished||t.finalCalled||("function"!=typeof e._final||t.destroyed?(t.prefinished=!0,e.emit("prefinish")):(t.pendingcb++,t.finalCalled=!0,r.nextTick(C,e,t)))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"),t.autoDestroy))){var i=e._readableState;(!i||i.autoDestroy&&i.endEmitted)&&e.destroy()}return n}n(7)(A,a),M.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(M.prototype,"buffer",{get:s.deprecate((function(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(f=Function.prototype[Symbol.hasInstance],Object.defineProperty(A,Symbol.hasInstance,{value:function(e){return!!f.call(this,e)||this===A&&(e&&e._writableState instanceof M)}})):f=function(e){return e instanceof this},A.prototype.pipe=function(){S(this,new m)},A.prototype.write=function(e,t,n){var i,o=this._writableState,s=!1,a=!o.objectMode&&(i=e,u.isBuffer(i)||i instanceof c);return a&&!u.isBuffer(e)&&(e=function(e){return u.from(e)}(e)),"function"==typeof t&&(n=t,t=null),a?t="buffer":t||(t=o.defaultEncoding),"function"!=typeof n&&(n=E),o.ending?function(e,t){var n=new w;S(e,n),r.nextTick(t,n)}(this,n):(a||function(e,t,n,i){var o;return null===n?o=new y:"string"==typeof n||t.objectMode||(o=new p("chunk",["string","Buffer"],n)),!o||(S(e,o),r.nextTick(i,o),!1)}(this,o,e,n))&&(o.pendingcb++,s=function(e,t,n,r,i,o){if(!n){var s=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=u.from(t,n));return t}(t,r,i);r!==s&&(n=!0,i="buffer",r=s)}var a=t.objectMode?1:r.length;t.length+=a;var c=t.length<t.highWaterMark;c||(t.needDrain=!0);if(t.writing||t.corked){var f=t.lastBufferedRequest;t.lastBufferedRequest={chunk:r,encoding:i,isBuf:n,callback:o,next:null},f?f.next=t.lastBufferedRequest:t.bufferedRequest=t.lastBufferedRequest,t.bufferedRequestCount+=1}else I(e,t,!1,a,r,i,o);return c}(this,o,a,e,t,n)),s},A.prototype.cork=function(){this._writableState.corked++},A.prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--,e.writing||e.corked||e.bufferProcessing||!e.bufferedRequest||O(this,e))},A.prototype.setDefaultEncoding=function(e){if("string"==typeof e&&(e=e.toLowerCase()),!(["hex","utf8","utf-8","ascii","binary","base64","ucs2","ucs-2","utf16le","utf-16le","raw"].indexOf((e+"").toLowerCase())>-1))throw new _(e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(A.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(A.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),A.prototype._write=function(e,t,n){n(new v("_write()"))},A.prototype._writev=null,A.prototype.end=function(e,t,n){var i=this._writableState;return"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!=e&&this.write(e,t),i.corked&&(i.corked=1,this.uncork()),i.ending||function(e,t,n){t.ending=!0,T(e,t),n&&(t.finished?r.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,i,n),this},Object.defineProperty(A.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(A.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),A.prototype.destroy=l.destroy,A.prototype._undestroy=l.undestroy,A.prototype._destroy=function(e,t){t(e)}}).call(this,n(31),n(20))},function(e,t,n){"use strict";e.exports=f;var r=n(70).codes,i=r.ERR_METHOD_NOT_IMPLEMENTED,o=r.ERR_MULTIPLE_CALLBACK,s=r.ERR_TRANSFORM_ALREADY_TRANSFORMING,a=r.ERR_TRANSFORM_WITH_LENGTH_0,u=n(71);function c(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(null===r)return this.emit("error",new o);n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length<i.highWaterMark)&&this._read(i.highWaterMark)}function f(e){if(!(this instanceof f))return new f(e);u.call(this,e),this._transformState={afterTransform:c.bind(this),needTransform:!1,transforming:!1,writecb:null,writechunk:null,writeencoding:null},this._readableState.needReadable=!0,this._readableState.sync=!1,e&&("function"==typeof e.transform&&(this._transform=e.transform),"function"==typeof e.flush&&(this._flush=e.flush)),this.on("prefinish",l)}function l(){var e=this;"function"!=typeof this._flush||this._readableState.destroyed?d(this,null,null):this._flush((function(t,n){d(e,t,n)}))}function d(e,t,n){if(t)return e.emit("error",t);if(null!=n&&e.push(n),e._writableState.length)throw new a;if(e._transformState.transforming)throw new s;return e.push(null)}n(7)(f,u),f.prototype.push=function(e,t){return this._transformState.needTransform=!1,u.prototype.push.call(this,e,t)},f.prototype._transform=function(e,t,n){n(new i("_transform()"))},f.prototype._write=function(e,t,n){var r=this._transformState;if(r.writecb=n,r.writechunk=e,r.writeencoding=t,!r.transforming){var i=this._readableState;(r.needTransform||i.needReadable||i.length<i.highWaterMark)&&this._read(i.highWaterMark)}},f.prototype._read=function(e){var t=this._transformState;null===t.writechunk||t.transforming?t.needTransform=!0:(t.transforming=!0,this._transform(t.writechunk,t.writeencoding,t.afterTransform))},f.prototype._destroy=function(e,t){u.prototype._destroy.call(this,e,(function(e){t(e)}))}},function(e,t,n){"use strict";var r=t;function i(e){return 1===e.length?"0"+e:e}function o(e){for(var t="",n=0;n<e.length;n++)t+=i(e[n].toString(16));return t}r.toArray=function(e,t){if(Array.isArray(e))return e.slice();if(!e)return[];var n=[];if("string"!=typeof e){for(var r=0;r<e.length;r++)n[r]=0|e[r];return n}if("hex"===t){(e=e.replace(/[^a-z0-9]+/gi,"")).length%2!=0&&(e="0"+e);for(r=0;r<e.length;r+=2)n.push(parseInt(e[r]+e[r+1],16))}else for(r=0;r<e.length;r++){var i=e.charCodeAt(r),o=i>>8,s=255&i;o?n.push(o,s):n.push(s)}return n},r.zero2=i,r.toHex=o,r.encode=function(e,t){return"hex"===t?o(e):e}},function(e,t,n){"use strict";var r=t;r.base=n(95),r.short=n(332),r.mont=n(333),r.edwards=n(334)},function(e,t,n){"use strict";var r=n(51).rotr32;function i(e,t,n){return e&t^~e&n}function o(e,t,n){return e&t^e&n^t&n}function s(e,t,n){return e^t^n}t.ft_1=function(e,t,n,r){return 0===e?i(t,n,r):1===e||3===e?s(t,n,r):2===e?o(t,n,r):void 0},t.ch32=i,t.maj32=o,t.p32=s,t.s0_256=function(e){return r(e,2)^r(e,13)^r(e,22)},t.s1_256=function(e){return r(e,6)^r(e,11)^r(e,25)},t.g0_256=function(e){return r(e,7)^r(e,18)^e>>>3},t.g1_256=function(e){return r(e,17)^r(e,19)^e>>>10}},function(e,t,n){"use strict";var r=n(51),i=n(82),o=n(200),s=n(46),a=r.sum32,u=r.sum32_4,c=r.sum32_5,f=o.ch32,l=o.maj32,d=o.s0_256,h=o.s1_256,p=o.g0_256,v=o.g1_256,g=i.BlockHash,m=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function b(){if(!(this instanceof b))return new b;g.call(this),this.h=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],this.k=m,this.W=new Array(64)}r.inherits(b,g),e.exports=b,b.blockSize=512,b.outSize=256,b.hmacStrength=192,b.padLength=64,b.prototype._update=function(e,t){for(var n=this.W,r=0;r<16;r++)n[r]=e[t+r];for(;r<n.length;r++)n[r]=u(v(n[r-2]),n[r-7],p(n[r-15]),n[r-16]);var i=this.h[0],o=this.h[1],g=this.h[2],m=this.h[3],b=this.h[4],y=this.h[5],w=this.h[6],_=this.h[7];for(s(this.k.length===n.length),r=0;r<n.length;r++){var S=c(_,h(b),f(b,y,w),this.k[r],n[r]),E=a(d(i),l(i,o,g));_=w,w=y,y=b,b=a(m,S),m=g,g=o,o=i,i=a(S,E)}this.h[0]=a(this.h[0],i),this.h[1]=a(this.h[1],o),this.h[2]=a(this.h[2],g),this.h[3]=a(this.h[3],m),this.h[4]=a(this.h[4],b),this.h[5]=a(this.h[5],y),this.h[6]=a(this.h[6],w),this.h[7]=a(this.h[7],_)},b.prototype._digest=function(e){return"hex"===e?r.toHex32(this.h,"big"):r.split32(this.h,"big")}},function(e,t,n){"use strict";var r=n(51),i=n(82),o=n(46),s=r.rotr64_hi,a=r.rotr64_lo,u=r.shr64_hi,c=r.shr64_lo,f=r.sum64,l=r.sum64_hi,d=r.sum64_lo,h=r.sum64_4_hi,p=r.sum64_4_lo,v=r.sum64_5_hi,g=r.sum64_5_lo,m=i.BlockHash,b=[1116352408,3609767458,1899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556,3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403,1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344,2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,685471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816402316,1246189591];function y(){if(!(this instanceof y))return new y;m.call(this),this.h=[1779033703,4089235720,3144134277,2227873595,1013904242,4271175723,2773480762,1595750129,1359893119,2917565137,2600822924,725511199,528734635,4215389547,1541459225,327033209],this.k=b,this.W=new Array(160)}function w(e,t,n,r,i){var o=e&n^~e&i;return o<0&&(o+=4294967296),o}function _(e,t,n,r,i,o){var s=t&r^~t&o;return s<0&&(s+=4294967296),s}function S(e,t,n,r,i){var o=e&n^e&i^n&i;return o<0&&(o+=4294967296),o}function E(e,t,n,r,i,o){var s=t&r^t&o^r&o;return s<0&&(s+=4294967296),s}function M(e,t){var n=s(e,t,28)^s(t,e,2)^s(t,e,7);return n<0&&(n+=4294967296),n}function A(e,t){var n=a(e,t,28)^a(t,e,2)^a(t,e,7);return n<0&&(n+=4294967296),n}function I(e,t){var n=s(e,t,14)^s(e,t,18)^s(t,e,9);return n<0&&(n+=4294967296),n}function k(e,t){var n=a(e,t,14)^a(e,t,18)^a(t,e,9);return n<0&&(n+=4294967296),n}function O(e,t){var n=s(e,t,1)^s(e,t,8)^u(e,t,7);return n<0&&(n+=4294967296),n}function x(e,t){var n=a(e,t,1)^a(e,t,8)^c(e,t,7);return n<0&&(n+=4294967296),n}function C(e,t){var n=s(e,t,19)^s(t,e,29)^u(e,t,6);return n<0&&(n+=4294967296),n}function T(e,t){var n=a(e,t,19)^a(t,e,29)^c(e,t,6);return n<0&&(n+=4294967296),n}r.inherits(y,m),e.exports=y,y.blockSize=1024,y.outSize=512,y.hmacStrength=192,y.padLength=128,y.prototype._prepareBlock=function(e,t){for(var n=this.W,r=0;r<32;r++)n[r]=e[t+r];for(;r<n.length;r+=2){var i=C(n[r-4],n[r-3]),o=T(n[r-4],n[r-3]),s=n[r-14],a=n[r-13],u=O(n[r-30],n[r-29]),c=x(n[r-30],n[r-29]),f=n[r-32],l=n[r-31];n[r]=h(i,o,s,a,u,c,f,l),n[r+1]=p(i,o,s,a,u,c,f,l)}},y.prototype._update=function(e,t){this._prepareBlock(e,t);var n=this.W,r=this.h[0],i=this.h[1],s=this.h[2],a=this.h[3],u=this.h[4],c=this.h[5],h=this.h[6],p=this.h[7],m=this.h[8],b=this.h[9],y=this.h[10],O=this.h[11],x=this.h[12],C=this.h[13],T=this.h[14],P=this.h[15];o(this.k.length===n.length);for(var N=0;N<n.length;N+=2){var R=T,L=P,j=I(m,b),D=k(m,b),U=w(m,b,y,O,x),B=_(m,b,y,O,x,C),F=this.k[N],z=this.k[N+1],q=n[N],K=n[N+1],H=v(R,L,j,D,U,B,F,z,q,K),V=g(R,L,j,D,U,B,F,z,q,K);R=M(r,i),L=A(r,i),j=S(r,i,s,a,u),D=E(r,i,s,a,u,c);var G=l(R,L,j,D),W=d(R,L,j,D);T=x,P=C,x=y,C=O,y=m,O=b,m=l(h,p,H,V),b=d(p,p,H,V),h=u,p=c,u=s,c=a,s=r,a=i,r=l(H,V,G,W),i=d(H,V,G,W)}f(this.h,0,r,i),f(this.h,2,s,a),f(this.h,4,u,c),f(this.h,6,h,p),f(this.h,8,m,b),f(this.h,10,y,O),f(this.h,12,x,C),f(this.h,14,T,P)},y.prototype._digest=function(e){return"hex"===e?r.toHex32(this.h,"big"):r.split32(this.h,"big")}},function(e,t,n){(function(e){!function(e,t){"use strict";function r(e,t){if(!e)throw new Error(t||"Assertion failed")}function i(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}function o(e,t,n){if(o.isBN(e))return e;this.negative=0,this.words=null,this.length=0,this.red=null,null!==e&&("le"!==t&&"be"!==t||(n=t,t=10),this._init(e||0,t||10,n||"be"))}var s;"object"==typeof e?e.exports=o:t.BN=o,o.BN=o,o.wordSize=26;try{s=n(349).Buffer}catch(e){}function a(e,t,n){for(var i=0,o=Math.min(e.length,n),s=0,a=t;a<o;a++){var u,c=e.charCodeAt(a)-48;i<<=4,i|=u=c>=49&&c<=54?c-49+10:c>=17&&c<=22?c-17+10:c,s|=u}return r(!(240&s),"Invalid character in "+e),i}function u(e,t,n,i){for(var o=0,s=0,a=Math.min(e.length,n),u=t;u<a;u++){var c=e.charCodeAt(u)-48;o*=i,s=c>=49?c-49+10:c>=17?c-17+10:c,r(c>=0&&s<i,"Invalid character"),o+=s}return o}function c(e,t){e.words=t.words,e.length=t.length,e.negative=t.negative,e.red=t.red}if(o.isBN=function(e){return e instanceof o||null!==e&&"object"==typeof e&&e.constructor.wordSize===o.wordSize&&Array.isArray(e.words)},o.max=function(e,t){return e.cmp(t)>0?e:t},o.min=function(e,t){return e.cmp(t)<0?e:t},o.prototype._init=function(e,t,n){if("number"==typeof e)return this._initNumber(e,t,n);if("object"==typeof e)return this._initArray(e,t,n);"hex"===t&&(t=16),r(t===(0|t)&&t>=2&&t<=36);var i=0;"-"===(e=e.toString().replace(/\s+/g,""))[0]&&i++,16===t?this._parseHex(e,i):this._parseBase(e,t,i),"-"===e[0]&&(this.negative=1),this._strip(),"le"===n&&this._initArray(this.toArray(),t,n)},o.prototype._initNumber=function(e,t,n){e<0&&(this.negative=1,e=-e),e<67108864?(this.words=[67108863&e],this.length=1):e<4503599627370496?(this.words=[67108863&e,e/67108864&67108863],this.length=2):(r(e<9007199254740992),this.words=[67108863&e,e/67108864&67108863,1],this.length=3),"le"===n&&this._initArray(this.toArray(),t,n)},o.prototype._initArray=function(e,t,n){if(r("number"==typeof e.length),e.length<=0)return this.words=[0],this.length=1,this;this.length=Math.ceil(e.length/3),this.words=new Array(this.length);for(var i=0;i<this.length;i++)this.words[i]=0;var o,s,a=0;if("be"===n)for(i=e.length-1,o=0;i>=0;i-=3)s=e[i]|e[i-1]<<8|e[i-2]<<16,this.words[o]|=s<<a&67108863,this.words[o+1]=s>>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===n)for(i=0,o=0;i<e.length;i+=3)s=e[i]|e[i+1]<<8|e[i+2]<<16,this.words[o]|=s<<a&67108863,this.words[o+1]=s>>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this._strip()},o.prototype._parseHex=function(e,t){this.length=Math.ceil((e.length-t)/6),this.words=new Array(this.length);for(var n=0;n<this.length;n++)this.words[n]=0;var r,i,o=0;for(n=e.length-6,r=0;n>=t;n-=6)i=a(e,n,n+6),this.words[r]|=i<<o&67108863,this.words[r+1]|=i>>>26-o&4194303,(o+=24)>=26&&(o-=26,r++);n+6!==t&&(i=a(e,t,n+6),this.words[r]|=i<<o&67108863,this.words[r+1]|=i>>>26-o&4194303),this._strip()},o.prototype._parseBase=function(e,t,n){this.words=[0],this.length=1;for(var r=0,i=1;i<=67108863;i*=t)r++;r--,i=i/t|0;for(var o=e.length-n,s=o%r,a=Math.min(o,o-s)+n,c=0,f=n;f<a;f+=r)c=u(e,f,f+r,t),this.imuln(i),this.words[0]+c<67108864?this.words[0]+=c:this._iaddn(c);if(0!==s){var l=1;for(c=u(e,f,e.length,t),f=0;f<s;f++)l*=t;this.imuln(l),this.words[0]+c<67108864?this.words[0]+=c:this._iaddn(c)}},o.prototype.copy=function(e){e.words=new Array(this.length);for(var t=0;t<this.length;t++)e.words[t]=this.words[t];e.length=this.length,e.negative=this.negative,e.red=this.red},o.prototype._move=function(e){c(e,this)},o.prototype.clone=function(){var e=new o(null);return this.copy(e),e},o.prototype._expand=function(e){for(;this.length<e;)this.words[this.length++]=0;return this},o.prototype._strip=function(){for(;this.length>1&&0===this.words[this.length-1];)this.length--;return this._normSign()},o.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},"undefined"!=typeof Symbol&&"function"==typeof Symbol.for)try{o.prototype[Symbol.for("nodejs.util.inspect.custom")]=f}catch(e){o.prototype.inspect=f}else o.prototype.inspect=f;function f(){return(this.red?"<BN-R: ":"<BN: ")+this.toString(16)+">"}var l=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],d=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],h=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];o.prototype.toString=function(e,t){var n;if(t=0|t||1,16===(e=e||10)||"hex"===e){n="";for(var i=0,o=0,s=0;s<this.length;s++){var a=this.words[s],u=(16777215&(a<<i|o)).toString(16);n=0!==(o=a>>>24-i&16777215)||s!==this.length-1?l[6-u.length]+u+n:u+n,(i+=2)>=26&&(i-=26,s--)}for(0!==o&&(n=o.toString(16)+n);n.length%t!=0;)n="0"+n;return 0!==this.negative&&(n="-"+n),n}if(e===(0|e)&&e>=2&&e<=36){var c=d[e],f=h[e];n="";var p=this.clone();for(p.negative=0;!p.isZero();){var v=p.modrn(f).toString(e);n=(p=p.idivn(f)).isZero()?v+n:l[c-v.length]+v+n}for(this.isZero()&&(n="0"+n);n.length%t!=0;)n="0"+n;return 0!==this.negative&&(n="-"+n),n}r(!1,"Base should be between 2 and 36")},o.prototype.toNumber=function(){var e=this.words[0];return 2===this.length?e+=67108864*this.words[1]:3===this.length&&1===this.words[2]?e+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-e:e},o.prototype.toJSON=function(){return this.toString(16,2)},s&&(o.prototype.toBuffer=function(e,t){return this.toArrayLike(s,e,t)}),o.prototype.toArray=function(e,t){return this.toArrayLike(Array,e,t)};function p(e,t,n){n.negative=t.negative^e.negative;var r=e.length+t.length|0;n.length=r,r=r-1|0;var i=0|e.words[0],o=0|t.words[0],s=i*o,a=67108863&s,u=s/67108864|0;n.words[0]=a;for(var c=1;c<r;c++){for(var f=u>>>26,l=67108863&u,d=Math.min(c,t.length-1),h=Math.max(0,c-e.length+1);h<=d;h++){var p=c-h|0;f+=(s=(i=0|e.words[p])*(o=0|t.words[h])+l)/67108864|0,l=67108863&s}n.words[c]=0|l,u=0|f}return 0!==u?n.words[c]=0|u:n.length--,n._strip()}o.prototype.toArrayLike=function(e,t,n){this._strip();var i=this.byteLength(),o=n||Math.max(1,i);r(i<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0");var s=function(e,t){return e.allocUnsafe?e.allocUnsafe(t):new e(t)}(e,o);return this["_toArrayLike"+("le"===t?"LE":"BE")](s,i),s},o.prototype._toArrayLikeLE=function(e,t){for(var n=0,r=0,i=0,o=0;i<this.length;i++){var s=this.words[i]<<o|r;e[n++]=255&s,n<e.length&&(e[n++]=s>>8&255),n<e.length&&(e[n++]=s>>16&255),6===o?(n<e.length&&(e[n++]=s>>24&255),r=0,o=0):(r=s>>>24,o+=2)}if(n<e.length)for(e[n++]=r;n<e.length;)e[n++]=0},o.prototype._toArrayLikeBE=function(e,t){for(var n=e.length-1,r=0,i=0,o=0;i<this.length;i++){var s=this.words[i]<<o|r;e[n--]=255&s,n>=0&&(e[n--]=s>>8&255),n>=0&&(e[n--]=s>>16&255),6===o?(n>=0&&(e[n--]=s>>24&255),r=0,o=0):(r=s>>>24,o+=2)}if(n>=0)for(e[n--]=r;n>=0;)e[n--]=0},Math.clz32?o.prototype._countBits=function(e){return 32-Math.clz32(e)}:o.prototype._countBits=function(e){var t=e,n=0;return t>=4096&&(n+=13,t>>>=13),t>=64&&(n+=7,t>>>=7),t>=8&&(n+=4,t>>>=4),t>=2&&(n+=2,t>>>=2),n+t},o.prototype._zeroBits=function(e){if(0===e)return 26;var t=e,n=0;return 0==(8191&t)&&(n+=13,t>>>=13),0==(127&t)&&(n+=7,t>>>=7),0==(15&t)&&(n+=4,t>>>=4),0==(3&t)&&(n+=2,t>>>=2),0==(1&t)&&n++,n},o.prototype.bitLength=function(){var e=this.words[this.length-1],t=this._countBits(e);return 26*(this.length-1)+t},o.prototype.zeroBits=function(){if(this.isZero())return 0;for(var e=0,t=0;t<this.length;t++){var n=this._zeroBits(this.words[t]);if(e+=n,26!==n)break}return e},o.prototype.byteLength=function(){return Math.ceil(this.bitLength()/8)},o.prototype.toTwos=function(e){return 0!==this.negative?this.abs().inotn(e).iaddn(1):this.clone()},o.prototype.fromTwos=function(e){return this.testn(e-1)?this.notn(e).iaddn(1).ineg():this.clone()},o.prototype.isNeg=function(){return 0!==this.negative},o.prototype.neg=function(){return this.clone().ineg()},o.prototype.ineg=function(){return this.isZero()||(this.negative^=1),this},o.prototype.iuor=function(e){for(;this.length<e.length;)this.words[this.length++]=0;for(var t=0;t<e.length;t++)this.words[t]=this.words[t]|e.words[t];return this._strip()},o.prototype.ior=function(e){return r(0==(this.negative|e.negative)),this.iuor(e)},o.prototype.or=function(e){return this.length>e.length?this.clone().ior(e):e.clone().ior(this)},o.prototype.uor=function(e){return this.length>e.length?this.clone().iuor(e):e.clone().iuor(this)},o.prototype.iuand=function(e){var t;t=this.length>e.length?e:this;for(var n=0;n<t.length;n++)this.words[n]=this.words[n]&e.words[n];return this.length=t.length,this._strip()},o.prototype.iand=function(e){return r(0==(this.negative|e.negative)),this.iuand(e)},o.prototype.and=function(e){return this.length>e.length?this.clone().iand(e):e.clone().iand(this)},o.prototype.uand=function(e){return this.length>e.length?this.clone().iuand(e):e.clone().iuand(this)},o.prototype.iuxor=function(e){var t,n;this.length>e.length?(t=this,n=e):(t=e,n=this);for(var r=0;r<n.length;r++)this.words[r]=t.words[r]^n.words[r];if(this!==t)for(;r<t.length;r++)this.words[r]=t.words[r];return this.length=t.length,this._strip()},o.prototype.ixor=function(e){return r(0==(this.negative|e.negative)),this.iuxor(e)},o.prototype.xor=function(e){return this.length>e.length?this.clone().ixor(e):e.clone().ixor(this)},o.prototype.uxor=function(e){return this.length>e.length?this.clone().iuxor(e):e.clone().iuxor(this)},o.prototype.inotn=function(e){r("number"==typeof e&&e>=0);var t=0|Math.ceil(e/26),n=e%26;this._expand(t),n>0&&t--;for(var i=0;i<t;i++)this.words[i]=67108863&~this.words[i];return n>0&&(this.words[i]=~this.words[i]&67108863>>26-n),this._strip()},o.prototype.notn=function(e){return this.clone().inotn(e)},o.prototype.setn=function(e,t){r("number"==typeof e&&e>=0);var n=e/26|0,i=e%26;return this._expand(n+1),this.words[n]=t?this.words[n]|1<<i:this.words[n]&~(1<<i),this._strip()},o.prototype.iadd=function(e){var t,n,r;if(0!==this.negative&&0===e.negative)return this.negative=0,t=this.isub(e),this.negative^=1,this._normSign();if(0===this.negative&&0!==e.negative)return e.negative=0,t=this.isub(e),e.negative=1,t._normSign();this.length>e.length?(n=this,r=e):(n=e,r=this);for(var i=0,o=0;o<r.length;o++)t=(0|n.words[o])+(0|r.words[o])+i,this.words[o]=67108863&t,i=t>>>26;for(;0!==i&&o<n.length;o++)t=(0|n.words[o])+i,this.words[o]=67108863&t,i=t>>>26;if(this.length=n.length,0!==i)this.words[this.length]=i,this.length++;else if(n!==this)for(;o<n.length;o++)this.words[o]=n.words[o];return this},o.prototype.add=function(e){var t;return 0!==e.negative&&0===this.negative?(e.negative=0,t=this.sub(e),e.negative^=1,t):0===e.negative&&0!==this.negative?(this.negative=0,t=e.sub(this),this.negative=1,t):this.length>e.length?this.clone().iadd(e):e.clone().iadd(this)},o.prototype.isub=function(e){if(0!==e.negative){e.negative=0;var t=this.iadd(e);return e.negative=1,t._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(e),this.negative=1,this._normSign();var n,r,i=this.cmp(e);if(0===i)return this.negative=0,this.length=1,this.words[0]=0,this;i>0?(n=this,r=e):(n=e,r=this);for(var o=0,s=0;s<r.length;s++)o=(t=(0|n.words[s])-(0|r.words[s])+o)>>26,this.words[s]=67108863&t;for(;0!==o&&s<n.length;s++)o=(t=(0|n.words[s])+o)>>26,this.words[s]=67108863&t;if(0===o&&s<n.length&&n!==this)for(;s<n.length;s++)this.words[s]=n.words[s];return this.length=Math.max(this.length,s),n!==this&&(this.negative=1),this._strip()},o.prototype.sub=function(e){return this.clone().isub(e)};var v=function(e,t,n){var r,i,o,s=e.words,a=t.words,u=n.words,c=0,f=0|s[0],l=8191&f,d=f>>>13,h=0|s[1],p=8191&h,v=h>>>13,g=0|s[2],m=8191&g,b=g>>>13,y=0|s[3],w=8191&y,_=y>>>13,S=0|s[4],E=8191&S,M=S>>>13,A=0|s[5],I=8191&A,k=A>>>13,O=0|s[6],x=8191&O,C=O>>>13,T=0|s[7],P=8191&T,N=T>>>13,R=0|s[8],L=8191&R,j=R>>>13,D=0|s[9],U=8191&D,B=D>>>13,F=0|a[0],z=8191&F,q=F>>>13,K=0|a[1],H=8191&K,V=K>>>13,G=0|a[2],W=8191&G,$=G>>>13,Y=0|a[3],J=8191&Y,Z=Y>>>13,X=0|a[4],Q=8191&X,ee=X>>>13,te=0|a[5],ne=8191&te,re=te>>>13,ie=0|a[6],oe=8191&ie,se=ie>>>13,ae=0|a[7],ue=8191&ae,ce=ae>>>13,fe=0|a[8],le=8191&fe,de=fe>>>13,he=0|a[9],pe=8191&he,ve=he>>>13;n.negative=e.negative^t.negative,n.length=19;var ge=(c+(r=Math.imul(l,z))|0)+((8191&(i=(i=Math.imul(l,q))+Math.imul(d,z)|0))<<13)|0;c=((o=Math.imul(d,q))+(i>>>13)|0)+(ge>>>26)|0,ge&=67108863,r=Math.imul(p,z),i=(i=Math.imul(p,q))+Math.imul(v,z)|0,o=Math.imul(v,q);var me=(c+(r=r+Math.imul(l,H)|0)|0)+((8191&(i=(i=i+Math.imul(l,V)|0)+Math.imul(d,H)|0))<<13)|0;c=((o=o+Math.imul(d,V)|0)+(i>>>13)|0)+(me>>>26)|0,me&=67108863,r=Math.imul(m,z),i=(i=Math.imul(m,q))+Math.imul(b,z)|0,o=Math.imul(b,q),r=r+Math.imul(p,H)|0,i=(i=i+Math.imul(p,V)|0)+Math.imul(v,H)|0,o=o+Math.imul(v,V)|0;var be=(c+(r=r+Math.imul(l,W)|0)|0)+((8191&(i=(i=i+Math.imul(l,$)|0)+Math.imul(d,W)|0))<<13)|0;c=((o=o+Math.imul(d,$)|0)+(i>>>13)|0)+(be>>>26)|0,be&=67108863,r=Math.imul(w,z),i=(i=Math.imul(w,q))+Math.imul(_,z)|0,o=Math.imul(_,q),r=r+Math.imul(m,H)|0,i=(i=i+Math.imul(m,V)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,V)|0,r=r+Math.imul(p,W)|0,i=(i=i+Math.imul(p,$)|0)+Math.imul(v,W)|0,o=o+Math.imul(v,$)|0;var ye=(c+(r=r+Math.imul(l,J)|0)|0)+((8191&(i=(i=i+Math.imul(l,Z)|0)+Math.imul(d,J)|0))<<13)|0;c=((o=o+Math.imul(d,Z)|0)+(i>>>13)|0)+(ye>>>26)|0,ye&=67108863,r=Math.imul(E,z),i=(i=Math.imul(E,q))+Math.imul(M,z)|0,o=Math.imul(M,q),r=r+Math.imul(w,H)|0,i=(i=i+Math.imul(w,V)|0)+Math.imul(_,H)|0,o=o+Math.imul(_,V)|0,r=r+Math.imul(m,W)|0,i=(i=i+Math.imul(m,$)|0)+Math.imul(b,W)|0,o=o+Math.imul(b,$)|0,r=r+Math.imul(p,J)|0,i=(i=i+Math.imul(p,Z)|0)+Math.imul(v,J)|0,o=o+Math.imul(v,Z)|0;var we=(c+(r=r+Math.imul(l,Q)|0)|0)+((8191&(i=(i=i+Math.imul(l,ee)|0)+Math.imul(d,Q)|0))<<13)|0;c=((o=o+Math.imul(d,ee)|0)+(i>>>13)|0)+(we>>>26)|0,we&=67108863,r=Math.imul(I,z),i=(i=Math.imul(I,q))+Math.imul(k,z)|0,o=Math.imul(k,q),r=r+Math.imul(E,H)|0,i=(i=i+Math.imul(E,V)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,V)|0,r=r+Math.imul(w,W)|0,i=(i=i+Math.imul(w,$)|0)+Math.imul(_,W)|0,o=o+Math.imul(_,$)|0,r=r+Math.imul(m,J)|0,i=(i=i+Math.imul(m,Z)|0)+Math.imul(b,J)|0,o=o+Math.imul(b,Z)|0,r=r+Math.imul(p,Q)|0,i=(i=i+Math.imul(p,ee)|0)+Math.imul(v,Q)|0,o=o+Math.imul(v,ee)|0;var _e=(c+(r=r+Math.imul(l,ne)|0)|0)+((8191&(i=(i=i+Math.imul(l,re)|0)+Math.imul(d,ne)|0))<<13)|0;c=((o=o+Math.imul(d,re)|0)+(i>>>13)|0)+(_e>>>26)|0,_e&=67108863,r=Math.imul(x,z),i=(i=Math.imul(x,q))+Math.imul(C,z)|0,o=Math.imul(C,q),r=r+Math.imul(I,H)|0,i=(i=i+Math.imul(I,V)|0)+Math.imul(k,H)|0,o=o+Math.imul(k,V)|0,r=r+Math.imul(E,W)|0,i=(i=i+Math.imul(E,$)|0)+Math.imul(M,W)|0,o=o+Math.imul(M,$)|0,r=r+Math.imul(w,J)|0,i=(i=i+Math.imul(w,Z)|0)+Math.imul(_,J)|0,o=o+Math.imul(_,Z)|0,r=r+Math.imul(m,Q)|0,i=(i=i+Math.imul(m,ee)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,ee)|0,r=r+Math.imul(p,ne)|0,i=(i=i+Math.imul(p,re)|0)+Math.imul(v,ne)|0,o=o+Math.imul(v,re)|0;var Se=(c+(r=r+Math.imul(l,oe)|0)|0)+((8191&(i=(i=i+Math.imul(l,se)|0)+Math.imul(d,oe)|0))<<13)|0;c=((o=o+Math.imul(d,se)|0)+(i>>>13)|0)+(Se>>>26)|0,Se&=67108863,r=Math.imul(P,z),i=(i=Math.imul(P,q))+Math.imul(N,z)|0,o=Math.imul(N,q),r=r+Math.imul(x,H)|0,i=(i=i+Math.imul(x,V)|0)+Math.imul(C,H)|0,o=o+Math.imul(C,V)|0,r=r+Math.imul(I,W)|0,i=(i=i+Math.imul(I,$)|0)+Math.imul(k,W)|0,o=o+Math.imul(k,$)|0,r=r+Math.imul(E,J)|0,i=(i=i+Math.imul(E,Z)|0)+Math.imul(M,J)|0,o=o+Math.imul(M,Z)|0,r=r+Math.imul(w,Q)|0,i=(i=i+Math.imul(w,ee)|0)+Math.imul(_,Q)|0,o=o+Math.imul(_,ee)|0,r=r+Math.imul(m,ne)|0,i=(i=i+Math.imul(m,re)|0)+Math.imul(b,ne)|0,o=o+Math.imul(b,re)|0,r=r+Math.imul(p,oe)|0,i=(i=i+Math.imul(p,se)|0)+Math.imul(v,oe)|0,o=o+Math.imul(v,se)|0;var Ee=(c+(r=r+Math.imul(l,ue)|0)|0)+((8191&(i=(i=i+Math.imul(l,ce)|0)+Math.imul(d,ue)|0))<<13)|0;c=((o=o+Math.imul(d,ce)|0)+(i>>>13)|0)+(Ee>>>26)|0,Ee&=67108863,r=Math.imul(L,z),i=(i=Math.imul(L,q))+Math.imul(j,z)|0,o=Math.imul(j,q),r=r+Math.imul(P,H)|0,i=(i=i+Math.imul(P,V)|0)+Math.imul(N,H)|0,o=o+Math.imul(N,V)|0,r=r+Math.imul(x,W)|0,i=(i=i+Math.imul(x,$)|0)+Math.imul(C,W)|0,o=o+Math.imul(C,$)|0,r=r+Math.imul(I,J)|0,i=(i=i+Math.imul(I,Z)|0)+Math.imul(k,J)|0,o=o+Math.imul(k,Z)|0,r=r+Math.imul(E,Q)|0,i=(i=i+Math.imul(E,ee)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,ee)|0,r=r+Math.imul(w,ne)|0,i=(i=i+Math.imul(w,re)|0)+Math.imul(_,ne)|0,o=o+Math.imul(_,re)|0,r=r+Math.imul(m,oe)|0,i=(i=i+Math.imul(m,se)|0)+Math.imul(b,oe)|0,o=o+Math.imul(b,se)|0,r=r+Math.imul(p,ue)|0,i=(i=i+Math.imul(p,ce)|0)+Math.imul(v,ue)|0,o=o+Math.imul(v,ce)|0;var Me=(c+(r=r+Math.imul(l,le)|0)|0)+((8191&(i=(i=i+Math.imul(l,de)|0)+Math.imul(d,le)|0))<<13)|0;c=((o=o+Math.imul(d,de)|0)+(i>>>13)|0)+(Me>>>26)|0,Me&=67108863,r=Math.imul(U,z),i=(i=Math.imul(U,q))+Math.imul(B,z)|0,o=Math.imul(B,q),r=r+Math.imul(L,H)|0,i=(i=i+Math.imul(L,V)|0)+Math.imul(j,H)|0,o=o+Math.imul(j,V)|0,r=r+Math.imul(P,W)|0,i=(i=i+Math.imul(P,$)|0)+Math.imul(N,W)|0,o=o+Math.imul(N,$)|0,r=r+Math.imul(x,J)|0,i=(i=i+Math.imul(x,Z)|0)+Math.imul(C,J)|0,o=o+Math.imul(C,Z)|0,r=r+Math.imul(I,Q)|0,i=(i=i+Math.imul(I,ee)|0)+Math.imul(k,Q)|0,o=o+Math.imul(k,ee)|0,r=r+Math.imul(E,ne)|0,i=(i=i+Math.imul(E,re)|0)+Math.imul(M,ne)|0,o=o+Math.imul(M,re)|0,r=r+Math.imul(w,oe)|0,i=(i=i+Math.imul(w,se)|0)+Math.imul(_,oe)|0,o=o+Math.imul(_,se)|0,r=r+Math.imul(m,ue)|0,i=(i=i+Math.imul(m,ce)|0)+Math.imul(b,ue)|0,o=o+Math.imul(b,ce)|0,r=r+Math.imul(p,le)|0,i=(i=i+Math.imul(p,de)|0)+Math.imul(v,le)|0,o=o+Math.imul(v,de)|0;var Ae=(c+(r=r+Math.imul(l,pe)|0)|0)+((8191&(i=(i=i+Math.imul(l,ve)|0)+Math.imul(d,pe)|0))<<13)|0;c=((o=o+Math.imul(d,ve)|0)+(i>>>13)|0)+(Ae>>>26)|0,Ae&=67108863,r=Math.imul(U,H),i=(i=Math.imul(U,V))+Math.imul(B,H)|0,o=Math.imul(B,V),r=r+Math.imul(L,W)|0,i=(i=i+Math.imul(L,$)|0)+Math.imul(j,W)|0,o=o+Math.imul(j,$)|0,r=r+Math.imul(P,J)|0,i=(i=i+Math.imul(P,Z)|0)+Math.imul(N,J)|0,o=o+Math.imul(N,Z)|0,r=r+Math.imul(x,Q)|0,i=(i=i+Math.imul(x,ee)|0)+Math.imul(C,Q)|0,o=o+Math.imul(C,ee)|0,r=r+Math.imul(I,ne)|0,i=(i=i+Math.imul(I,re)|0)+Math.imul(k,ne)|0,o=o+Math.imul(k,re)|0,r=r+Math.imul(E,oe)|0,i=(i=i+Math.imul(E,se)|0)+Math.imul(M,oe)|0,o=o+Math.imul(M,se)|0,r=r+Math.imul(w,ue)|0,i=(i=i+Math.imul(w,ce)|0)+Math.imul(_,ue)|0,o=o+Math.imul(_,ce)|0,r=r+Math.imul(m,le)|0,i=(i=i+Math.imul(m,de)|0)+Math.imul(b,le)|0,o=o+Math.imul(b,de)|0;var Ie=(c+(r=r+Math.imul(p,pe)|0)|0)+((8191&(i=(i=i+Math.imul(p,ve)|0)+Math.imul(v,pe)|0))<<13)|0;c=((o=o+Math.imul(v,ve)|0)+(i>>>13)|0)+(Ie>>>26)|0,Ie&=67108863,r=Math.imul(U,W),i=(i=Math.imul(U,$))+Math.imul(B,W)|0,o=Math.imul(B,$),r=r+Math.imul(L,J)|0,i=(i=i+Math.imul(L,Z)|0)+Math.imul(j,J)|0,o=o+Math.imul(j,Z)|0,r=r+Math.imul(P,Q)|0,i=(i=i+Math.imul(P,ee)|0)+Math.imul(N,Q)|0,o=o+Math.imul(N,ee)|0,r=r+Math.imul(x,ne)|0,i=(i=i+Math.imul(x,re)|0)+Math.imul(C,ne)|0,o=o+Math.imul(C,re)|0,r=r+Math.imul(I,oe)|0,i=(i=i+Math.imul(I,se)|0)+Math.imul(k,oe)|0,o=o+Math.imul(k,se)|0,r=r+Math.imul(E,ue)|0,i=(i=i+Math.imul(E,ce)|0)+Math.imul(M,ue)|0,o=o+Math.imul(M,ce)|0,r=r+Math.imul(w,le)|0,i=(i=i+Math.imul(w,de)|0)+Math.imul(_,le)|0,o=o+Math.imul(_,de)|0;var ke=(c+(r=r+Math.imul(m,pe)|0)|0)+((8191&(i=(i=i+Math.imul(m,ve)|0)+Math.imul(b,pe)|0))<<13)|0;c=((o=o+Math.imul(b,ve)|0)+(i>>>13)|0)+(ke>>>26)|0,ke&=67108863,r=Math.imul(U,J),i=(i=Math.imul(U,Z))+Math.imul(B,J)|0,o=Math.imul(B,Z),r=r+Math.imul(L,Q)|0,i=(i=i+Math.imul(L,ee)|0)+Math.imul(j,Q)|0,o=o+Math.imul(j,ee)|0,r=r+Math.imul(P,ne)|0,i=(i=i+Math.imul(P,re)|0)+Math.imul(N,ne)|0,o=o+Math.imul(N,re)|0,r=r+Math.imul(x,oe)|0,i=(i=i+Math.imul(x,se)|0)+Math.imul(C,oe)|0,o=o+Math.imul(C,se)|0,r=r+Math.imul(I,ue)|0,i=(i=i+Math.imul(I,ce)|0)+Math.imul(k,ue)|0,o=o+Math.imul(k,ce)|0,r=r+Math.imul(E,le)|0,i=(i=i+Math.imul(E,de)|0)+Math.imul(M,le)|0,o=o+Math.imul(M,de)|0;var Oe=(c+(r=r+Math.imul(w,pe)|0)|0)+((8191&(i=(i=i+Math.imul(w,ve)|0)+Math.imul(_,pe)|0))<<13)|0;c=((o=o+Math.imul(_,ve)|0)+(i>>>13)|0)+(Oe>>>26)|0,Oe&=67108863,r=Math.imul(U,Q),i=(i=Math.imul(U,ee))+Math.imul(B,Q)|0,o=Math.imul(B,ee),r=r+Math.imul(L,ne)|0,i=(i=i+Math.imul(L,re)|0)+Math.imul(j,ne)|0,o=o+Math.imul(j,re)|0,r=r+Math.imul(P,oe)|0,i=(i=i+Math.imul(P,se)|0)+Math.imul(N,oe)|0,o=o+Math.imul(N,se)|0,r=r+Math.imul(x,ue)|0,i=(i=i+Math.imul(x,ce)|0)+Math.imul(C,ue)|0,o=o+Math.imul(C,ce)|0,r=r+Math.imul(I,le)|0,i=(i=i+Math.imul(I,de)|0)+Math.imul(k,le)|0,o=o+Math.imul(k,de)|0;var xe=(c+(r=r+Math.imul(E,pe)|0)|0)+((8191&(i=(i=i+Math.imul(E,ve)|0)+Math.imul(M,pe)|0))<<13)|0;c=((o=o+Math.imul(M,ve)|0)+(i>>>13)|0)+(xe>>>26)|0,xe&=67108863,r=Math.imul(U,ne),i=(i=Math.imul(U,re))+Math.imul(B,ne)|0,o=Math.imul(B,re),r=r+Math.imul(L,oe)|0,i=(i=i+Math.imul(L,se)|0)+Math.imul(j,oe)|0,o=o+Math.imul(j,se)|0,r=r+Math.imul(P,ue)|0,i=(i=i+Math.imul(P,ce)|0)+Math.imul(N,ue)|0,o=o+Math.imul(N,ce)|0,r=r+Math.imul(x,le)|0,i=(i=i+Math.imul(x,de)|0)+Math.imul(C,le)|0,o=o+Math.imul(C,de)|0;var Ce=(c+(r=r+Math.imul(I,pe)|0)|0)+((8191&(i=(i=i+Math.imul(I,ve)|0)+Math.imul(k,pe)|0))<<13)|0;c=((o=o+Math.imul(k,ve)|0)+(i>>>13)|0)+(Ce>>>26)|0,Ce&=67108863,r=Math.imul(U,oe),i=(i=Math.imul(U,se))+Math.imul(B,oe)|0,o=Math.imul(B,se),r=r+Math.imul(L,ue)|0,i=(i=i+Math.imul(L,ce)|0)+Math.imul(j,ue)|0,o=o+Math.imul(j,ce)|0,r=r+Math.imul(P,le)|0,i=(i=i+Math.imul(P,de)|0)+Math.imul(N,le)|0,o=o+Math.imul(N,de)|0;var Te=(c+(r=r+Math.imul(x,pe)|0)|0)+((8191&(i=(i=i+Math.imul(x,ve)|0)+Math.imul(C,pe)|0))<<13)|0;c=((o=o+Math.imul(C,ve)|0)+(i>>>13)|0)+(Te>>>26)|0,Te&=67108863,r=Math.imul(U,ue),i=(i=Math.imul(U,ce))+Math.imul(B,ue)|0,o=Math.imul(B,ce),r=r+Math.imul(L,le)|0,i=(i=i+Math.imul(L,de)|0)+Math.imul(j,le)|0,o=o+Math.imul(j,de)|0;var Pe=(c+(r=r+Math.imul(P,pe)|0)|0)+((8191&(i=(i=i+Math.imul(P,ve)|0)+Math.imul(N,pe)|0))<<13)|0;c=((o=o+Math.imul(N,ve)|0)+(i>>>13)|0)+(Pe>>>26)|0,Pe&=67108863,r=Math.imul(U,le),i=(i=Math.imul(U,de))+Math.imul(B,le)|0,o=Math.imul(B,de);var Ne=(c+(r=r+Math.imul(L,pe)|0)|0)+((8191&(i=(i=i+Math.imul(L,ve)|0)+Math.imul(j,pe)|0))<<13)|0;c=((o=o+Math.imul(j,ve)|0)+(i>>>13)|0)+(Ne>>>26)|0,Ne&=67108863;var Re=(c+(r=Math.imul(U,pe))|0)+((8191&(i=(i=Math.imul(U,ve))+Math.imul(B,pe)|0))<<13)|0;return c=((o=Math.imul(B,ve))+(i>>>13)|0)+(Re>>>26)|0,Re&=67108863,u[0]=ge,u[1]=me,u[2]=be,u[3]=ye,u[4]=we,u[5]=_e,u[6]=Se,u[7]=Ee,u[8]=Me,u[9]=Ae,u[10]=Ie,u[11]=ke,u[12]=Oe,u[13]=xe,u[14]=Ce,u[15]=Te,u[16]=Pe,u[17]=Ne,u[18]=Re,0!==c&&(u[19]=c,n.length++),n};function g(e,t,n){n.negative=t.negative^e.negative,n.length=e.length+t.length;for(var r=0,i=0,o=0;o<n.length-1;o++){var s=i;i=0;for(var a=67108863&r,u=Math.min(o,t.length-1),c=Math.max(0,o-e.length+1);c<=u;c++){var f=o-c,l=(0|e.words[f])*(0|t.words[c]),d=67108863&l;a=67108863&(d=d+a|0),i+=(s=(s=s+(l/67108864|0)|0)+(d>>>26)|0)>>>26,s&=67108863}n.words[o]=a,r=s,s=i}return 0!==r?n.words[o]=r:n.length--,n._strip()}function m(e,t,n){return g(e,t,n)}function b(e,t){this.x=e,this.y=t}Math.imul||(v=p),o.prototype.mulTo=function(e,t){var n=this.length+e.length;return 10===this.length&&10===e.length?v(this,e,t):n<63?p(this,e,t):n<1024?g(this,e,t):m(this,e,t)},b.prototype.makeRBT=function(e){for(var t=new Array(e),n=o.prototype._countBits(e)-1,r=0;r<e;r++)t[r]=this.revBin(r,n,e);return t},b.prototype.revBin=function(e,t,n){if(0===e||e===n-1)return e;for(var r=0,i=0;i<t;i++)r|=(1&e)<<t-i-1,e>>=1;return r},b.prototype.permute=function(e,t,n,r,i,o){for(var s=0;s<o;s++)r[s]=t[e[s]],i[s]=n[e[s]]},b.prototype.transform=function(e,t,n,r,i,o){this.permute(o,e,t,n,r,i);for(var s=1;s<i;s<<=1)for(var a=s<<1,u=Math.cos(2*Math.PI/a),c=Math.sin(2*Math.PI/a),f=0;f<i;f+=a)for(var l=u,d=c,h=0;h<s;h++){var p=n[f+h],v=r[f+h],g=n[f+h+s],m=r[f+h+s],b=l*g-d*m;m=l*m+d*g,g=b,n[f+h]=p+g,r[f+h]=v+m,n[f+h+s]=p-g,r[f+h+s]=v-m,h!==a&&(b=u*l-c*d,d=u*d+c*l,l=b)}},b.prototype.guessLen13b=function(e,t){var n=1|Math.max(t,e),r=1&n,i=0;for(n=n/2|0;n;n>>>=1)i++;return 1<<i+1+r},b.prototype.conjugate=function(e,t,n){if(!(n<=1))for(var r=0;r<n/2;r++){var i=e[r];e[r]=e[n-r-1],e[n-r-1]=i,i=t[r],t[r]=-t[n-r-1],t[n-r-1]=-i}},b.prototype.normalize13b=function(e,t){for(var n=0,r=0;r<t/2;r++){var i=8192*Math.round(e[2*r+1]/t)+Math.round(e[2*r]/t)+n;e[r]=67108863&i,n=i<67108864?0:i/67108864|0}return e},b.prototype.convert13b=function(e,t,n,i){for(var o=0,s=0;s<t;s++)o+=0|e[s],n[2*s]=8191&o,o>>>=13,n[2*s+1]=8191&o,o>>>=13;for(s=2*t;s<i;++s)n[s]=0;r(0===o),r(0==(-8192&o))},b.prototype.stub=function(e){for(var t=new Array(e),n=0;n<e;n++)t[n]=0;return t},b.prototype.mulp=function(e,t,n){var r=2*this.guessLen13b(e.length,t.length),i=this.makeRBT(r),o=this.stub(r),s=new Array(r),a=new Array(r),u=new Array(r),c=new Array(r),f=new Array(r),l=new Array(r),d=n.words;d.length=r,this.convert13b(e.words,e.length,s,r),this.convert13b(t.words,t.length,c,r),this.transform(s,o,a,u,r,i),this.transform(c,o,f,l,r,i);for(var h=0;h<r;h++){var p=a[h]*f[h]-u[h]*l[h];u[h]=a[h]*l[h]+u[h]*f[h],a[h]=p}return this.conjugate(a,u,r),this.transform(a,u,d,o,r,i),this.conjugate(d,o,r),this.normalize13b(d,r),n.negative=e.negative^t.negative,n.length=e.length+t.length,n._strip()},o.prototype.mul=function(e){var t=new o(null);return t.words=new Array(this.length+e.length),this.mulTo(e,t)},o.prototype.mulf=function(e){var t=new o(null);return t.words=new Array(this.length+e.length),m(this,e,t)},o.prototype.imul=function(e){return this.clone().mulTo(e,this)},o.prototype.imuln=function(e){var t=e<0;t&&(e=-e),r("number"==typeof e),r(e<67108864);for(var n=0,i=0;i<this.length;i++){var o=(0|this.words[i])*e,s=(67108863&o)+(67108863&n);n>>=26,n+=o/67108864|0,n+=s>>>26,this.words[i]=67108863&s}return 0!==n&&(this.words[i]=n,this.length++),t?this.ineg():this},o.prototype.muln=function(e){return this.clone().imuln(e)},o.prototype.sqr=function(){return this.mul(this)},o.prototype.isqr=function(){return this.imul(this.clone())},o.prototype.pow=function(e){var t=function(e){for(var t=new Array(e.bitLength()),n=0;n<t.length;n++){var r=n/26|0,i=n%26;t[n]=e.words[r]>>>i&1}return t}(e);if(0===t.length)return new o(1);for(var n=this,r=0;r<t.length&&0===t[r];r++,n=n.sqr());if(++r<t.length)for(var i=n.sqr();r<t.length;r++,i=i.sqr())0!==t[r]&&(n=n.mul(i));return n},o.prototype.iushln=function(e){r("number"==typeof e&&e>=0);var t,n=e%26,i=(e-n)/26,o=67108863>>>26-n<<26-n;if(0!==n){var s=0;for(t=0;t<this.length;t++){var a=this.words[t]&o,u=(0|this.words[t])-a<<n;this.words[t]=u|s,s=a>>>26-n}s&&(this.words[t]=s,this.length++)}if(0!==i){for(t=this.length-1;t>=0;t--)this.words[t+i]=this.words[t];for(t=0;t<i;t++)this.words[t]=0;this.length+=i}return this._strip()},o.prototype.ishln=function(e){return r(0===this.negative),this.iushln(e)},o.prototype.iushrn=function(e,t,n){var i;r("number"==typeof e&&e>=0),i=t?(t-t%26)/26:0;var o=e%26,s=Math.min((e-o)/26,this.length),a=67108863^67108863>>>o<<o,u=n;if(i-=s,i=Math.max(0,i),u){for(var c=0;c<s;c++)u.words[c]=this.words[c];u.length=s}if(0===s);else if(this.length>s)for(this.length-=s,c=0;c<this.length;c++)this.words[c]=this.words[c+s];else this.words[0]=0,this.length=1;var f=0;for(c=this.length-1;c>=0&&(0!==f||c>=i);c--){var l=0|this.words[c];this.words[c]=f<<26-o|l>>>o,f=l&a}return u&&0!==f&&(u.words[u.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this._strip()},o.prototype.ishrn=function(e,t,n){return r(0===this.negative),this.iushrn(e,t,n)},o.prototype.shln=function(e){return this.clone().ishln(e)},o.prototype.ushln=function(e){return this.clone().iushln(e)},o.prototype.shrn=function(e){return this.clone().ishrn(e)},o.prototype.ushrn=function(e){return this.clone().iushrn(e)},o.prototype.testn=function(e){r("number"==typeof e&&e>=0);var t=e%26,n=(e-t)/26,i=1<<t;return!(this.length<=n)&&!!(this.words[n]&i)},o.prototype.imaskn=function(e){r("number"==typeof e&&e>=0);var t=e%26,n=(e-t)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=n)return this;if(0!==t&&n++,this.length=Math.min(n,this.length),0!==t){var i=67108863^67108863>>>t<<t;this.words[this.length-1]&=i}return this._strip()},o.prototype.maskn=function(e){return this.clone().imaskn(e)},o.prototype.iaddn=function(e){return r("number"==typeof e),r(e<67108864),e<0?this.isubn(-e):0!==this.negative?1===this.length&&(0|this.words[0])<=e?(this.words[0]=e-(0|this.words[0]),this.negative=0,this):(this.negative=0,this.isubn(e),this.negative=1,this):this._iaddn(e)},o.prototype._iaddn=function(e){this.words[0]+=e;for(var t=0;t<this.length&&this.words[t]>=67108864;t++)this.words[t]-=67108864,t===this.length-1?this.words[t+1]=1:this.words[t+1]++;return this.length=Math.max(this.length,t+1),this},o.prototype.isubn=function(e){if(r("number"==typeof e),r(e<67108864),e<0)return this.iaddn(-e);if(0!==this.negative)return this.negative=0,this.iaddn(e),this.negative=1,this;if(this.words[0]-=e,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var t=0;t<this.length&&this.words[t]<0;t++)this.words[t]+=67108864,this.words[t+1]-=1;return this._strip()},o.prototype.addn=function(e){return this.clone().iaddn(e)},o.prototype.subn=function(e){return this.clone().isubn(e)},o.prototype.iabs=function(){return this.negative=0,this},o.prototype.abs=function(){return this.clone().iabs()},o.prototype._ishlnsubmul=function(e,t,n){var i,o,s=e.length+n;this._expand(s);var a=0;for(i=0;i<e.length;i++){o=(0|this.words[i+n])+a;var u=(0|e.words[i])*t;a=((o-=67108863&u)>>26)-(u/67108864|0),this.words[i+n]=67108863&o}for(;i<this.length-n;i++)a=(o=(0|this.words[i+n])+a)>>26,this.words[i+n]=67108863&o;if(0===a)return this._strip();for(r(-1===a),a=0,i=0;i<this.length;i++)a=(o=-(0|this.words[i])+a)>>26,this.words[i]=67108863&o;return this.negative=1,this._strip()},o.prototype._wordDiv=function(e,t){var n=(this.length,e.length),r=this.clone(),i=e,s=0|i.words[i.length-1];0!==(n=26-this._countBits(s))&&(i=i.ushln(n),r.iushln(n),s=0|i.words[i.length-1]);var a,u=r.length-i.length;if("mod"!==t){(a=new o(null)).length=u+1,a.words=new Array(a.length);for(var c=0;c<a.length;c++)a.words[c]=0}var f=r.clone()._ishlnsubmul(i,1,u);0===f.negative&&(r=f,a&&(a.words[u]=1));for(var l=u-1;l>=0;l--){var d=67108864*(0|r.words[i.length+l])+(0|r.words[i.length+l-1]);for(d=Math.min(d/s|0,67108863),r._ishlnsubmul(i,d,l);0!==r.negative;)d--,r.negative=0,r._ishlnsubmul(i,1,l),r.isZero()||(r.negative^=1);a&&(a.words[l]=d)}return a&&a._strip(),r._strip(),"div"!==t&&0!==n&&r.iushrn(n),{div:a||null,mod:r}},o.prototype.divmod=function(e,t,n){return r(!e.isZero()),this.isZero()?{div:new o(0),mod:new o(0)}:0!==this.negative&&0===e.negative?(a=this.neg().divmod(e,t),"mod"!==t&&(i=a.div.neg()),"div"!==t&&(s=a.mod.neg(),n&&0!==s.negative&&s.iadd(e)),{div:i,mod:s}):0===this.negative&&0!==e.negative?(a=this.divmod(e.neg(),t),"mod"!==t&&(i=a.div.neg()),{div:i,mod:a.mod}):0!=(this.negative&e.negative)?(a=this.neg().divmod(e.neg(),t),"div"!==t&&(s=a.mod.neg(),n&&0!==s.negative&&s.isub(e)),{div:a.div,mod:s}):e.length>this.length||this.cmp(e)<0?{div:new o(0),mod:this}:1===e.length?"div"===t?{div:this.divn(e.words[0]),mod:null}:"mod"===t?{div:null,mod:new o(this.modrn(e.words[0]))}:{div:this.divn(e.words[0]),mod:new o(this.modrn(e.words[0]))}:this._wordDiv(e,t);var i,s,a},o.prototype.div=function(e){return this.divmod(e,"div",!1).div},o.prototype.mod=function(e){return this.divmod(e,"mod",!1).mod},o.prototype.umod=function(e){return this.divmod(e,"mod",!0).mod},o.prototype.divRound=function(e){var t=this.divmod(e);if(t.mod.isZero())return t.div;var n=0!==t.div.negative?t.mod.isub(e):t.mod,r=e.ushrn(1),i=e.andln(1),o=n.cmp(r);return o<0||1===i&&0===o?t.div:0!==t.div.negative?t.div.isubn(1):t.div.iaddn(1)},o.prototype.modrn=function(e){var t=e<0;t&&(e=-e),r(e<=67108863);for(var n=(1<<26)%e,i=0,o=this.length-1;o>=0;o--)i=(n*i+(0|this.words[o]))%e;return t?-i:i},o.prototype.modn=function(e){return this.modrn(e)},o.prototype.idivn=function(e){var t=e<0;t&&(e=-e),r(e<=67108863);for(var n=0,i=this.length-1;i>=0;i--){var o=(0|this.words[i])+67108864*n;this.words[i]=o/e|0,n=o%e}return this._strip(),t?this.ineg():this},o.prototype.divn=function(e){return this.clone().idivn(e)},o.prototype.egcd=function(e){r(0===e.negative),r(!e.isZero());var t=this,n=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i=new o(1),s=new o(0),a=new o(0),u=new o(1),c=0;t.isEven()&&n.isEven();)t.iushrn(1),n.iushrn(1),++c;for(var f=n.clone(),l=t.clone();!t.isZero();){for(var d=0,h=1;0==(t.words[0]&h)&&d<26;++d,h<<=1);if(d>0)for(t.iushrn(d);d-- >0;)(i.isOdd()||s.isOdd())&&(i.iadd(f),s.isub(l)),i.iushrn(1),s.iushrn(1);for(var p=0,v=1;0==(n.words[0]&v)&&p<26;++p,v<<=1);if(p>0)for(n.iushrn(p);p-- >0;)(a.isOdd()||u.isOdd())&&(a.iadd(f),u.isub(l)),a.iushrn(1),u.iushrn(1);t.cmp(n)>=0?(t.isub(n),i.isub(a),s.isub(u)):(n.isub(t),a.isub(i),u.isub(s))}return{a:a,b:u,gcd:n.iushln(c)}},o.prototype._invmp=function(e){r(0===e.negative),r(!e.isZero());var t=this,n=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i,s=new o(1),a=new o(0),u=n.clone();t.cmpn(1)>0&&n.cmpn(1)>0;){for(var c=0,f=1;0==(t.words[0]&f)&&c<26;++c,f<<=1);if(c>0)for(t.iushrn(c);c-- >0;)s.isOdd()&&s.iadd(u),s.iushrn(1);for(var l=0,d=1;0==(n.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(n.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(u),a.iushrn(1);t.cmp(n)>=0?(t.isub(n),s.isub(a)):(n.isub(t),a.isub(s))}return(i=0===t.cmpn(1)?s:a).cmpn(0)<0&&i.iadd(e),i},o.prototype.gcd=function(e){if(this.isZero())return e.abs();if(e.isZero())return this.abs();var t=this.clone(),n=e.clone();t.negative=0,n.negative=0;for(var r=0;t.isEven()&&n.isEven();r++)t.iushrn(1),n.iushrn(1);for(;;){for(;t.isEven();)t.iushrn(1);for(;n.isEven();)n.iushrn(1);var i=t.cmp(n);if(i<0){var o=t;t=n,n=o}else if(0===i||0===n.cmpn(1))break;t.isub(n)}return n.iushln(r)},o.prototype.invm=function(e){return this.egcd(e).a.umod(e)},o.prototype.isEven=function(){return 0==(1&this.words[0])},o.prototype.isOdd=function(){return 1==(1&this.words[0])},o.prototype.andln=function(e){return this.words[0]&e},o.prototype.bincn=function(e){r("number"==typeof e);var t=e%26,n=(e-t)/26,i=1<<t;if(this.length<=n)return this._expand(n+1),this.words[n]|=i,this;for(var o=i,s=n;0!==o&&s<this.length;s++){var a=0|this.words[s];o=(a+=o)>>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},o.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},o.prototype.cmpn=function(e){var t,n=e<0;if(0!==this.negative&&!n)return-1;if(0===this.negative&&n)return 1;if(this._strip(),this.length>1)t=1;else{n&&(e=-e),r(e<=67108863,"Number is too big");var i=0|this.words[0];t=i===e?0:i<e?-1:1}return 0!==this.negative?0|-t:t},o.prototype.cmp=function(e){if(0!==this.negative&&0===e.negative)return-1;if(0===this.negative&&0!==e.negative)return 1;var t=this.ucmp(e);return 0!==this.negative?0|-t:t},o.prototype.ucmp=function(e){if(this.length>e.length)return 1;if(this.length<e.length)return-1;for(var t=0,n=this.length-1;n>=0;n--){var r=0|this.words[n],i=0|e.words[n];if(r!==i){r<i?t=-1:r>i&&(t=1);break}}return t},o.prototype.gtn=function(e){return 1===this.cmpn(e)},o.prototype.gt=function(e){return 1===this.cmp(e)},o.prototype.gten=function(e){return this.cmpn(e)>=0},o.prototype.gte=function(e){return this.cmp(e)>=0},o.prototype.ltn=function(e){return-1===this.cmpn(e)},o.prototype.lt=function(e){return-1===this.cmp(e)},o.prototype.lten=function(e){return this.cmpn(e)<=0},o.prototype.lte=function(e){return this.cmp(e)<=0},o.prototype.eqn=function(e){return 0===this.cmpn(e)},o.prototype.eq=function(e){return 0===this.cmp(e)},o.red=function(e){return new A(e)},o.prototype.toRed=function(e){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),e.convertTo(this)._forceRed(e)},o.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},o.prototype._forceRed=function(e){return this.red=e,this},o.prototype.forceRed=function(e){return r(!this.red,"Already a number in reduction context"),this._forceRed(e)},o.prototype.redAdd=function(e){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,e)},o.prototype.redIAdd=function(e){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,e)},o.prototype.redSub=function(e){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,e)},o.prototype.redISub=function(e){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,e)},o.prototype.redShl=function(e){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,e)},o.prototype.redMul=function(e){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.mul(this,e)},o.prototype.redIMul=function(e){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.imul(this,e)},o.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},o.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},o.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},o.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},o.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},o.prototype.redPow=function(e){return r(this.red&&!e.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,e)};var y={k256:null,p224:null,p192:null,p25519:null};function w(e,t){this.name=e,this.p=new o(t,16),this.n=this.p.bitLength(),this.k=new o(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function _(){w.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function S(){w.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function E(){w.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){w.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function A(e){if("string"==typeof e){var t=o._prime(e);this.m=t.p,this.prime=t}else r(e.gtn(1),"modulus must be greater than 1"),this.m=e,this.prime=null}function I(e){A.call(this,e),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new o(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}w.prototype._tmp=function(){var e=new o(null);return e.words=new Array(Math.ceil(this.n/13)),e},w.prototype.ireduce=function(e){var t,n=e;do{this.split(n,this.tmp),t=(n=(n=this.imulK(n)).iadd(this.tmp)).bitLength()}while(t>this.n);var r=t<this.n?-1:n.ucmp(this.p);return 0===r?(n.words[0]=0,n.length=1):r>0?n.isub(this.p):void 0!==n.strip?n.strip():n._strip(),n},w.prototype.split=function(e,t){e.iushrn(this.n,0,t)},w.prototype.imulK=function(e){return e.imul(this.k)},i(_,w),_.prototype.split=function(e,t){for(var n=Math.min(e.length,9),r=0;r<n;r++)t.words[r]=e.words[r];if(t.length=n,e.length<=9)return e.words[0]=0,void(e.length=1);var i=e.words[9];for(t.words[t.length++]=4194303&i,r=10;r<e.length;r++){var o=0|e.words[r];e.words[r-10]=(4194303&o)<<4|i>>>22,i=o}i>>>=22,e.words[r-10]=i,0===i&&e.length>10?e.length-=10:e.length-=9},_.prototype.imulK=function(e){e.words[e.length]=0,e.words[e.length+1]=0,e.length+=2;for(var t=0,n=0;n<e.length;n++){var r=0|e.words[n];t+=977*r,e.words[n]=67108863&t,t=64*r+(t/67108864|0)}return 0===e.words[e.length-1]&&(e.length--,0===e.words[e.length-1]&&e.length--),e},i(S,w),i(E,w),i(M,w),M.prototype.imulK=function(e){for(var t=0,n=0;n<e.length;n++){var r=19*(0|e.words[n])+t,i=67108863&r;r>>>=26,e.words[n]=i,t=r}return 0!==t&&(e.words[e.length++]=t),e},o._prime=function(e){if(y[e])return y[e];var t;if("k256"===e)t=new _;else if("p224"===e)t=new S;else if("p192"===e)t=new E;else{if("p25519"!==e)throw new Error("Unknown prime "+e);t=new M}return y[e]=t,t},A.prototype._verify1=function(e){r(0===e.negative,"red works only with positives"),r(e.red,"red works only with red numbers")},A.prototype._verify2=function(e,t){r(0==(e.negative|t.negative),"red works only with positives"),r(e.red&&e.red===t.red,"red works only with red numbers")},A.prototype.imod=function(e){return this.prime?this.prime.ireduce(e)._forceRed(this):(c(e,e.umod(this.m)._forceRed(this)),e)},A.prototype.neg=function(e){return e.isZero()?e.clone():this.m.sub(e)._forceRed(this)},A.prototype.add=function(e,t){this._verify2(e,t);var n=e.add(t);return n.cmp(this.m)>=0&&n.isub(this.m),n._forceRed(this)},A.prototype.iadd=function(e,t){this._verify2(e,t);var n=e.iadd(t);return n.cmp(this.m)>=0&&n.isub(this.m),n},A.prototype.sub=function(e,t){this._verify2(e,t);var n=e.sub(t);return n.cmpn(0)<0&&n.iadd(this.m),n._forceRed(this)},A.prototype.isub=function(e,t){this._verify2(e,t);var n=e.isub(t);return n.cmpn(0)<0&&n.iadd(this.m),n},A.prototype.shl=function(e,t){return this._verify1(e),this.imod(e.ushln(t))},A.prototype.imul=function(e,t){return this._verify2(e,t),this.imod(e.imul(t))},A.prototype.mul=function(e,t){return this._verify2(e,t),this.imod(e.mul(t))},A.prototype.isqr=function(e){return this.imul(e,e.clone())},A.prototype.sqr=function(e){return this.mul(e,e)},A.prototype.sqrt=function(e){if(e.isZero())return e.clone();var t=this.m.andln(3);if(r(t%2==1),3===t){var n=this.m.add(new o(1)).iushrn(2);return this.pow(e,n)}for(var i=this.m.subn(1),s=0;!i.isZero()&&0===i.andln(1);)s++,i.iushrn(1);r(!i.isZero());var a=new o(1).toRed(this),u=a.redNeg(),c=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new o(2*f*f).toRed(this);0!==this.pow(f,c).cmp(u);)f.redIAdd(u);for(var l=this.pow(f,i),d=this.pow(e,i.addn(1).iushrn(1)),h=this.pow(e,i),p=s;0!==h.cmp(a);){for(var v=h,g=0;0!==v.cmp(a);g++)v=v.redSqr();r(g<p);var m=this.pow(l,new o(1).iushln(p-g-1));d=d.redMul(m),l=m.redSqr(),h=h.redMul(l),p=g}return d},A.prototype.invm=function(e){var t=e._invmp(this.m);return 0!==t.negative?(t.negative=0,this.imod(t).redNeg()):this.imod(t)},A.prototype.pow=function(e,t){if(t.isZero())return new o(1).toRed(this);if(0===t.cmpn(1))return e.clone();var n=new Array(16);n[0]=new o(1).toRed(this),n[1]=e;for(var r=2;r<n.length;r++)n[r]=this.mul(n[r-1],e);var i=n[0],s=0,a=0,u=t.bitLength()%26;for(0===u&&(u=26),r=t.length-1;r>=0;r--){for(var c=t.words[r],f=u-1;f>=0;f--){var l=c>>f&1;i!==n[0]&&(i=this.sqr(i)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===r&&0===f)&&(i=this.mul(i,n[s]),a=0,s=0)):a=0}u=26}return i},A.prototype.convertTo=function(e){var t=e.umod(this.m);return t===e?t.clone():t},A.prototype.convertFrom=function(e){var t=e.clone();return t.red=null,t},o.mont=function(e){return new I(e)},i(I,A),I.prototype.convertTo=function(e){return this.imod(e.ushln(this.shift))},I.prototype.convertFrom=function(e){var t=this.imod(e.mul(this.rinv));return t.red=null,t},I.prototype.imul=function(e,t){if(e.isZero()||t.isZero())return e.words[0]=0,e.length=1,e;var n=e.imul(t),r=n.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=n.isub(r).iushrn(this.shift),o=i;return i.cmp(this.m)>=0?o=i.isub(this.m):i.cmpn(0)<0&&(o=i.iadd(this.m)),o._forceRed(this)},I.prototype.mul=function(e,t){if(e.isZero()||t.isZero())return new o(0)._forceRed(this);var n=e.mul(t),r=n.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=n.isub(r).iushrn(this.shift),s=i;return i.cmp(this.m)>=0?s=i.isub(this.m):i.cmpn(0)<0&&(s=i.iadd(this.m)),s._forceRed(this)},I.prototype.invm=function(e){return this.imod(e._invmp(this.m).mul(this.r2))._forceRed(this)}}(e,this)}).call(this,n(57)(e))},function(e,t,n){"use strict";const r=t;r.bignum=n(29),r.define=n(351).define,r.base=n(354),r.constants=n(355),r.decoders=n(207),r.encoders=n(205)},function(e,t,n){"use strict";const r=t;r.der=n(206),r.pem=n(352)},function(e,t,n){"use strict";const r=n(7),i=n(130).Buffer,o=n(131),s=n(133);function a(e){this.enc="der",this.name=e.name,this.entity=e,this.tree=new u,this.tree._init(e.body)}function u(e){o.call(this,"der",e)}function c(e){return e<10?"0"+e:e}e.exports=a,a.prototype.encode=function(e,t){return this.tree._encode(e,t).join()},r(u,o),u.prototype._encodeComposite=function(e,t,n,r){const o=function(e,t,n,r){let i;"seqof"===e?e="seq":"setof"===e&&(e="set");if(s.tagByName.hasOwnProperty(e))i=s.tagByName[e];else{if("number"!=typeof e||(0|e)!==e)return r.error("Unknown tag: "+e);i=e}if(i>=31)return r.error("Multi-octet tag encoding unsupported");t||(i|=32);return i|=s.tagClassByName[n||"universal"]<<6,i}(e,t,n,this.reporter);if(r.length<128){const e=i.alloc(2);return e[0]=o,e[1]=r.length,this._createEncoderBuffer([e,r])}let a=1;for(let e=r.length;e>=256;e>>=8)a++;const u=i.alloc(2+a);u[0]=o,u[1]=128|a;for(let e=1+a,t=r.length;t>0;e--,t>>=8)u[e]=255&t;return this._createEncoderBuffer([u,r])},u.prototype._encodeStr=function(e,t){if("bitstr"===t)return this._createEncoderBuffer([0|e.unused,e.data]);if("bmpstr"===t){const t=i.alloc(2*e.length);for(let n=0;n<e.length;n++)t.writeUInt16BE(e.charCodeAt(n),2*n);return this._createEncoderBuffer(t)}return"numstr"===t?this._isNumstr(e)?this._createEncoderBuffer(e):this.reporter.error("Encoding of string type: numstr supports only digits and space"):"printstr"===t?this._isPrintstr(e)?this._createEncoderBuffer(e):this.reporter.error("Encoding of string type: printstr supports only latin upper and lower case letters, digits, space, apostrophe, left and rigth parenthesis, plus sign, comma, hyphen, dot, slash, colon, equal sign, question mark"):/str$/.test(t)||"objDesc"===t?this._createEncoderBuffer(e):this.reporter.error("Encoding of string type: "+t+" unsupported")},u.prototype._encodeObjid=function(e,t,n){if("string"==typeof e){if(!t)return this.reporter.error("string objid given, but no values map found");if(!t.hasOwnProperty(e))return this.reporter.error("objid not found in values map");e=t[e].split(/[\s.]+/g);for(let t=0;t<e.length;t++)e[t]|=0}else if(Array.isArray(e)){e=e.slice();for(let t=0;t<e.length;t++)e[t]|=0}if(!Array.isArray(e))return this.reporter.error("objid() should be either array or string, got: "+JSON.stringify(e));if(!n){if(e[1]>=40)return this.reporter.error("Second objid identifier OOB");e.splice(0,2,40*e[0]+e[1])}let r=0;for(let t=0;t<e.length;t++){let n=e[t];for(r++;n>=128;n>>=7)r++}const o=i.alloc(r);let s=o.length-1;for(let t=e.length-1;t>=0;t--){let n=e[t];for(o[s--]=127&n;(n>>=7)>0;)o[s--]=128|127&n}return this._createEncoderBuffer(o)},u.prototype._encodeTime=function(e,t){let n;const r=new Date(e);return"gentime"===t?n=[c(r.getUTCFullYear()),c(r.getUTCMonth()+1),c(r.getUTCDate()),c(r.getUTCHours()),c(r.getUTCMinutes()),c(r.getUTCSeconds()),"Z"].join(""):"utctime"===t?n=[c(r.getUTCFullYear()%100),c(r.getUTCMonth()+1),c(r.getUTCDate()),c(r.getUTCHours()),c(r.getUTCMinutes()),c(r.getUTCSeconds()),"Z"].join(""):this.reporter.error("Encoding "+t+" time is not supported yet"),this._encodeStr(n,"octstr")},u.prototype._encodeNull=function(){return this._createEncoderBuffer("")},u.prototype._encodeInt=function(e,t){if("string"==typeof e){if(!t)return this.reporter.error("String int or enum given, but no values map");if(!t.hasOwnProperty(e))return this.reporter.error("Values map doesn't contain: "+JSON.stringify(e));e=t[e]}if("number"!=typeof e&&!i.isBuffer(e)){const t=e.toArray();!e.sign&&128&t[0]&&t.unshift(0),e=i.from(t)}if(i.isBuffer(e)){let t=e.length;0===e.length&&t++;const n=i.alloc(t);return e.copy(n),0===e.length&&(n[0]=0),this._createEncoderBuffer(n)}if(e<128)return this._createEncoderBuffer(e);if(e<256)return this._createEncoderBuffer([0,e]);let n=1;for(let t=e;t>=256;t>>=8)n++;const r=new Array(n);for(let t=r.length-1;t>=0;t--)r[t]=255&e,e>>=8;return 128&r[0]&&r.unshift(0),this._createEncoderBuffer(i.from(r))},u.prototype._encodeBool=function(e){return this._createEncoderBuffer(e?255:0)},u.prototype._use=function(e,t){return"function"==typeof e&&(e=e(t)),e._getEncoder("der").tree},u.prototype._skipDefault=function(e,t,n){const r=this._baseState;let i;if(null===r.default)return!1;const o=e.join();if(void 0===r.defaultBuffer&&(r.defaultBuffer=this._encodeValue(r.default,t,n).join()),o.length!==r.defaultBuffer.length)return!1;for(i=0;i<o.length;i++)if(o[i]!==r.defaultBuffer[i])return!1;return!0}},function(e,t,n){"use strict";const r=t;r.der=n(208),r.pem=n(353)},function(e,t,n){"use strict";const r=n(7),i=n(29),o=n(83).DecoderBuffer,s=n(131),a=n(133);function u(e){this.enc="der",this.name=e.name,this.entity=e,this.tree=new c,this.tree._init(e.body)}function c(e){s.call(this,"der",e)}function f(e,t){let n=e.readUInt8(t);if(e.isError(n))return n;const r=a.tagClass[n>>6],i=0==(32&n);if(31==(31&n)){let r=n;for(n=0;128==(128&r);){if(r=e.readUInt8(t),e.isError(r))return r;n<<=7,n|=127&r}}else n&=31;return{cls:r,primitive:i,tag:n,tagStr:a.tag[n]}}function l(e,t,n){let r=e.readUInt8(n);if(e.isError(r))return r;if(!t&&128===r)return null;if(0==(128&r))return r;const i=127&r;if(i>4)return e.error("length octect is too long");r=0;for(let t=0;t<i;t++){r<<=8;const t=e.readUInt8(n);if(e.isError(t))return t;r|=t}return r}e.exports=u,u.prototype.decode=function(e,t){return o.isDecoderBuffer(e)||(e=new o(e,t)),this.tree._decode(e,t)},r(c,s),c.prototype._peekTag=function(e,t,n){if(e.isEmpty())return!1;const r=e.save(),i=f(e,'Failed to peek tag: "'+t+'"');return e.isError(i)?i:(e.restore(r),i.tag===t||i.tagStr===t||i.tagStr+"of"===t||n)},c.prototype._decodeTag=function(e,t,n){const r=f(e,'Failed to decode tag of "'+t+'"');if(e.isError(r))return r;let i=l(e,r.primitive,'Failed to get length of "'+t+'"');if(e.isError(i))return i;if(!n&&r.tag!==t&&r.tagStr!==t&&r.tagStr+"of"!==t)return e.error('Failed to match tag: "'+t+'"');if(r.primitive||null!==i)return e.skip(i,'Failed to match body of: "'+t+'"');const o=e.save(),s=this._skipUntilEnd(e,'Failed to skip indefinite length body: "'+this.tag+'"');return e.isError(s)?s:(i=e.offset-o.offset,e.restore(o),e.skip(i,'Failed to match body of: "'+t+'"'))},c.prototype._skipUntilEnd=function(e,t){for(;;){const n=f(e,t);if(e.isError(n))return n;const r=l(e,n.primitive,t);if(e.isError(r))return r;let i;if(i=n.primitive||null!==r?e.skip(r):this._skipUntilEnd(e,t),e.isError(i))return i;if("end"===n.tagStr)break}},c.prototype._decodeList=function(e,t,n,r){const i=[];for(;!e.isEmpty();){const t=this._peekTag(e,"end");if(e.isError(t))return t;const o=n.decode(e,"der",r);if(e.isError(o)&&t)break;i.push(o)}return i},c.prototype._decodeStr=function(e,t){if("bitstr"===t){const t=e.readUInt8();return e.isError(t)?t:{unused:t,data:e.raw()}}if("bmpstr"===t){const t=e.raw();if(t.length%2==1)return e.error("Decoding of string type: bmpstr length mismatch");let n="";for(let e=0;e<t.length/2;e++)n+=String.fromCharCode(t.readUInt16BE(2*e));return n}if("numstr"===t){const t=e.raw().toString("ascii");return this._isNumstr(t)?t:e.error("Decoding of string type: numstr unsupported characters")}if("octstr"===t)return e.raw();if("objDesc"===t)return e.raw();if("printstr"===t){const t=e.raw().toString("ascii");return this._isPrintstr(t)?t:e.error("Decoding of string type: printstr unsupported characters")}return/str$/.test(t)?e.raw().toString():e.error("Decoding of string type: "+t+" unsupported")},c.prototype._decodeObjid=function(e,t,n){let r;const i=[];let o=0,s=0;for(;!e.isEmpty();)s=e.readUInt8(),o<<=7,o|=127&s,0==(128&s)&&(i.push(o),o=0);128&s&&i.push(o);const a=i[0]/40|0,u=i[0]%40;if(r=n?i:[a,u].concat(i.slice(1)),t){let e=t[r.join(" ")];void 0===e&&(e=t[r.join(".")]),void 0!==e&&(r=e)}return r},c.prototype._decodeTime=function(e,t){const n=e.raw().toString();let r,i,o,s,a,u;if("gentime"===t)r=0|n.slice(0,4),i=0|n.slice(4,6),o=0|n.slice(6,8),s=0|n.slice(8,10),a=0|n.slice(10,12),u=0|n.slice(12,14);else{if("utctime"!==t)return e.error("Decoding "+t+" time is not supported yet");r=0|n.slice(0,2),i=0|n.slice(2,4),o=0|n.slice(4,6),s=0|n.slice(6,8),a=0|n.slice(8,10),u=0|n.slice(10,12),r=r<70?2e3+r:1900+r}return Date.UTC(r,i-1,o,s,a,u,0)},c.prototype._decodeNull=function(){return null},c.prototype._decodeBool=function(e){const t=e.readUInt8();return e.isError(t)?t:0!==t},c.prototype._decodeInt=function(e,t){const n=e.raw();let r=new i(n);return t&&(r=t[r.toString(10)]||r),r},c.prototype._use=function(e,t){return"function"==typeof e&&(e=e(t)),e._getDecoder("der").tree}},function(e){e.exports=JSON.parse('{"1.3.132.0.10":"secp256k1","1.3.132.0.33":"p224","1.2.840.10045.3.1.1":"p192","1.2.840.10045.3.1.7":"p256","1.3.132.0.34":"p384","1.3.132.0.35":"p521"}')},function(e,t,n){var r=n(79),i=n(8).Buffer;function o(e){var t=i.allocUnsafe(4);return t.writeUInt32BE(e,0),t}e.exports=function(e,t){for(var n,s=i.alloc(0),a=0;s.length<t;)n=o(a++),s=i.concat([s,r("sha1").update(e).update(n).digest()]);return s.slice(0,t)}},function(e,t){e.exports=function(e,t){for(var n=e.length,r=-1;++r<n;)e[r]^=t[r];return e}},function(e,t,n){var r=n(29),i=n(8).Buffer;e.exports=function(e,t){return i.from(e.toRed(r.mont(t.modulus)).redPow(new r(t.publicExponent)).fromRed().toArray())}},function(e,t,n){"use strict";n.r(t),t.default=function(e,t){return t=t||{},new Promise((function(n,r){var i=new XMLHttpRequest,o=[],s=[],a={},u=function(){return{ok:2==(i.status/100|0),statusText:i.statusText,status:i.status,url:i.responseURL,text:function(){return Promise.resolve(i.responseText)},json:function(){return Promise.resolve(i.responseText).then(JSON.parse)},blob:function(){return Promise.resolve(new Blob([i.response]))},clone:u,headers:{keys:function(){return o},entries:function(){return s},get:function(e){return a[e.toLowerCase()]},has:function(e){return e.toLowerCase()in a}}}};for(var c in i.open(t.method||"get",e,!0),i.onload=function(){i.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,(function(e,t,n){o.push(t=t.toLowerCase()),s.push([t,n]),a[t]=a[t]?a[t]+","+n:n})),n(u())},i.onerror=r,i.withCredentials="include"==t.credentials,t.headers)i.setRequestHeader(c,t.headers[c]);i.send(t.body||null)}))}},function(e,t){var n="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof window.msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto);if(n){var r=new Uint8Array(16);e.exports=function(){return n(r),r}}else{var i=new Array(16);e.exports=function(){for(var e,t=0;t<16;t++)0==(3&t)&&(e=4294967296*Math.random()),i[t]=e>>>((3&t)<<3)&255;return i}}},function(e,t){for(var n=[],r=0;r<256;++r)n[r]=(r+256).toString(16).substr(1);e.exports=function(e,t){var r=t||0,i=n;return[i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]]].join("")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Sha256=void 0;var r=n(217),i=n(218),o=n(249),s=n(134),a=function(){function e(e){e?(this.operation=function(e){return new Promise((function(t,n){var r=s.locateWindow().msCrypto.subtle.importKey("raw",u(e),i.SHA_256_HMAC_ALGO,!1,["sign"]);r.oncomplete=function(){r.result&&t(r.result),n("ImportKey completed without importing key.")},r.onerror=function(){n("ImportKey failed to import key.")}}))}(e).then((function(e){return s.locateWindow().msCrypto.subtle.sign(i.SHA_256_HMAC_ALGO,e)})),this.operation.catch((function(){}))):this.operation=Promise.resolve(s.locateWindow().msCrypto.subtle.digest("SHA-256"))}return e.prototype.update=function(e){var t=this;r.isEmptyData(e)||(this.operation=this.operation.then((function(n){return n.onerror=function(){t.operation=Promise.reject(new Error("Error encountered updating hash"))},n.process(u(e)),n})),this.operation.catch((function(){})))},e.prototype.digest=function(){return this.operation.then((function(e){return new Promise((function(t,n){e.onerror=function(){n("Error encountered finalizing hash")},e.oncomplete=function(){e.result&&t(new Uint8Array(e.result)),n("Error encountered finalizing hash")},e.finish()}))}))},e}();function u(e){return"string"==typeof e?o.fromUtf8(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}t.Sha256=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isEmptyData=void 0,t.isEmptyData=function(e){return"string"==typeof e?0===e.length:0===e.byteLength}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.EMPTY_DATA_SHA_256=t.SHA_256_HMAC_ALGO=t.SHA_256_HASH=void 0,t.SHA_256_HASH={name:"SHA-256"},t.SHA_256_HMAC_ALGO={name:"HMAC",hash:t.SHA_256_HASH},t.EMPTY_DATA_SHA_256=new Uint8Array([227,176,196,66,152,252,28,20,154,251,244,200,153,111,185,36,39,174,65,228,100,155,147,76,164,149,153,27,120,82,184,85])},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Sha256=void 0;var r=n(249),i=n(217),o=n(218),s=n(134),a=function(){function e(e){this.toHash=new Uint8Array(0),void 0!==e&&(this.key=new Promise((function(t,n){s.locateWindow().crypto.subtle.importKey("raw",u(e),o.SHA_256_HMAC_ALGO,!1,["sign"]).then(t,n)})),this.key.catch((function(){})))}return e.prototype.update=function(e){if(!i.isEmptyData(e)){var t=u(e),n=new Uint8Array(this.toHash.byteLength+t.byteLength);n.set(this.toHash,0),n.set(t,this.toHash.byteLength),this.toHash=n}},e.prototype.digest=function(){var e=this;return this.key?this.key.then((function(t){return s.locateWindow().crypto.subtle.sign(o.SHA_256_HMAC_ALGO,t,e.toHash).then((function(e){return new Uint8Array(e)}))})):i.isEmptyData(this.toHash)?Promise.resolve(o.EMPTY_DATA_SHA_256):Promise.resolve().then((function(){return s.locateWindow().crypto.subtle.digest(o.SHA_256_HASH,e.toHash)})).then((function(e){return Promise.resolve(new Uint8Array(e))}))},e}();function u(e){return"string"==typeof e?r.fromUtf8(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}t.Sha256=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.MAX_HASHABLE_LENGTH=t.INIT=t.KEY=t.DIGEST_LENGTH=t.BLOCK_SIZE=void 0,t.BLOCK_SIZE=64,t.DIGEST_LENGTH=32,t.KEY=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),t.INIT=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],t.MAX_HASHABLE_LENGTH=Math.pow(2,53)-1},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=new(n(44).a)("Parser"),i=function(e){var t,n={};if(e.aws_mobile_analytics_app_id){var i={AWSPinpoint:{appId:e.aws_mobile_analytics_app_id,region:e.aws_mobile_analytics_app_region}};n.Analytics=i}return(e.aws_cognito_identity_pool_id||e.aws_user_pools_id)&&(n.Auth={userPoolId:e.aws_user_pools_id,userPoolWebClientId:e.aws_user_pools_web_client_id,region:e.aws_cognito_region,identityPoolId:e.aws_cognito_identity_pool_id,identityPoolRegion:e.aws_cognito_region,mandatorySignIn:"enable"===e.aws_mandatory_sign_in}),t=e.aws_user_files_s3_bucket?{AWSS3:{bucket:e.aws_user_files_s3_bucket,region:e.aws_user_files_s3_bucket_region,dangerouslyConnectToHttpEndpointForTesting:e.aws_user_files_s3_dangerously_connect_to_http_endpoint_for_testing}}:e?e.Storage||e:{},n.Analytics=Object.assign({},n.Analytics,e.Analytics),n.Auth=Object.assign({},n.Auth,e.Auth),n.Storage=Object.assign({},t),r.debug("parse config",e,"to amplifyconfig",n),n},o=function(){function e(){}return e.parseMobilehubConfig=i,e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BLOCK_SIZE=64,t.DIGEST_LENGTH=32,t.KEY=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),t.INIT=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],t.MAX_HASHABLE_LENGTH=Math.pow(2,53)-1},function(e,t,n){(function(t){var n="object"==typeof t&&t&&t.Object===Object&&t;e.exports=n}).call(this,n(31))},function(e,t,n){var r=n(84),i=n(225);e.exports=function(e){if(!i(e))return!1;var t=r(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},function(e,t){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},function(e,t){var n=Function.prototype.toString;e.exports=function(e){if(null!=e){try{return n.call(e)}catch(e){}try{return e+""}catch(e){}}return""}},function(e,t){e.exports=function(e,t){return e===t||e!=e&&t!=t}},function(e,t,n){var r=n(229),i=n(420),o=Object.prototype.hasOwnProperty;e.exports=function(e){if(!r(e))return i(e);var t=[];for(var n in Object(e))o.call(e,n)&&"constructor"!=n&&t.push(n);return t}},function(e,t){var n=Object.prototype;e.exports=function(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||n)}},function(e,t,n){var r=n(422),i=n(137),o=n(423),s=n(424),a=n(425),u=n(84),c=n(226),f=c(r),l=c(i),d=c(o),h=c(s),p=c(a),v=u;(r&&"[object DataView]"!=v(new r(new ArrayBuffer(1)))||i&&"[object Map]"!=v(new i)||o&&"[object Promise]"!=v(o.resolve())||s&&"[object Set]"!=v(new s)||a&&"[object WeakMap]"!=v(new a))&&(v=function(e){var t=u(e),n="[object Object]"==t?e.constructor:void 0,r=n?c(n):"";if(r)switch(r){case f:return"[object DataView]";case l:return"[object Map]";case d:return"[object Promise]";case h:return"[object Set]";case p:return"[object WeakMap]"}return t}),e.exports=v},function(e,t,n){var r=n(426),i=n(85),o=Object.prototype,s=o.hasOwnProperty,a=o.propertyIsEnumerable,u=r(function(){return arguments}())?r:function(e){return i(e)&&s.call(e,"callee")&&!a.call(e,"callee")};e.exports=u},function(e,t,n){var r=n(224),i=n(233);e.exports=function(e){return null!=e&&i(e.length)&&!r(e)}},function(e,t){e.exports=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}},function(e,t,n){var r=n(439),i=n(442),o=n(443);e.exports=function(e,t,n,s,a,u){var c=1&n,f=e.length,l=t.length;if(f!=l&&!(c&&l>f))return!1;var d=u.get(e),h=u.get(t);if(d&&h)return d==t&&h==e;var p=-1,v=!0,g=2&n?new r:void 0;for(u.set(e,t),u.set(t,e);++p<f;){var m=e[p],b=t[p];if(s)var y=c?s(b,m,p,t,e,u):s(m,b,p,e,t,u);if(void 0!==y){if(y)continue;v=!1;break}if(g){if(!i(t,(function(e,t){if(!o(g,t)&&(m===e||a(m,e,n,s,u)))return g.push(t)}))){v=!1;break}}else if(m!==b&&!a(m,b,n,s,u)){v=!1;break}}return u.delete(e),u.delete(t),v}},function(e,t,n){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";var r=n(45);function i(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,n){if(!t)return e;var o;if(n)o=n(t);else if(r.isURLSearchParams(t))o=t.toString();else{var s=[];r.forEach(t,(function(e,t){null!=e&&(r.isArray(e)?t+="[]":e=[e],r.forEach(e,(function(e){r.isDate(e)?e=e.toISOString():r.isObject(e)&&(e=JSON.stringify(e)),s.push(i(t)+"="+i(e))})))})),o=s.join("&")}if(o){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+o}return e}},function(e,t,n){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";(function(t){var r=n(45),i=n(470),o={"Content-Type":"application/x-www-form-urlencoded"};function s(e,t){!r.isUndefined(e)&&r.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}var a,u={adapter:(("undefined"!=typeof XMLHttpRequest||void 0!==t&&"[object process]"===Object.prototype.toString.call(t))&&(a=n(239)),a),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),r.isFormData(e)||r.isArrayBuffer(e)||r.isBuffer(e)||r.isStream(e)||r.isFile(e)||r.isBlob(e)?e:r.isArrayBufferView(e)?e.buffer:r.isURLSearchParams(e)?(s(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):r.isObject(e)?(s(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},r.forEach(["delete","get","head"],(function(e){u.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){u.headers[e]=r.merge(o)})),e.exports=u}).call(this,n(20))},function(e,t,n){"use strict";var r=n(45),i=n(471),o=n(473),s=n(236),a=n(474),u=n(477),c=n(478),f=n(240);e.exports=function(e){return new Promise((function(t,n){var l=e.data,d=e.headers;r.isFormData(l)&&delete d["Content-Type"];var h=new XMLHttpRequest;if(e.auth){var p=e.auth.username||"",v=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";d.Authorization="Basic "+btoa(p+":"+v)}var g=a(e.baseURL,e.url);if(h.open(e.method.toUpperCase(),s(g,e.params,e.paramsSerializer),!0),h.timeout=e.timeout,h.onreadystatechange=function(){if(h&&4===h.readyState&&(0!==h.status||h.responseURL&&0===h.responseURL.indexOf("file:"))){var r="getAllResponseHeaders"in h?u(h.getAllResponseHeaders()):null,o={data:e.responseType&&"text"!==e.responseType?h.response:h.responseText,status:h.status,statusText:h.statusText,headers:r,config:e,request:h};i(t,n,o),h=null}},h.onabort=function(){h&&(n(f("Request aborted",e,"ECONNABORTED",h)),h=null)},h.onerror=function(){n(f("Network Error",e,null,h)),h=null},h.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),n(f(t,e,"ECONNABORTED",h)),h=null},r.isStandardBrowserEnv()){var m=(e.withCredentials||c(g))&&e.xsrfCookieName?o.read(e.xsrfCookieName):void 0;m&&(d[e.xsrfHeaderName]=m)}if("setRequestHeader"in h&&r.forEach(d,(function(e,t){void 0===l&&"content-type"===t.toLowerCase()?delete d[t]:h.setRequestHeader(t,e)})),r.isUndefined(e.withCredentials)||(h.withCredentials=!!e.withCredentials),e.responseType)try{h.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&h.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&h.upload&&h.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then((function(e){h&&(h.abort(),n(e),h=null)})),l||(l=null),h.send(l)}))}},function(e,t,n){"use strict";var r=n(472);e.exports=function(e,t,n,i,o){var s=new Error(e);return r(s,t,n,i,o)}},function(e,t,n){"use strict";var r=n(45);e.exports=function(e,t){t=t||{};var n={},i=["url","method","data"],o=["headers","auth","proxy","params"],s=["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","timeoutMessage","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","decompress","maxContentLength","maxBodyLength","maxRedirects","transport","httpAgent","httpsAgent","cancelToken","socketPath","responseEncoding"],a=["validateStatus"];function u(e,t){return r.isPlainObject(e)&&r.isPlainObject(t)?r.merge(e,t):r.isPlainObject(t)?r.merge({},t):r.isArray(t)?t.slice():t}function c(i){r.isUndefined(t[i])?r.isUndefined(e[i])||(n[i]=u(void 0,e[i])):n[i]=u(e[i],t[i])}r.forEach(i,(function(e){r.isUndefined(t[e])||(n[e]=u(void 0,t[e]))})),r.forEach(o,c),r.forEach(s,(function(i){r.isUndefined(t[i])?r.isUndefined(e[i])||(n[i]=u(void 0,e[i])):n[i]=u(void 0,t[i])})),r.forEach(a,(function(r){r in t?n[r]=u(e[r],t[r]):r in e&&(n[r]=u(void 0,e[r]))}));var f=i.concat(o).concat(s).concat(a),l=Object.keys(e).concat(Object.keys(t)).filter((function(e){return-1===f.indexOf(e)}));return r.forEach(l,c),n}},function(e,t,n){"use strict";function r(e){this.message=e}r.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},r.prototype.__CANCEL__=!0,e.exports=r},function(e,t){var n="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof window.msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto);if(n){var r=new Uint8Array(16);e.exports=function(){return n(r),r}}else{var i=new Array(16);e.exports=function(){for(var e,t=0;t<16;t++)0==(3&t)&&(e=4294967296*Math.random()),i[t]=e>>>((3&t)<<3)&255;return i}}},function(e,t){for(var n=[],r=0;r<256;++r)n[r]=(r+256).toString(16).substr(1);e.exports=function(e,t){var r=t||0,i=n;return[i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],"-",i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]],i[e[r++]]].join("")}},function(e,t,n){"use strict";n.d(t,"a",(function(){return r}));var r=function(){function e(){}return e.createPredicateBuilder=function(t){var n=t.name,r=new Set(Object.keys(t.fields)),i=new Proxy({},{get:function(t,i,o){var s=i;if(!r.has(s))throw new Error("Invalid field for model. field: "+s+", model: "+n);return function(t){return e.sortPredicateGroupsMap.get(o).push({field:s,sortDirection:t}),o}}});return e.sortPredicateGroupsMap.set(i,[]),i},e.isValidPredicate=function(t){return e.sortPredicateGroupsMap.has(t)},e.getPredicates=function(t,n){if(void 0===n&&(n=!0),n&&!e.isValidPredicate(t))throw new Error("The predicate is not valid");return e.sortPredicateGroupsMap.get(t)},e.createFromExisting=function(t,n){if(n&&t)return n(e.createPredicateBuilder(t))},e.sortPredicateGroupsMap=new WeakMap,e}()},function(e,t,n){(function(e){var n,r,i,o;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)}o=function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==s(e)&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";function r(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}Object.defineProperty(t,"__esModule",{value:!0}),r(n(1)),r(n(2)),r(n(3)),r(n(4)),r(n(5)),r(n(6)),r(n(7)),r(n(8)),r(n(9)),r(n(10)),r(n(11)),r(n(12)),r(n(13))},function(e,t,n){e.exports={a:"Anchor__a___1_Iz8"}},function(e,t,n){e.exports={button:"Button__button___vS7Mv",signInButton:"Button__signInButton___3bUH-",googleSignInButton:"Button__googleSignInButton___1YiCu",signInButtonIcon:"Button__signInButtonIcon___ihN75",auth0SignInButton:"Button__auth0SignInButton___znnCj",facebookSignInButton:"Button__facebookSignInButton___34Txh",amazonSignInButton:"Button__amazonSignInButton___2EMtl",oAuthSignInButton:"Button__oAuthSignInButton___3UGOl",signInButtonContent:"Button__signInButtonContent___xqTXJ"}},function(e,t,n){e.exports={formContainer:"Form__formContainer___1GA3x",formSection:"Form__formSection___1PPvW",formField:"Form__formField___38Ikl",formRow:"Form__formRow___2mwRs"}},function(e,t,n){e.exports={hint:"Hint__hint___2XngB"}},function(e,t,n){e.exports={input:"Input__input___3e_bf",inputLabel:"Input__inputLabel___3VF0S",label:"Input__label___23sO8",radio:"Input__radio___2hllK"}},function(e,t,n){e.exports={navBar:"Nav__navBar___xtCFA",navRight:"Nav__navRight___1QG2J",nav:"Nav__nav___2Dx2Y",navItem:"Nav__navItem___1LtFQ"}},function(e,t,n){e.exports={photoPickerButton:"PhotoPicker__photoPickerButton___2XdVn",photoPlaceholder:"PhotoPicker__photoPlaceholder___2JXO4",photoPlaceholderIcon:"PhotoPicker__photoPlaceholderIcon___3Et71"}},function(e,t,n){e.exports={container:"Section__container___3YYTG",actionRow:"Section__actionRow___2LWSU",sectionHeader:"Section__sectionHeader___2djyg",sectionHeaderHint:"Section__sectionHeaderHint___3Wxdc",sectionBody:"Section__sectionBody___ihqqd",sectionHeaderContent:"Section__sectionHeaderContent___1UCqa",sectionFooter:"Section__sectionFooter___1T54C",sectionFooterPrimaryContent:"Section__sectionFooterPrimaryContent___2r9ZX",sectionFooterSecondaryContent:"Section__sectionFooterSecondaryContent___Nj41Q"}},function(e,t,n){e.exports={selectInput:"SelectInput__selectInput___3efO4"}},function(e,t,n){e.exports={strike:"Strike__strike___1XV1b",strikeContent:"Strike__strikeContent___10gLb"}},function(e,t,n){e.exports={toast:"Toast__toast___XXr3v",toastClose:"Toast__toastClose___18lU4"}},function(e,t,n){e.exports={totpQrcode:"Totp__totpQrcode___1crLx"}},function(e,t,n){e.exports={sumerianSceneContainer:"XR__sumerianSceneContainer___3nVMt",sumerianScene:"XR__sumerianScene___2Tt7-",loadingOverlay:"XR__loadingOverlay___IbqcI",loadingContainer:"XR__loadingContainer___2Itxb",loadingLogo:"XR__loadingLogo___Ub7xQ",loadingSceneName:"XR__loadingSceneName___3__ne",loadingBar:"XR__loadingBar___2vcke",loadingBarFill:"XR__loadingBarFill___3M-D9",sceneErrorText:"XR__sceneErrorText___2y0tp",sceneBar:"XR__sceneBar___2ShrP",sceneName:"XR__sceneName___1ApHr",sceneActions:"XR__sceneActions___7plGs",actionButton:"XR__actionButton___2poIM",tooltip:"XR__tooltip___UYyhn",actionIcon:"XR__actionIcon___2qnd2",autoShowTooltip:"XR__autoShowTooltip___V1QH7"}}])},"object"==s(t)&&"object"==s(e)?e.exports=o():(r=[],void 0===(i="function"==typeof(n=o)?n.apply(t,r):n)||(e.exports=i))}).call(this,n(57)(e))},function(e,t,n){"use strict";n.d(t,"a",(function(){return d}));var r=n(52),i=n(63),o=n(89),s=n(19),a=n(146),u=n(258),c=function(){return(c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},f=[i.a,o.a],l=[r.a,i.a,u.a];function d(e){void 0===e&&(e={});var t=e.modules,n=void 0===t?l:t,r=e.req,i=s.a.configure(),o=new s.b,u=new a.a({req:r});return f.forEach((function(e){n.includes(e)||o.register(new e.constructor)})),n.forEach((function(e){o.register(new e.constructor)})),o.configure(c(c({},i),{storage:u})),o}},function(e,t,n){"use strict";n.d(t,"b",(function(){return We})),n.d(t,"a",(function(){return $e}));var r=n(91),i={Name:[],Document:["definitions"],OperationDefinition:["name","variableDefinitions","directives","selectionSet"],VariableDefinition:["variable","type","defaultValue","directives"],Variable:["name"],SelectionSet:["selections"],Field:["alias","name","arguments","directives","selectionSet"],Argument:["name","value"],FragmentSpread:["name","directives"],InlineFragment:["typeCondition","directives","selectionSet"],FragmentDefinition:["name","variableDefinitions","typeCondition","directives","selectionSet"],IntValue:[],FloatValue:[],StringValue:[],BooleanValue:[],NullValue:[],EnumValue:[],ListValue:["values"],ObjectValue:["fields"],ObjectField:["name","value"],Directive:["name","arguments"],NamedType:["name"],ListType:["type"],NonNullType:["type"],SchemaDefinition:["directives","operationTypes"],OperationTypeDefinition:["type"],ScalarTypeDefinition:["description","name","directives"],ObjectTypeDefinition:["description","name","interfaces","directives","fields"],FieldDefinition:["description","name","arguments","type","directives"],InputValueDefinition:["description","name","type","defaultValue","directives"],InterfaceTypeDefinition:["description","name","directives","fields"],UnionTypeDefinition:["description","name","directives","types"],EnumTypeDefinition:["description","name","directives","values"],EnumValueDefinition:["description","name","directives"],InputObjectTypeDefinition:["description","name","directives","fields"],DirectiveDefinition:["description","name","arguments","locations"],SchemaExtension:["directives","operationTypes"],ScalarTypeExtension:["name","directives"],ObjectTypeExtension:["name","interfaces","directives","fields"],InterfaceTypeExtension:["name","directives","fields"],UnionTypeExtension:["name","directives","types"],EnumTypeExtension:["name","directives","values"],InputObjectTypeExtension:["name","directives","fields"]},o={};function s(e){return Boolean(e&&"string"==typeof e.kind)}function a(e,t,n){var r=e[t];if(r){if(!n&&"function"==typeof r)return r;var i=n?r.leave:r.enter;if("function"==typeof i)return i}else{var o=n?e.leave:e.enter;if(o){if("function"==typeof o)return o;var s=o[t];if("function"==typeof s)return s}}}function u(e){return function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:i,r=void 0,u=Array.isArray(e),c=[e],f=-1,l=[],d=void 0,h=void 0,p=void 0,v=[],g=[],m=e;do{var b=++f===c.length,y=b&&0!==l.length;if(b){if(h=0===g.length?void 0:v[v.length-1],d=p,p=g.pop(),y){if(u)d=d.slice();else{var w={};for(var _ in d)d.hasOwnProperty(_)&&(w[_]=d[_]);d=w}for(var S=0,E=0;E<l.length;E++){var M=l[E][0],A=l[E][1];u&&(M-=S),u&&null===A?(d.splice(M,1),S++):d[M]=A}}f=r.index,c=r.keys,l=r.edits,u=r.inArray,r=r.prev}else{if(h=p?u?f:c[f]:void 0,null==(d=p?p[h]:m))continue;p&&v.push(h)}var I=void 0;if(!Array.isArray(d)){if(!s(d))throw new Error("Invalid AST Node: "+JSON.stringify(d));var k=a(t,d.kind,b);if(k){if((I=k.call(t,d,h,p,v,g))===o)break;if(!1===I){if(!b){v.pop();continue}}else if(void 0!==I&&(l.push([h,I]),!b)){if(!s(I)){v.pop();continue}d=I}}}void 0===I&&y&&l.push([h,d]),b?v.pop():(r={inArray:u,index:f,keys:c,edits:l,prev:r},c=(u=Array.isArray(d))?d:n[d.kind]||[],f=-1,l=[],p&&g.push(p),p=d)}while(void 0!==r);return 0!==l.length&&(m=l[l.length-1][1]),m}(e,{leave:c})}var c={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return l(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=h("(",l(e.variableDefinitions,", "),")"),i=l(e.directives," "),o=e.selectionSet;return n||i||r||"query"!==t?l([t,l([n,r]),i,o]," "):o},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+h(" = ",r)+h(" ",l(i," "))},SelectionSet:function(e){return d(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,o=e.selectionSet;return l([h("",t,": ")+n+h("(",l(r,", "),")"),l(i," "),o]," ")},Argument:function(e){return e.name+": "+e.value},FragmentSpread:function(e){return"..."+e.name+h(" ",l(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return l(["...",h("on ",t),l(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,i=e.directives,o=e.selectionSet;return"fragment ".concat(t).concat(h("(",l(r,", "),")")," ")+"on ".concat(n," ").concat(h("",l(i," ")," "))+o},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?function(e,t){var n=e.replace(/"""/g,'\\"""');return" "!==e[0]&&"\t"!==e[0]||-1!==e.indexOf("\n")?'"""\n'.concat(t?n:p(n),'\n"""'):'"""'.concat(n.replace(/"$/,'"\n'),'"""')}(n,"description"===t):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+l(e.values,", ")+"]"},ObjectValue:function(e){return"{"+l(e.fields,", ")+"}"},ObjectField:function(e){return e.name+": "+e.value},Directive:function(e){return"@"+e.name+h("(",l(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:function(e){var t=e.directives,n=e.operationTypes;return l(["schema",l(t," "),d(n)]," ")},OperationTypeDefinition:function(e){return e.operation+": "+e.type},ScalarTypeDefinition:f((function(e){return l(["scalar",e.name,l(e.directives," ")]," ")})),ObjectTypeDefinition:f((function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return l(["type",t,h("implements ",l(n," & ")),l(r," "),d(i)]," ")})),FieldDefinition:f((function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(n.every((function(e){return-1===e.indexOf("\n")}))?h("(",l(n,", "),")"):h("(\n",p(l(n,"\n")),"\n)"))+": "+r+h(" ",l(i," "))})),InputValueDefinition:f((function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return l([t+": "+n,h("= ",r),l(i," ")]," ")})),InterfaceTypeDefinition:f((function(e){var t=e.name,n=e.directives,r=e.fields;return l(["interface",t,l(n," "),d(r)]," ")})),UnionTypeDefinition:f((function(e){var t=e.name,n=e.directives,r=e.types;return l(["union",t,l(n," "),r&&0!==r.length?"= "+l(r," | "):""]," ")})),EnumTypeDefinition:f((function(e){var t=e.name,n=e.directives,r=e.values;return l(["enum",t,l(n," "),d(r)]," ")})),EnumValueDefinition:f((function(e){return l([e.name,l(e.directives," ")]," ")})),InputObjectTypeDefinition:f((function(e){var t=e.name,n=e.directives,r=e.fields;return l(["input",t,l(n," "),d(r)]," ")})),DirectiveDefinition:f((function(e){var t=e.name,n=e.arguments,r=e.locations;return"directive @"+t+(n.every((function(e){return-1===e.indexOf("\n")}))?h("(",l(n,", "),")"):h("(\n",p(l(n,"\n")),"\n)"))+" on "+l(r," | ")})),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return l(["extend schema",l(t," "),d(n)]," ")},ScalarTypeExtension:function(e){return l(["extend scalar",e.name,l(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return l(["extend type",t,h("implements ",l(n," & ")),l(r," "),d(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return l(["extend interface",t,l(n," "),d(r)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return l(["extend union",t,l(n," "),r&&0!==r.length?"= "+l(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return l(["extend enum",t,l(n," "),d(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return l(["extend input",t,l(n," "),d(r)]," ")}};function f(e){return function(t){return l([t.description,e(t)],"\n")}}function l(e,t){return e?e.filter((function(e){return e})).join(t||""):""}function d(e){return e&&0!==e.length?"{\n"+p(l(e,"\n"))+"\n}":""}function h(e,t,n){return t?e+t+(n||""):""}function p(e){return e&&" "+e.replace(/\n/g,"\n ")}function v(e){return(v="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)}function g(e){return e&&"object"===v(e)?"function"==typeof e.inspect?e.inspect():Array.isArray(e)?"["+e.map(g).join(", ")+"]":"{"+Object.keys(e).map((function(t){return"".concat(t,": ").concat(g(e[t]))})).join(", ")+"}":"string"==typeof e?'"'+e+'"':"function"==typeof e?"[function ".concat(e.name,"]"):String(e)}function m(e,t){if(!e)throw new Error(t)}function b(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var y,w=function(e,t,n){b(this,"body",void 0),b(this,"name",void 0),b(this,"locationOffset",void 0),this.body=e,this.name=t||"GraphQL request",this.locationOffset=n||{line:1,column:1},this.locationOffset.line>0||m(0,"line in locationOffset is 1-indexed and must be positive"),this.locationOffset.column>0||m(0,"column in locationOffset is 1-indexed and must be positive")};function _(e,t,n){return new r.a("Syntax Error: ".concat(n),void 0,e,[t])}function S(e){for(var t=e.split(/\r\n|[\n\r]/g),n=null,r=1;r<t.length;r++){var i=t[r],o=E(i);if(o<i.length&&(null===n||o<n)&&0===(n=o))break}if(n)for(var s=1;s<t.length;s++)t[s]=t[s].slice(n);for(;t.length>0&&M(t[0]);)t.shift();for(;t.length>0&&M(t[t.length-1]);)t.pop();return t.join("\n")}function E(e){for(var t=0;t<e.length&&(" "===e[t]||"\t"===e[t]);)t++;return t}function M(e){return E(e)===e.length}function A(e,t){var n=new P(O.SOF,0,0,0,0,null);return{source:e,options:t,lastToken:n,token:n,line:1,lineStart:0,advance:I,lookahead:k}}function I(){return this.lastToken=this.token,this.token=this.lookahead()}function k(){var e=this.token;if(e.kind!==O.EOF)do{e=e.next||(e.next=R(this,e))}while(e.kind===O.COMMENT);return e}y=w,"function"==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(y.prototype,Symbol.toStringTag,{get:function(){return this.constructor.name}});var O=Object.freeze({SOF:"<SOF>",EOF:"<EOF>",BANG:"!",DOLLAR:"$",AMP:"&",PAREN_L:"(",PAREN_R:")",SPREAD:"...",COLON:":",EQUALS:"=",AT:"@",BRACKET_L:"[",BRACKET_R:"]",BRACE_L:"{",PIPE:"|",BRACE_R:"}",NAME:"Name",INT:"Int",FLOAT:"Float",STRING:"String",BLOCK_STRING:"BlockString",COMMENT:"Comment"});function x(e){var t=e.value;return t?"".concat(e.kind,' "').concat(t,'"'):e.kind}var C=String.prototype.charCodeAt,T=String.prototype.slice;function P(e,t,n,r,i,o,s){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=i,this.value=s,this.prev=o,this.next=null}function N(e){return isNaN(e)?O.EOF:e<127?JSON.stringify(String.fromCharCode(e)):'"\\u'.concat(("00"+e.toString(16).toUpperCase()).slice(-4),'"')}function R(e,t){var n=e.source,r=n.body,i=r.length,o=function(e,t,n){var r=e.length,i=t;for(;i<r;){var o=C.call(e,i);if(9===o||32===o||44===o||65279===o)++i;else if(10===o)++i,++n.line,n.lineStart=i;else{if(13!==o)break;10===C.call(e,i+1)?i+=2:++i,++n.line,n.lineStart=i}}return i}(r,t.end,e),s=e.line,a=1+o-e.lineStart;if(o>=i)return new P(O.EOF,i,i,s,a,t);var u=C.call(r,o);switch(u){case 33:return new P(O.BANG,o,o+1,s,a,t);case 35:return function(e,t,n,r,i){var o,s=e.body,a=t;do{o=C.call(s,++a)}while(null!==o&&(o>31||9===o));return new P(O.COMMENT,t,a,n,r,i,T.call(s,t+1,a))}(n,o,s,a,t);case 36:return new P(O.DOLLAR,o,o+1,s,a,t);case 38:return new P(O.AMP,o,o+1,s,a,t);case 40:return new P(O.PAREN_L,o,o+1,s,a,t);case 41:return new P(O.PAREN_R,o,o+1,s,a,t);case 46:if(46===C.call(r,o+1)&&46===C.call(r,o+2))return new P(O.SPREAD,o,o+3,s,a,t);break;case 58:return new P(O.COLON,o,o+1,s,a,t);case 61:return new P(O.EQUALS,o,o+1,s,a,t);case 64:return new P(O.AT,o,o+1,s,a,t);case 91:return new P(O.BRACKET_L,o,o+1,s,a,t);case 93:return new P(O.BRACKET_R,o,o+1,s,a,t);case 123:return new P(O.BRACE_L,o,o+1,s,a,t);case 124:return new P(O.PIPE,o,o+1,s,a,t);case 125:return new P(O.BRACE_R,o,o+1,s,a,t);case 65:case 66:case 67:case 68:case 69:case 70:case 71:case 72:case 73:case 74:case 75:case 76:case 77:case 78:case 79:case 80:case 81:case 82:case 83:case 84:case 85:case 86:case 87:case 88:case 89:case 90:case 95:case 97:case 98:case 99:case 100:case 101:case 102:case 103:case 104:case 105:case 106:case 107:case 108:case 109:case 110:case 111:case 112:case 113:case 114:case 115:case 116:case 117:case 118:case 119:case 120:case 121:case 122:return function(e,t,n,r,i){var o=e.body,s=o.length,a=t+1,u=0;for(;a!==s&&null!==(u=C.call(o,a))&&(95===u||u>=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122);)++a;return new P(O.NAME,t,a,n,r,i,T.call(o,t,a))}(n,o,s,a,t);case 45:case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return function(e,t,n,r,i,o){var s=e.body,a=n,u=t,c=!1;45===a&&(a=C.call(s,++u));if(48===a){if((a=C.call(s,++u))>=48&&a<=57)throw _(e,u,"Invalid number, unexpected digit after 0: ".concat(N(a),"."))}else u=L(e,u,a),a=C.call(s,u);46===a&&(c=!0,a=C.call(s,++u),u=L(e,u,a),a=C.call(s,u));69!==a&&101!==a||(c=!0,43!==(a=C.call(s,++u))&&45!==a||(a=C.call(s,++u)),u=L(e,u,a));return new P(c?O.FLOAT:O.INT,t,u,r,i,o,T.call(s,t,u))}(n,o,u,s,a,t);case 34:return 34===C.call(r,o+1)&&34===C.call(r,o+2)?function(e,t,n,r,i){var o=e.body,s=t+3,a=s,u=0,c="";for(;s<o.length&&null!==(u=C.call(o,s));){if(34===u&&34===C.call(o,s+1)&&34===C.call(o,s+2))return c+=T.call(o,a,s),new P(O.BLOCK_STRING,t,s+3,n,r,i,S(c));if(u<32&&9!==u&&10!==u&&13!==u)throw _(e,s,"Invalid character within String: ".concat(N(u),"."));92===u&&34===C.call(o,s+1)&&34===C.call(o,s+2)&&34===C.call(o,s+3)?(c+=T.call(o,a,s)+'"""',a=s+=4):++s}throw _(e,s,"Unterminated string.")}(n,o,s,a,t):function(e,t,n,r,i){var o=e.body,s=t+1,a=s,u=0,c="";for(;s<o.length&&null!==(u=C.call(o,s))&&10!==u&&13!==u;){if(34===u)return c+=T.call(o,a,s),new P(O.STRING,t,s+1,n,r,i,c);if(u<32&&9!==u)throw _(e,s,"Invalid character within String: ".concat(N(u),"."));if(++s,92===u){switch(c+=T.call(o,a,s-1),u=C.call(o,s)){case 34:c+='"';break;case 47:c+="/";break;case 92:c+="\\";break;case 98:c+="\b";break;case 102:c+="\f";break;case 110:c+="\n";break;case 114:c+="\r";break;case 116:c+="\t";break;case 117:var f=(l=C.call(o,s+1),d=C.call(o,s+2),h=C.call(o,s+3),p=C.call(o,s+4),j(l)<<12|j(d)<<8|j(h)<<4|j(p));if(f<0)throw _(e,s,"Invalid character escape sequence: "+"\\u".concat(o.slice(s+1,s+5),"."));c+=String.fromCharCode(f),s+=4;break;default:throw _(e,s,"Invalid character escape sequence: \\".concat(String.fromCharCode(u),"."))}++s,a=s}}var l,d,h,p;throw _(e,s,"Unterminated string.")}(n,o,s,a,t)}throw _(n,o,function(e){if(e<32&&9!==e&&10!==e&&13!==e)return"Cannot contain the invalid character ".concat(N(e),".");if(39===e)return"Unexpected single quote character ('), did you mean to use a double quote (\")?";return"Cannot parse the unexpected character ".concat(N(e),".")}(u))}function L(e,t,n){var r=e.body,i=t,o=n;if(o>=48&&o<=57){do{o=C.call(r,++i)}while(o>=48&&o<=57);return i}throw _(e,i,"Invalid number, expected digit but got: ".concat(N(o),"."))}function j(e){return e>=48&&e<=57?e-48:e>=65&&e<=70?e-55:e>=97&&e<=102?e-87:-1}P.prototype.toJSON=P.prototype.inspect=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}};var D=Object.freeze({NAME:"Name",DOCUMENT:"Document",OPERATION_DEFINITION:"OperationDefinition",VARIABLE_DEFINITION:"VariableDefinition",SELECTION_SET:"SelectionSet",FIELD:"Field",ARGUMENT:"Argument",FRAGMENT_SPREAD:"FragmentSpread",INLINE_FRAGMENT:"InlineFragment",FRAGMENT_DEFINITION:"FragmentDefinition",VARIABLE:"Variable",INT:"IntValue",FLOAT:"FloatValue",STRING:"StringValue",BOOLEAN:"BooleanValue",NULL:"NullValue",ENUM:"EnumValue",LIST:"ListValue",OBJECT:"ObjectValue",OBJECT_FIELD:"ObjectField",DIRECTIVE:"Directive",NAMED_TYPE:"NamedType",LIST_TYPE:"ListType",NON_NULL_TYPE:"NonNullType",SCHEMA_DEFINITION:"SchemaDefinition",OPERATION_TYPE_DEFINITION:"OperationTypeDefinition",SCALAR_TYPE_DEFINITION:"ScalarTypeDefinition",OBJECT_TYPE_DEFINITION:"ObjectTypeDefinition",FIELD_DEFINITION:"FieldDefinition",INPUT_VALUE_DEFINITION:"InputValueDefinition",INTERFACE_TYPE_DEFINITION:"InterfaceTypeDefinition",UNION_TYPE_DEFINITION:"UnionTypeDefinition",ENUM_TYPE_DEFINITION:"EnumTypeDefinition",ENUM_VALUE_DEFINITION:"EnumValueDefinition",INPUT_OBJECT_TYPE_DEFINITION:"InputObjectTypeDefinition",DIRECTIVE_DEFINITION:"DirectiveDefinition",SCHEMA_EXTENSION:"SchemaExtension",SCALAR_TYPE_EXTENSION:"ScalarTypeExtension",OBJECT_TYPE_EXTENSION:"ObjectTypeExtension",INTERFACE_TYPE_EXTENSION:"InterfaceTypeExtension",UNION_TYPE_EXTENSION:"UnionTypeExtension",ENUM_TYPE_EXTENSION:"EnumTypeExtension",INPUT_OBJECT_TYPE_EXTENSION:"InputObjectTypeExtension"}),U=Object.freeze({QUERY:"QUERY",MUTATION:"MUTATION",SUBSCRIPTION:"SUBSCRIPTION",FIELD:"FIELD",FRAGMENT_DEFINITION:"FRAGMENT_DEFINITION",FRAGMENT_SPREAD:"FRAGMENT_SPREAD",INLINE_FRAGMENT:"INLINE_FRAGMENT",VARIABLE_DEFINITION:"VARIABLE_DEFINITION",SCHEMA:"SCHEMA",SCALAR:"SCALAR",OBJECT:"OBJECT",FIELD_DEFINITION:"FIELD_DEFINITION",ARGUMENT_DEFINITION:"ARGUMENT_DEFINITION",INTERFACE:"INTERFACE",UNION:"UNION",ENUM:"ENUM",ENUM_VALUE:"ENUM_VALUE",INPUT_OBJECT:"INPUT_OBJECT",INPUT_FIELD_DEFINITION:"INPUT_FIELD_DEFINITION"});function B(e,t){var n="string"==typeof e?new w(e):e;if(!(n instanceof w))throw new TypeError("Must provide Source. Received: ".concat(g(n)));return function(e){var t=e.token;return{kind:D.DOCUMENT,definitions:Te(e,O.SOF,z,O.EOF),loc:Ee(e,t)}}(A(n,t||{}))}function F(e){var t=ke(e,O.NAME);return{kind:D.NAME,value:t.value,loc:Ee(e,t)}}function z(e){if(Ae(e,O.NAME))switch(e.token.value){case"query":case"mutation":case"subscription":case"fragment":return q(e);case"schema":case"scalar":case"type":case"interface":case"union":case"enum":case"input":case"directive":return ce(e);case"extend":return function(e){var t=e.lookahead();if(t.kind===O.NAME)switch(t.value){case"schema":return function(e){var t=e.token;Oe(e,"extend"),Oe(e,"schema");var n=oe(e,!0),r=Ae(e,O.BRACE_L)?Te(e,O.BRACE_L,de,O.BRACE_R):[];if(0===n.length&&0===r.length)throw xe(e);return{kind:D.SCHEMA_EXTENSION,directives:n,operationTypes:r,loc:Ee(e,t)}}(e);case"scalar":return function(e){var t=e.token;Oe(e,"extend"),Oe(e,"scalar");var n=F(e),r=oe(e,!0);if(0===r.length)throw xe(e);return{kind:D.SCALAR_TYPE_EXTENSION,name:n,directives:r,loc:Ee(e,t)}}(e);case"type":return function(e){var t=e.token;Oe(e,"extend"),Oe(e,"type");var n=F(e),r=he(e),i=oe(e,!0),o=pe(e);if(0===r.length&&0===i.length&&0===o.length)throw xe(e);return{kind:D.OBJECT_TYPE_EXTENSION,name:n,interfaces:r,directives:i,fields:o,loc:Ee(e,t)}}(e);case"interface":return function(e){var t=e.token;Oe(e,"extend"),Oe(e,"interface");var n=F(e),r=oe(e,!0),i=pe(e);if(0===r.length&&0===i.length)throw xe(e);return{kind:D.INTERFACE_TYPE_EXTENSION,name:n,directives:r,fields:i,loc:Ee(e,t)}}(e);case"union":return function(e){var t=e.token;Oe(e,"extend"),Oe(e,"union");var n=F(e),r=oe(e,!0),i=be(e);if(0===r.length&&0===i.length)throw xe(e);return{kind:D.UNION_TYPE_EXTENSION,name:n,directives:r,types:i,loc:Ee(e,t)}}(e);case"enum":return function(e){var t=e.token;Oe(e,"extend"),Oe(e,"enum");var n=F(e),r=oe(e,!0),i=ye(e);if(0===r.length&&0===i.length)throw xe(e);return{kind:D.ENUM_TYPE_EXTENSION,name:n,directives:r,values:i,loc:Ee(e,t)}}(e);case"input":return function(e){var t=e.token;Oe(e,"extend"),Oe(e,"input");var n=F(e),r=oe(e,!0),i=_e(e);if(0===r.length&&0===i.length)throw xe(e);return{kind:D.INPUT_OBJECT_TYPE_EXTENSION,name:n,directives:r,fields:i,loc:Ee(e,t)}}(e)}throw xe(e,t)}(e)}else{if(Ae(e,O.BRACE_L))return q(e);if(fe(e))return ce(e)}throw xe(e)}function q(e){if(Ae(e,O.NAME))switch(e.token.value){case"query":case"mutation":case"subscription":return K(e);case"fragment":return function(e){var t=e.token;if(Oe(e,"fragment"),e.options.experimentalFragmentVariables)return{kind:D.FRAGMENT_DEFINITION,name:Q(e),variableDefinitions:V(e),typeCondition:(Oe(e,"on"),ue(e)),directives:oe(e,!1),selectionSet:$(e),loc:Ee(e,t)};return{kind:D.FRAGMENT_DEFINITION,name:Q(e),typeCondition:(Oe(e,"on"),ue(e)),directives:oe(e,!1),selectionSet:$(e),loc:Ee(e,t)}}(e)}else if(Ae(e,O.BRACE_L))return K(e);throw xe(e)}function K(e){var t=e.token;if(Ae(e,O.BRACE_L))return{kind:D.OPERATION_DEFINITION,operation:"query",name:void 0,variableDefinitions:[],directives:[],selectionSet:$(e),loc:Ee(e,t)};var n,r=H(e);return Ae(e,O.NAME)&&(n=F(e)),{kind:D.OPERATION_DEFINITION,operation:r,name:n,variableDefinitions:V(e),directives:oe(e,!1),selectionSet:$(e),loc:Ee(e,t)}}function H(e){var t=ke(e,O.NAME);switch(t.value){case"query":return"query";case"mutation":return"mutation";case"subscription":return"subscription"}throw xe(e,t)}function V(e){return Ae(e,O.PAREN_L)?Te(e,O.PAREN_L,G,O.PAREN_R):[]}function G(e){var t=e.token;return e.options.experimentalVariableDefinitionDirectives?{kind:D.VARIABLE_DEFINITION,variable:W(e),type:(ke(e,O.COLON),ae(e)),defaultValue:Ie(e,O.EQUALS)?ee(e,!0):void 0,directives:oe(e,!0),loc:Ee(e,t)}:{kind:D.VARIABLE_DEFINITION,variable:W(e),type:(ke(e,O.COLON),ae(e)),defaultValue:Ie(e,O.EQUALS)?ee(e,!0):void 0,loc:Ee(e,t)}}function W(e){var t=e.token;return ke(e,O.DOLLAR),{kind:D.VARIABLE,name:F(e),loc:Ee(e,t)}}function $(e){var t=e.token;return{kind:D.SELECTION_SET,selections:Te(e,O.BRACE_L,Y,O.BRACE_R),loc:Ee(e,t)}}function Y(e){return Ae(e,O.SPREAD)?function(e){var t,n=e.token;if(ke(e,O.SPREAD),Ae(e,O.NAME)&&"on"!==e.token.value)return{kind:D.FRAGMENT_SPREAD,name:Q(e),directives:oe(e,!1),loc:Ee(e,n)};"on"===e.token.value&&(e.advance(),t=ue(e));return{kind:D.INLINE_FRAGMENT,typeCondition:t,directives:oe(e,!1),selectionSet:$(e),loc:Ee(e,n)}}(e):function(e){var t,n,r=e.token,i=F(e);Ie(e,O.COLON)?(t=i,n=F(e)):n=i;return{kind:D.FIELD,alias:t,name:n,arguments:J(e,!1),directives:oe(e,!1),selectionSet:Ae(e,O.BRACE_L)?$(e):void 0,loc:Ee(e,r)}}(e)}function J(e,t){var n=t?X:Z;return Ae(e,O.PAREN_L)?Te(e,O.PAREN_L,n,O.PAREN_R):[]}function Z(e){var t=e.token;return{kind:D.ARGUMENT,name:F(e),value:(ke(e,O.COLON),ee(e,!1)),loc:Ee(e,t)}}function X(e){var t=e.token;return{kind:D.ARGUMENT,name:F(e),value:(ke(e,O.COLON),ne(e)),loc:Ee(e,t)}}function Q(e){if("on"===e.token.value)throw xe(e);return F(e)}function ee(e,t){var n=e.token;switch(n.kind){case O.BRACKET_L:return function(e,t){var n=e.token,r=t?ne:re;return{kind:D.LIST,values:Ce(e,O.BRACKET_L,r,O.BRACKET_R),loc:Ee(e,n)}}(e,t);case O.BRACE_L:return function(e,t){var n=e.token;ke(e,O.BRACE_L);var r=[];for(;!Ie(e,O.BRACE_R);)r.push(ie(e,t));return{kind:D.OBJECT,fields:r,loc:Ee(e,n)}}(e,t);case O.INT:return e.advance(),{kind:D.INT,value:n.value,loc:Ee(e,n)};case O.FLOAT:return e.advance(),{kind:D.FLOAT,value:n.value,loc:Ee(e,n)};case O.STRING:case O.BLOCK_STRING:return te(e);case O.NAME:return"true"===n.value||"false"===n.value?(e.advance(),{kind:D.BOOLEAN,value:"true"===n.value,loc:Ee(e,n)}):"null"===n.value?(e.advance(),{kind:D.NULL,loc:Ee(e,n)}):(e.advance(),{kind:D.ENUM,value:n.value,loc:Ee(e,n)});case O.DOLLAR:if(!t)return W(e)}throw xe(e)}function te(e){var t=e.token;return e.advance(),{kind:D.STRING,value:t.value,block:t.kind===O.BLOCK_STRING,loc:Ee(e,t)}}function ne(e){return ee(e,!0)}function re(e){return ee(e,!1)}function ie(e,t){var n=e.token;return{kind:D.OBJECT_FIELD,name:F(e),value:(ke(e,O.COLON),ee(e,t)),loc:Ee(e,n)}}function oe(e,t){for(var n=[];Ae(e,O.AT);)n.push(se(e,t));return n}function se(e,t){var n=e.token;return ke(e,O.AT),{kind:D.DIRECTIVE,name:F(e),arguments:J(e,t),loc:Ee(e,n)}}function ae(e){var t,n=e.token;return Ie(e,O.BRACKET_L)?(t=ae(e),ke(e,O.BRACKET_R),t={kind:D.LIST_TYPE,type:t,loc:Ee(e,n)}):t=ue(e),Ie(e,O.BANG)?{kind:D.NON_NULL_TYPE,type:t,loc:Ee(e,n)}:t}function ue(e){var t=e.token;return{kind:D.NAMED_TYPE,name:F(e),loc:Ee(e,t)}}function ce(e){var t=fe(e)?e.lookahead():e.token;if(t.kind===O.NAME)switch(t.value){case"schema":return function(e){var t=e.token;Oe(e,"schema");var n=oe(e,!0),r=Te(e,O.BRACE_L,de,O.BRACE_R);return{kind:D.SCHEMA_DEFINITION,directives:n,operationTypes:r,loc:Ee(e,t)}}(e);case"scalar":return function(e){var t=e.token,n=le(e);Oe(e,"scalar");var r=F(e),i=oe(e,!0);return{kind:D.SCALAR_TYPE_DEFINITION,description:n,name:r,directives:i,loc:Ee(e,t)}}(e);case"type":return function(e){var t=e.token,n=le(e);Oe(e,"type");var r=F(e),i=he(e),o=oe(e,!0),s=pe(e);return{kind:D.OBJECT_TYPE_DEFINITION,description:n,name:r,interfaces:i,directives:o,fields:s,loc:Ee(e,t)}}(e);case"interface":return function(e){var t=e.token,n=le(e);Oe(e,"interface");var r=F(e),i=oe(e,!0),o=pe(e);return{kind:D.INTERFACE_TYPE_DEFINITION,description:n,name:r,directives:i,fields:o,loc:Ee(e,t)}}(e);case"union":return function(e){var t=e.token,n=le(e);Oe(e,"union");var r=F(e),i=oe(e,!0),o=be(e);return{kind:D.UNION_TYPE_DEFINITION,description:n,name:r,directives:i,types:o,loc:Ee(e,t)}}(e);case"enum":return function(e){var t=e.token,n=le(e);Oe(e,"enum");var r=F(e),i=oe(e,!0),o=ye(e);return{kind:D.ENUM_TYPE_DEFINITION,description:n,name:r,directives:i,values:o,loc:Ee(e,t)}}(e);case"input":return function(e){var t=e.token,n=le(e);Oe(e,"input");var r=F(e),i=oe(e,!0),o=_e(e);return{kind:D.INPUT_OBJECT_TYPE_DEFINITION,description:n,name:r,directives:i,fields:o,loc:Ee(e,t)}}(e);case"directive":return function(e){var t=e.token,n=le(e);Oe(e,"directive"),ke(e,O.AT);var r=F(e),i=ge(e);Oe(e,"on");var o=function(e){Ie(e,O.PIPE);var t=[];do{t.push(Se(e))}while(Ie(e,O.PIPE));return t}(e);return{kind:D.DIRECTIVE_DEFINITION,description:n,name:r,arguments:i,locations:o,loc:Ee(e,t)}}(e)}throw xe(e,t)}function fe(e){return Ae(e,O.STRING)||Ae(e,O.BLOCK_STRING)}function le(e){if(fe(e))return te(e)}function de(e){var t=e.token,n=H(e);ke(e,O.COLON);var r=ue(e);return{kind:D.OPERATION_TYPE_DEFINITION,operation:n,type:r,loc:Ee(e,t)}}function he(e){var t=[];if("implements"===e.token.value){e.advance(),Ie(e,O.AMP);do{t.push(ue(e))}while(Ie(e,O.AMP)||e.options.allowLegacySDLImplementsInterfaces&&Ae(e,O.NAME))}return t}function pe(e){return e.options.allowLegacySDLEmptyFields&&Ae(e,O.BRACE_L)&&e.lookahead().kind===O.BRACE_R?(e.advance(),e.advance(),[]):Ae(e,O.BRACE_L)?Te(e,O.BRACE_L,ve,O.BRACE_R):[]}function ve(e){var t=e.token,n=le(e),r=F(e),i=ge(e);ke(e,O.COLON);var o=ae(e),s=oe(e,!0);return{kind:D.FIELD_DEFINITION,description:n,name:r,arguments:i,type:o,directives:s,loc:Ee(e,t)}}function ge(e){return Ae(e,O.PAREN_L)?Te(e,O.PAREN_L,me,O.PAREN_R):[]}function me(e){var t=e.token,n=le(e),r=F(e);ke(e,O.COLON);var i,o=ae(e);Ie(e,O.EQUALS)&&(i=ne(e));var s=oe(e,!0);return{kind:D.INPUT_VALUE_DEFINITION,description:n,name:r,type:o,defaultValue:i,directives:s,loc:Ee(e,t)}}function be(e){var t=[];if(Ie(e,O.EQUALS)){Ie(e,O.PIPE);do{t.push(ue(e))}while(Ie(e,O.PIPE))}return t}function ye(e){return Ae(e,O.BRACE_L)?Te(e,O.BRACE_L,we,O.BRACE_R):[]}function we(e){var t=e.token,n=le(e),r=F(e),i=oe(e,!0);return{kind:D.ENUM_VALUE_DEFINITION,description:n,name:r,directives:i,loc:Ee(e,t)}}function _e(e){return Ae(e,O.BRACE_L)?Te(e,O.BRACE_L,me,O.BRACE_R):[]}function Se(e){var t=e.token,n=F(e);if(U.hasOwnProperty(n.value))return n;throw xe(e,t)}function Ee(e,t){if(!e.options.noLocation)return new Me(t,e.lastToken,e.source)}function Me(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}function Ae(e,t){return e.token.kind===t}function Ie(e,t){var n=e.token.kind===t;return n&&e.advance(),n}function ke(e,t){var n=e.token;if(n.kind===t)return e.advance(),n;throw _(e.source,n.start,"Expected ".concat(t,", found ").concat(x(n)))}function Oe(e,t){var n=e.token;if(n.kind===O.NAME&&n.value===t)return e.advance(),n;throw _(e.source,n.start,'Expected "'.concat(t,'", found ').concat(x(n)))}function xe(e,t){var n=t||e.token;return _(e.source,n.start,"Unexpected ".concat(x(n)))}function Ce(e,t,n,r){ke(e,t);for(var i=[];!Ie(e,r);)i.push(n(e));return i}function Te(e,t,n,r){ke(e,t);for(var i=[n(e)];!Ie(e,r);)i.push(n(e));return i}Me.prototype.toJSON=Me.prototype.inspect=function(){return{start:this.start,end:this.end}};var Pe=n(44),Ne=n(89),Re=n(5),Le=n(103),je=n(19),De=n(34),Ue=n(42),Be=n(26),Fe=n(254),ze=function(){return(ze=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},qe=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},Ke=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},He=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},Ve=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},Ge=new Pe.a("GraphQLAPI"),We=function(e,t){return void 0===t&&(t={}),{query:e,variables:t}},$e=function(){function e(e){this._api=null,this.Auth=Ue.a,this.Cache=Be.a,this.Credentials=Ne.a,this._options=e,Ge.debug("API Options",this._options)}return e.prototype.getModuleName=function(){return"GraphQLAPI"},e.prototype.configure=function(e){var t=e||{},n=t.API,r=void 0===n?{}:n,i=He(t,["API"]),o=ze(ze({},i),r);return Ge.debug("configure GraphQL API",{opt:o}),o.aws_project_region&&(o=Object.assign({},o,{region:o.aws_project_region,header:{}})),void 0!==o.graphql_headers&&"function"!=typeof o.graphql_headers&&(Ge.warn("graphql_headers should be a function"),o.graphql_headers=void 0),this._options=Object.assign({},this._options,o),this.createInstance(),this._options},e.prototype.createInstance=function(){return Ge.debug("create Rest instance"),this._options?(this._api=new Fe.a(this._options),this._api.Credentials=this.Credentials,!0):Promise.reject("API not configured")},e.prototype._headerBasedAuth=function(e){return qe(this,void 0,void 0,(function(){var t,n,r,i,o,s,a,u;return Ke(this,(function(c){switch(c.label){case 0:switch(t=this._options,n=t.aws_appsync_authenticationType,r=t.aws_appsync_apiKey,i={},e||n||"AWS_IAM"){case"API_KEY":return[3,1];case"AWS_IAM":return[3,2];case"OPENID_CONNECT":return[3,4];case"AMAZON_COGNITO_USER_POOLS":return[3,9]}return[3,11];case 1:if(!r)throw new Error("No api-key configured");return i={Authorization:null,"X-Api-Key":r},[3,12];case 2:return[4,this._ensureCredentials()];case 3:if(!c.sent())throw new Error("No credentials");return[3,12];case 4:return o=void 0,[4,Be.a.getItem("federatedInfo")];case 5:return(s=c.sent())?(o=s.token,[3,8]):[3,6];case 6:return[4,Ue.a.currentAuthenticatedUser()];case 7:(a=c.sent())&&(o=a.token),c.label=8;case 8:if(!o)throw new Error("No federated jwt");return i={Authorization:o},[3,12];case 9:return[4,this.Auth.currentSession()];case 10:return u=c.sent(),i={Authorization:u.getAccessToken().getJwtToken()},[3,12];case 11:return i={Authorization:null},[3,12];case 12:return[2,i]}}))}))},e.prototype.getGraphqlOperationType=function(e){var t=B(e);return Ve(t.definitions,1)[0].operation},e.prototype.graphql=function(e,t){var n=e.query,r=e.variables,i=void 0===r?{}:r,o=e.authMode,s=B("string"==typeof n?n:u(n)),a=Ve(s.definitions.filter((function(e){return"OperationDefinition"===e.kind})),1)[0],c=(void 0===a?{}:a).operation;switch(c){case"query":case"mutation":var f=this._api.getCancellableToken(),l={cancellableToken:f},d=this._graphql({query:s,variables:i,authMode:o},t,l);return this._api.updateRequestToBeCancellable(d,f),d;case"subscription":return this._graphqlSubscribe({query:s,variables:i,authMode:o},t)}throw new Error("invalid operation type: "+c)},e.prototype._graphql=function(e,t,n){var i=e.query,o=e.variables,s=e.authMode;return void 0===t&&(t={}),void 0===n&&(n={}),qe(this,void 0,void 0,(function(){var e,a,c,f,l,d,h,p,v,g,m,b,y,w,_,S,E,M,A,I,k;return Ke(this,(function(O){switch(O.label){case 0:return this._api?[3,2]:[4,this.createInstance()];case 1:O.sent(),O.label=2;case 2:return e=this._options,a=e.aws_appsync_region,c=e.aws_appsync_graphqlEndpoint,f=e.graphql_headers,l=void 0===f?function(){return{}}:f,d=e.graphql_endpoint,h=e.graphql_endpoint_iam_region,v=[{}],(g=!d)?[4,this._headerBasedAuth(s)]:[3,4];case 3:g=O.sent(),O.label=4;case 4:return m=[ze.apply(void 0,v.concat([g]))],(b=d)?h?[4,this._headerBasedAuth(s)]:[3,6]:[3,8];case 5:return y=O.sent(),[3,7];case 6:y={Authorization:null},O.label=7;case 7:b=y,O.label=8;case 8:return w=[ze.apply(void 0,m.concat([b]))],[4,l({query:i,variables:o})];case 9:if(p=ze.apply(void 0,[ze.apply(void 0,[ze.apply(void 0,w.concat([O.sent()])),t]),!d&&(k={},k["x-amz-user-agent"]=Re.a.userAgent,k)]),_={query:u(i),variables:o},S=Object.assign({headers:p,body:_,signerServiceInfo:{service:d?"execute-api":"appsync",region:d?h:a}},n),!(E=d||c))throw{data:{},errors:[new r.a("No graphql endpoint provided.")]};O.label=10;case 10:return O.trys.push([10,12,,13]),[4,this._api.post(E,S)];case 11:return M=O.sent(),[3,13];case 12:if(A=O.sent(),this._api.isCancel(A))throw A;return M={data:{},errors:[new r.a(A.message)]},[3,13];case 13:if((I=M.errors)&&I.length)throw M;return[2,M]}}))}))},e.prototype.isCancel=function(e){return this._api.isCancel(e)},e.prototype.cancel=function(e,t){return this._api.cancel(e,t)},e.prototype._graphqlSubscribe=function(e,t){var n=e.query,r=e.variables,i=e.authMode;void 0===t&&(t={});var o=this._options,s=o.aws_appsync_region,a=o.aws_appsync_graphqlEndpoint,c=o.aws_appsync_authenticationType,f=o.aws_appsync_apiKey,l=o.graphql_headers,d=void 0===l?function(){return{}}:l,h=i||c||"AWS_IAM";if(De.b&&"function"==typeof De.b.subscribe)return De.b.subscribe("",{provider:Le.b,appSyncGraphqlEndpoint:a,authenticationType:h,apiKey:f,query:u(n),region:s,variables:r,graphql_headers:d,additionalHeaders:t});throw Ge.debug("No pubsub module applied for subscription"),new Error("No pubsub module applied for subscription")},e.prototype._ensureCredentials=function(){var e=this;return this.Credentials.get().then((function(t){if(!t)return!1;var n=e.Credentials.shear(t);return Ge.debug("set credentials for api",n),!0})).catch((function(e){return Ge.warn("ensure credentials error",e),!1}))},e}(),Ye=new $e(null);je.a.register(Ye)},function(e,t,n){"use strict";n.r(t),n.d(t,"fromUtf8",(function(){return r})),n.d(t,"toUtf8",(function(){return i}));var r=function(e){return"function"==typeof TextEncoder?function(e){return(new TextEncoder).encode(e)}(e):function(e){for(var t=[],n=0,r=e.length;n<r;n++){var i=e.charCodeAt(n);if(i<128)t.push(i);else if(i<2048)t.push(i>>6|192,63&i|128);else if(n+1<e.length&&55296==(64512&i)&&56320==(64512&e.charCodeAt(n+1))){var o=65536+((1023&i)<<10)+(1023&e.charCodeAt(++n));t.push(o>>18|240,o>>12&63|128,o>>6&63|128,63&o|128)}else t.push(i>>12|224,i>>6&63|128,63&i|128)}return Uint8Array.from(t)}(e)},i=function(e){return"function"==typeof TextDecoder?function(e){return new TextDecoder("utf-8").decode(e)}(e):function(e){for(var t="",n=0,r=e.length;n<r;n++){var i=e[n];if(i<128)t+=String.fromCharCode(i);else if(192<=i&&i<224){var o=e[++n];t+=String.fromCharCode((31&i)<<6|63&o)}else if(240<=i&&i<365){var s="%"+[i,e[++n],e[++n],e[++n]].map((function(e){return e.toString(16)})).join("%");t+=decodeURIComponent(s)}else t+=String.fromCharCode((15&i)<<12|(63&e[++n])<<6|63&e[++n])}return t}(e)}},function(e,t,n){"use strict";(function(e){var r;if(n.d(t,"a",(function(){return i})),"undefined"!=typeof window&&window.crypto&&(r=window.crypto),!r&&"undefined"!=typeof window&&window.msCrypto&&(r=window.msCrypto),!r&&void 0!==e&&e.crypto&&(r=e.crypto),!r)try{r=n(272)}catch(e){}function i(){if(r){if("function"==typeof r.getRandomValues)try{return r.getRandomValues(new Uint32Array(1))[0]}catch(e){}if("function"==typeof r.randomBytes)try{return r.randomBytes(4).readInt32LE()}catch(e){}}throw new Error("Native crypto module could not be used to get secure random number.")}}).call(this,n(31))},function(e,t,n){"use strict";n.d(t,"a",(function(){return r}));var r=function(e){return"function"==typeof ArrayBuffer&&e instanceof ArrayBuffer||"[object ArrayBuffer]"===Object.prototype.toString.call(e)}},function(e,t,n){var r=n(387);e.exports=function(e,t,n){var i=null==e?void 0:r(e,t);return void 0===i?n:i}},function(e,t,n){"use strict";const r=n(459),i=n(102),o=n(102),s=n(54).buildOptions,a=n(461);t.parse=function(e,t,n){if(n){!0===n&&(n={});const t=a.validate(e,n);if(!0!==t)throw Error(t.err.msg)}t=s(t,o.defaultOptions,o.props);const u=i.getTraversalObj(e,t);return r.convertToJson(u,t)},t.convertTonimn=n(462).convert2nimn,t.getTraversalObj=i.getTraversalObj,t.convertToJson=r.convertToJson,t.convertToJsonString=n(463).convertToJsonString,t.validate=a.validate,t.j2xParser=n(464),t.parseToNimn=function(e,n,r){return t.convertTonimn(t.getTraversalObj(e,r),n,r)}},function(e,t,n){"use strict";n.d(t,"a",(function(){return g}));var r=n(44),i=n(89),o=n(50),s=n(77),a=n(104),u=n(64),c=n.n(u),f=n(16),l=function(){return(l=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},d=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},h=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},p=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},v=new r.a("RestClient"),g=function(){function e(e){this._region="us-east-1",this._service="execute-api",this._custom_header=void 0,this._cancelTokenMap=null,this.Credentials=i.a,this._options=e,v.debug("API Options",this._options),null==this._cancelTokenMap&&(this._cancelTokenMap=new WeakMap)}return e.prototype.ajax=function(e,t,n){return d(this,void 0,void 0,(function(){var r,i,a,u,c,d,g,m,b,y,w,_,S,E,M=this;return h(this,(function(h){switch(h.label){case 0:return v.debug(t,e),a="us-east-1",u="execute-api",c=void 0,"string"==typeof e?(r=this._parseUrl(e),i=e):(i=e.endpoint,c=e.custom_header,a=e.region,u=e.service,r=this._parseUrl(e.endpoint)),d={method:t,url:i,host:r.host,path:r.path,headers:{},data:null,responseType:"json",timeout:0,cancelToken:null},g={},o.a.isReactNative&&(m=o.a.userAgent||"aws-amplify/0.1.x",g={"User-Agent":m}),b=Object.assign({},n),y=b.response,b.body&&("function"==typeof FormData&&b.body instanceof FormData?(g["Content-Type"]="multipart/form-data",d.data=b.body):(g["Content-Type"]="application/json; charset=UTF-8",d.data=JSON.stringify(b.body))),b.responseType&&(d.responseType=b.responseType),b.withCredentials&&(d.withCredentials=b.withCredentials),b.timeout&&(d.timeout=b.timeout),b.cancellableToken&&(d.cancelToken=b.cancellableToken.token),d.signerServiceInfo=b.signerServiceInfo,"function"!=typeof c?[3,2]:[4,c()];case 1:return _=h.sent(),[3,3];case 2:_=void 0,h.label=3;case 3:return w=_,d.headers=l(l(l({},g),w),b.headers),S=Object(f.parse)(i,!0,!0),S.search,E=p(S,["search"]),d.url=Object(f.format)(l(l({},E),{query:l(l({},E.query),b.queryStringParameters||{})})),void 0!==d.headers.Authorization?(d.headers=Object.keys(d.headers).reduce((function(e,t){return d.headers[t]&&(e[t]=d.headers[t]),e}),{}),[2,this._request(d,y)]):[2,this.Credentials.get().then((function(r){return M._signed(l({},d),r,y,{region:a,service:u}).catch((function(r){if(s.a.isClockSkewError(r)){var i=r.response.headers,o=i&&(i.date||i.Date),a=new Date(o),u=s.a.getDateFromHeaderString(d.headers["x-amz-date"]);if(s.a.isClockSkewed(u,a))return s.a.setClockOffset(a.getTime()-u.getTime()),M.ajax(e,t,n)}throw r}))}),(function(e){return v.debug("No credentials available, the request will be unsigned"),M._request(d,y)}))]}}))}))},e.prototype.get=function(e,t){return this.ajax(e,"GET",t)},e.prototype.put=function(e,t){return this.ajax(e,"PUT",t)},e.prototype.patch=function(e,t){return this.ajax(e,"PATCH",t)},e.prototype.post=function(e,t){return this.ajax(e,"POST",t)},e.prototype.del=function(e,t){return this.ajax(e,"DELETE",t)},e.prototype.head=function(e,t){return this.ajax(e,"HEAD",t)},e.prototype.cancel=function(e,t){var n=this._cancelTokenMap.get(e);return n&&n.cancel(t),!0},e.prototype.isCancel=function(e){return c.a.isCancel(e)},e.prototype.getCancellableToken=function(){return c.a.CancelToken.source()},e.prototype.updateRequestToBeCancellable=function(e,t){this._cancelTokenMap.set(e,t)},e.prototype.endpoint=function(e){var t=this,n=this._options.endpoints,r="";return Array.isArray(n)?(n.forEach((function(n){n.name===e&&(r=n.endpoint,"string"==typeof n.region?t._region=n.region:"string"==typeof t._options.region&&(t._region=t._options.region),"string"==typeof n.service?t._service=n.service||"execute-api":t._service="execute-api","function"==typeof n.custom_header?t._custom_header=n.custom_header:t._custom_header=void 0)})),r):r},e.prototype._signed=function(e,t,n,r){var i=r.service,o=r.region,s=e.signerServiceInfo,u=p(e,["signerServiceInfo"]),f=o||this._region||this._options.region,l=i||this._service||this._options.service,d={secret_key:t.secretAccessKey,access_key:t.accessKeyId,session_token:t.sessionToken},h={region:f,service:l},g=Object.assign(h,s),m=a.a.sign(u,d,g);return m.data&&(m.body=m.data),v.debug("Signed Request: ",m),delete m.headers.host,c()(m).then((function(e){return n?e:e.data})).catch((function(e){throw v.debug(e),e}))},e.prototype._request=function(e,t){return void 0===t&&(t=!1),c()(e).then((function(e){return t?e:e.data})).catch((function(e){throw v.debug(e),e}))},e.prototype._parseUrl=function(e){var t=e.split("/");return{host:t[2],path:"/"+t.slice(3).join("/")}},e}()},function(e,t,n){e.exports=n(482).Observable},function(e,t,n){(function(t){var n;n=function(){return function(e){var t,n=e.localStorage||(t={},{setItem:function(e,n){t[e]=n},getItem:function(e){return t[e]},removeItem:function(e){delete t[e]}}),r=1,i=2,o=3,s=4,a=5,u=6,c=7,f=8,l=9,d=10,h=11,p=12,v=13,g=14,m=function(e,t){for(var n in e)if(e.hasOwnProperty(n)){if(!t.hasOwnProperty(n)){var r="Unknown property, "+n+". Valid properties are:";for(var i in t)t.hasOwnProperty(i)&&(r=r+" "+i);throw new Error(r)}if(typeof e[n]!==t[n])throw new Error(_(y.INVALID_TYPE,[typeof e[n],n]))}},b=function(e,t){return function(){return e.apply(t,arguments)}},y={OK:{code:0,text:"AMQJSC0000I OK."},CONNECT_TIMEOUT:{code:1,text:"AMQJSC0001E Connect timed out."},SUBSCRIBE_TIMEOUT:{code:2,text:"AMQJS0002E Subscribe timed out."},UNSUBSCRIBE_TIMEOUT:{code:3,text:"AMQJS0003E Unsubscribe timed out."},PING_TIMEOUT:{code:4,text:"AMQJS0004E Ping timed out."},INTERNAL_ERROR:{code:5,text:"AMQJS0005E Internal error. Error Message: {0}, Stack trace: {1}"},CONNACK_RETURNCODE:{code:6,text:"AMQJS0006E Bad Connack return code:{0} {1}."},SOCKET_ERROR:{code:7,text:"AMQJS0007E Socket error:{0}."},SOCKET_CLOSE:{code:8,text:"AMQJS0008I Socket closed."},MALFORMED_UTF:{code:9,text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."},UNSUPPORTED:{code:10,text:"AMQJS0010E {0} is not supported by this browser."},INVALID_STATE:{code:11,text:"AMQJS0011E Invalid state {0}."},INVALID_TYPE:{code:12,text:"AMQJS0012E Invalid type {0} for {1}."},INVALID_ARGUMENT:{code:13,text:"AMQJS0013E Invalid argument {0} for {1}."},UNSUPPORTED_OPERATION:{code:14,text:"AMQJS0014E Unsupported operation."},INVALID_STORED_DATA:{code:15,text:"AMQJS0015E Invalid data in local storage key={0} value={1}."},INVALID_MQTT_MESSAGE_TYPE:{code:16,text:"AMQJS0016E Invalid MQTT message type {0}."},MALFORMED_UNICODE:{code:17,text:"AMQJS0017E Malformed Unicode string:{0} {1}."},BUFFER_FULL:{code:18,text:"AMQJS0018E Message buffer is full, maximum buffer size: {0}."}},w={0:"Connection Accepted",1:"Connection Refused: unacceptable protocol version",2:"Connection Refused: identifier rejected",3:"Connection Refused: server unavailable",4:"Connection Refused: bad user name or password",5:"Connection Refused: not authorized"},_=function(e,t){var n=e.text;if(t)for(var r,i,o=0;o<t.length;o++)if(r="{"+o+"}",(i=n.indexOf(r))>0){var s=n.substring(0,i),a=n.substring(i+r.length);n=s+t[o]+a}return n},S=[0,6,77,81,73,115,100,112,3],E=[0,4,77,81,84,84,4],M=function(e,t){for(var n in this.type=e,t)t.hasOwnProperty(n)&&(this[n]=t[n])};function A(e,t){var n,r=t,f=e[t],d=f>>4,p=f&=15;t+=1;var v=0,g=1;do{if(t==e.length)return[null,r];v+=(127&(n=e[t++]))*g,g*=128}while(0!=(128&n));var m=t+v;if(m>e.length)return[null,r];var b=new M(d);switch(d){case i:1&e[t++]&&(b.sessionPresent=!0),b.returnCode=e[t++];break;case o:var y=p>>1&3,w=O(e,t),_=T(e,t+=2,w);t+=w,y>0&&(b.messageIdentifier=O(e,t),t+=2);var S=new L(e.subarray(t,m));1==(1&p)&&(S.retained=!0),8==(8&p)&&(S.duplicate=!0),S.qos=y,S.destinationName=_,b.payloadMessage=S;break;case s:case a:case u:case c:case h:b.messageIdentifier=O(e,t);break;case l:b.messageIdentifier=O(e,t),t+=2,b.returnCode=e.subarray(t,m)}return[b,m]}function I(e,t,n){return t[n++]=e>>8,t[n++]=e%256,n}function k(e,t,n,r){return C(e,n,r=I(t,n,r)),r+t}function O(e,t){return 256*e[t]+e[t+1]}function x(e){for(var t=0,n=0;n<e.length;n++){var r=e.charCodeAt(n);r>2047?(55296<=r&&r<=56319&&(n++,t++),t+=3):r>127?t+=2:t++}return t}function C(e,t,n){for(var r=n,i=0;i<e.length;i++){var o=e.charCodeAt(i);if(55296<=o&&o<=56319){var s=e.charCodeAt(++i);if(isNaN(s))throw new Error(_(y.MALFORMED_UNICODE,[o,s]));o=s-56320+(o-55296<<10)+65536}o<=127?t[r++]=o:o<=2047?(t[r++]=o>>6&31|192,t[r++]=63&o|128):o<=65535?(t[r++]=o>>12&15|224,t[r++]=o>>6&63|128,t[r++]=63&o|128):(t[r++]=o>>18&7|240,t[r++]=o>>12&63|128,t[r++]=o>>6&63|128,t[r++]=63&o|128)}return t}function T(e,t,n){for(var r,i="",o=t;o<t+n;){var s=e[o++];if(s<128)r=s;else{var a=e[o++]-128;if(a<0)throw new Error(_(y.MALFORMED_UTF,[s.toString(16),a.toString(16),""]));if(s<224)r=64*(s-192)+a;else{var u=e[o++]-128;if(u<0)throw new Error(_(y.MALFORMED_UTF,[s.toString(16),a.toString(16),u.toString(16)]));if(s<240)r=4096*(s-224)+64*a+u;else{var c=e[o++]-128;if(c<0)throw new Error(_(y.MALFORMED_UTF,[s.toString(16),a.toString(16),u.toString(16),c.toString(16)]));if(!(s<248))throw new Error(_(y.MALFORMED_UTF,[s.toString(16),a.toString(16),u.toString(16),c.toString(16)]));r=262144*(s-240)+4096*a+64*u+c}}}r>65535&&(r-=65536,i+=String.fromCharCode(55296+(r>>10)),r=56320+(1023&r)),i+=String.fromCharCode(r)}return i}M.prototype.encode=function(){var e,t=(15&this.type)<<4,n=0,i=[],s=0;switch(void 0!==this.messageIdentifier&&(n+=2),this.type){case r:switch(this.mqttVersion){case 3:n+=S.length+3;break;case 4:n+=E.length+3}n+=x(this.clientId)+2,void 0!==this.willMessage&&(n+=x(this.willMessage.destinationName)+2,(e=this.willMessage.payloadBytes)instanceof Uint8Array||(e=new Uint8Array(c)),n+=e.byteLength+2),void 0!==this.userName&&(n+=x(this.userName)+2),void 0!==this.password&&(n+=x(this.password)+2);break;case f:t|=2;for(var a=0;a<this.topics.length;a++)i[a]=x(this.topics[a]),n+=i[a]+2;n+=this.requestedQos.length;break;case d:for(t|=2,a=0;a<this.topics.length;a++)i[a]=x(this.topics[a]),n+=i[a]+2;break;case u:t|=2;break;case o:this.payloadMessage.duplicate&&(t|=8),t=t|=this.payloadMessage.qos<<1,this.payloadMessage.retained&&(t|=1),n+=(s=x(this.payloadMessage.destinationName))+2;var c=this.payloadMessage.payloadBytes;n+=c.byteLength,c instanceof ArrayBuffer?c=new Uint8Array(c):c instanceof Uint8Array||(c=new Uint8Array(c.buffer))}var l=function(e){var t=new Array(1),n=0;do{var r=e%128;(e>>=7)>0&&(r|=128),t[n++]=r}while(e>0&&n<4);return t}(n),h=l.length+1,p=new ArrayBuffer(n+h),v=new Uint8Array(p);if(v[0]=t,v.set(l,1),this.type==o)h=k(this.payloadMessage.destinationName,s,v,h);else if(this.type==r){switch(this.mqttVersion){case 3:v.set(S,h),h+=S.length;break;case 4:v.set(E,h),h+=E.length}var g=0;this.cleanSession&&(g=2),void 0!==this.willMessage&&(g|=4,g|=this.willMessage.qos<<3,this.willMessage.retained&&(g|=32)),void 0!==this.userName&&(g|=128),void 0!==this.password&&(g|=64),v[h++]=g,h=I(this.keepAliveInterval,v,h)}switch(void 0!==this.messageIdentifier&&(h=I(this.messageIdentifier,v,h)),this.type){case r:h=k(this.clientId,x(this.clientId),v,h),void 0!==this.willMessage&&(h=k(this.willMessage.destinationName,x(this.willMessage.destinationName),v,h),h=I(e.byteLength,v,h),v.set(e,h),h+=e.byteLength),void 0!==this.userName&&(h=k(this.userName,x(this.userName),v,h)),void 0!==this.password&&(h=k(this.password,x(this.password),v,h));break;case o:v.set(c,h);break;case f:for(a=0;a<this.topics.length;a++)h=k(this.topics[a],i[a],v,h),v[h++]=this.requestedQos[a];break;case d:for(a=0;a<this.topics.length;a++)h=k(this.topics[a],i[a],v,h)}return p};var P=function(e,t){this._client=e,this._keepAliveInterval=1e3*t,this.isReset=!1;var n=new M(p).encode(),r=function(e){return function(){return i.apply(e)}},i=function(){this.isReset?(this.isReset=!1,this._client._trace("Pinger.doPing","send PINGREQ"),this._client.socket.send(n),this.timeout=setTimeout(r(this),this._keepAliveInterval)):(this._client._trace("Pinger.doPing","Timed out"),this._client._disconnected(y.PING_TIMEOUT.code,_(y.PING_TIMEOUT)))};this.reset=function(){this.isReset=!0,clearTimeout(this.timeout),this._keepAliveInterval>0&&(this.timeout=setTimeout(r(this),this._keepAliveInterval))},this.cancel=function(){clearTimeout(this.timeout)}},N=function(e,t,n,r){t||(t=30),this.timeout=setTimeout(function(e,t,n){return function(){return e.apply(t,n)}}(n,e,r),1e3*t),this.cancel=function(){clearTimeout(this.timeout)}},R=function(t,r,i,o,s){if(!("WebSocket"in e)||null===e.WebSocket)throw new Error(_(y.UNSUPPORTED,["WebSocket"]));if(!("ArrayBuffer"in e)||null===e.ArrayBuffer)throw new Error(_(y.UNSUPPORTED,["ArrayBuffer"]));for(var a in this._trace("Paho.Client",t,r,i,o,s),this.host=r,this.port=i,this.path=o,this.uri=t,this.clientId=s,this._wsuri=null,this._localKey=r+":"+i+("/mqtt"!=o?":"+o:"")+":"+s+":",this._msg_queue=[],this._buffered_msg_queue=[],this._sentMessages={},this._receivedMessages={},this._notify_msg_sent={},this._message_identifier=1,this._sequence=0,n)0!==a.indexOf("Sent:"+this._localKey)&&0!==a.indexOf("Received:"+this._localKey)||this.restore(a)};R.prototype.host=null,R.prototype.port=null,R.prototype.path=null,R.prototype.uri=null,R.prototype.clientId=null,R.prototype.socket=null,R.prototype.connected=!1,R.prototype.maxMessageIdentifier=65536,R.prototype.connectOptions=null,R.prototype.hostIndex=null,R.prototype.onConnected=null,R.prototype.onConnectionLost=null,R.prototype.onMessageDelivered=null,R.prototype.onMessageArrived=null,R.prototype.traceFunction=null,R.prototype._msg_queue=null,R.prototype._buffered_msg_queue=null,R.prototype._connectTimeout=null,R.prototype.sendPinger=null,R.prototype.receivePinger=null,R.prototype._reconnectInterval=1,R.prototype._reconnecting=!1,R.prototype._reconnectTimeout=null,R.prototype.disconnectedPublishing=!1,R.prototype.disconnectedBufferSize=5e3,R.prototype.receiveBuffer=null,R.prototype._traceBuffer=null,R.prototype._MAX_TRACE_ENTRIES=100,R.prototype.connect=function(e){var t=this._traceMask(e,"password");if(this._trace("Client.connect",t,this.socket,this.connected),this.connected)throw new Error(_(y.INVALID_STATE,["already connected"]));if(this.socket)throw new Error(_(y.INVALID_STATE,["already connected"]));this._reconnecting&&(this._reconnectTimeout.cancel(),this._reconnectTimeout=null,this._reconnecting=!1),this.connectOptions=e,this._reconnectInterval=1,this._reconnecting=!1,e.uris?(this.hostIndex=0,this._doConnect(e.uris[0])):this._doConnect(this.uri)},R.prototype.subscribe=function(e,t){if(this._trace("Client.subscribe",e,t),!this.connected)throw new Error(_(y.INVALID_STATE,["not connected"]));var n=new M(f);n.topics=e.constructor===Array?e:[e],void 0===t.qos&&(t.qos=0),n.requestedQos=[];for(var r=0;r<n.topics.length;r++)n.requestedQos[r]=t.qos;t.onSuccess&&(n.onSuccess=function(e){t.onSuccess({invocationContext:t.invocationContext,grantedQos:e})}),t.onFailure&&(n.onFailure=function(e){t.onFailure({invocationContext:t.invocationContext,errorCode:e,errorMessage:_(e)})}),t.timeout&&(n.timeOut=new N(this,t.timeout,t.onFailure,[{invocationContext:t.invocationContext,errorCode:y.SUBSCRIBE_TIMEOUT.code,errorMessage:_(y.SUBSCRIBE_TIMEOUT)}])),this._requires_ack(n),this._schedule_message(n)},R.prototype.unsubscribe=function(e,t){if(this._trace("Client.unsubscribe",e,t),!this.connected)throw new Error(_(y.INVALID_STATE,["not connected"]));var n=new M(d);n.topics=e.constructor===Array?e:[e],t.onSuccess&&(n.callback=function(){t.onSuccess({invocationContext:t.invocationContext})}),t.timeout&&(n.timeOut=new N(this,t.timeout,t.onFailure,[{invocationContext:t.invocationContext,errorCode:y.UNSUBSCRIBE_TIMEOUT.code,errorMessage:_(y.UNSUBSCRIBE_TIMEOUT)}])),this._requires_ack(n),this._schedule_message(n)},R.prototype.send=function(e){this._trace("Client.send",e);var t=new M(o);if(t.payloadMessage=e,this.connected)e.qos>0?this._requires_ack(t):this.onMessageDelivered&&(this._notify_msg_sent[t]=this.onMessageDelivered(t.payloadMessage)),this._schedule_message(t);else{if(!this._reconnecting||!this.disconnectedPublishing)throw new Error(_(y.INVALID_STATE,["not connected"]));if(Object.keys(this._sentMessages).length+this._buffered_msg_queue.length>this.disconnectedBufferSize)throw new Error(_(y.BUFFER_FULL,[this.disconnectedBufferSize]));e.qos>0?this._requires_ack(t):(t.sequence=++this._sequence,this._buffered_msg_queue.unshift(t))}},R.prototype.disconnect=function(){if(this._trace("Client.disconnect"),this._reconnecting&&(this._reconnectTimeout.cancel(),this._reconnectTimeout=null,this._reconnecting=!1),!this.socket)throw new Error(_(y.INVALID_STATE,["not connecting or connected"]));var e=new M(g);this._notify_msg_sent[e]=b(this._disconnected,this),this._schedule_message(e)},R.prototype.getTraceLog=function(){if(null!==this._traceBuffer){for(var e in this._trace("Client.getTraceLog",new Date),this._trace("Client.getTraceLog in flight messages",this._sentMessages.length),this._sentMessages)this._trace("_sentMessages ",e,this._sentMessages[e]);for(var e in this._receivedMessages)this._trace("_receivedMessages ",e,this._receivedMessages[e]);return this._traceBuffer}},R.prototype.startTrace=function(){null===this._traceBuffer&&(this._traceBuffer=[]),this._trace("Client.startTrace",new Date,"@VERSION@-@BUILDLEVEL@")},R.prototype.stopTrace=function(){delete this._traceBuffer},R.prototype._doConnect=function(e){if(this.connectOptions.useSSL){var t=e.split(":");t[0]="wss",e=t.join(":")}this._wsuri=e,this.connected=!1,this.connectOptions.mqttVersion<4?this.socket=new WebSocket(e,["mqttv3.1"]):this.socket=new WebSocket(e,["mqtt"]),this.socket.binaryType="arraybuffer",this.socket.onopen=b(this._on_socket_open,this),this.socket.onmessage=b(this._on_socket_message,this),this.socket.onerror=b(this._on_socket_error,this),this.socket.onclose=b(this._on_socket_close,this),this.sendPinger=new P(this,this.connectOptions.keepAliveInterval),this.receivePinger=new P(this,this.connectOptions.keepAliveInterval),this._connectTimeout&&(this._connectTimeout.cancel(),this._connectTimeout=null),this._connectTimeout=new N(this,this.connectOptions.timeout,this._disconnected,[y.CONNECT_TIMEOUT.code,_(y.CONNECT_TIMEOUT)])},R.prototype._schedule_message=function(e){this._msg_queue.unshift(e),this.connected&&this._process_queue()},R.prototype.store=function(e,t){var r={type:t.type,messageIdentifier:t.messageIdentifier,version:1};switch(t.type){case o:t.pubRecReceived&&(r.pubRecReceived=!0),r.payloadMessage={};for(var i="",s=t.payloadMessage.payloadBytes,a=0;a<s.length;a++)s[a]<=15?i=i+"0"+s[a].toString(16):i+=s[a].toString(16);r.payloadMessage.payloadHex=i,r.payloadMessage.qos=t.payloadMessage.qos,r.payloadMessage.destinationName=t.payloadMessage.destinationName,t.payloadMessage.duplicate&&(r.payloadMessage.duplicate=!0),t.payloadMessage.retained&&(r.payloadMessage.retained=!0),0===e.indexOf("Sent:")&&(void 0===t.sequence&&(t.sequence=++this._sequence),r.sequence=t.sequence);break;default:throw Error(_(y.INVALID_STORED_DATA,[e+this._localKey+t.messageIdentifier,r]))}n.setItem(e+this._localKey+t.messageIdentifier,JSON.stringify(r))},R.prototype.restore=function(e){var t=n.getItem(e),r=JSON.parse(t),i=new M(r.type,r);switch(r.type){case o:for(var s=r.payloadMessage.payloadHex,a=new ArrayBuffer(s.length/2),u=new Uint8Array(a),c=0;s.length>=2;){var f=parseInt(s.substring(0,2),16);s=s.substring(2,s.length),u[c++]=f}var l=new L(u);l.qos=r.payloadMessage.qos,l.destinationName=r.payloadMessage.destinationName,r.payloadMessage.duplicate&&(l.duplicate=!0),r.payloadMessage.retained&&(l.retained=!0),i.payloadMessage=l;break;default:throw Error(_(y.INVALID_STORED_DATA,[e,t]))}0===e.indexOf("Sent:"+this._localKey)?(i.payloadMessage.duplicate=!0,this._sentMessages[i.messageIdentifier]=i):0===e.indexOf("Received:"+this._localKey)&&(this._receivedMessages[i.messageIdentifier]=i)},R.prototype._process_queue=function(){for(var e=null;e=this._msg_queue.pop();)this._socket_send(e),this._notify_msg_sent[e]&&(this._notify_msg_sent[e](),delete this._notify_msg_sent[e])},R.prototype._requires_ack=function(e){var t=Object.keys(this._sentMessages).length;if(t>this.maxMessageIdentifier)throw Error("Too many messages:"+t);for(;void 0!==this._sentMessages[this._message_identifier];)this._message_identifier++;e.messageIdentifier=this._message_identifier,this._sentMessages[e.messageIdentifier]=e,e.type===o&&this.store("Sent:",e),this._message_identifier===this.maxMessageIdentifier&&(this._message_identifier=1)},R.prototype._on_socket_open=function(){var e=new M(r,this.connectOptions);e.clientId=this.clientId,this._socket_send(e)},R.prototype._on_socket_message=function(e){this._trace("Client._on_socket_message",e.data);for(var t=this._deframeMessages(e.data),n=0;n<t.length;n+=1)this._handleMessage(t[n])},R.prototype._deframeMessages=function(e){var t=new Uint8Array(e),n=[];if(this.receiveBuffer){var r=new Uint8Array(this.receiveBuffer.length+t.length);r.set(this.receiveBuffer),r.set(t,this.receiveBuffer.length),t=r,delete this.receiveBuffer}try{for(var i=0;i<t.length;){var o=A(t,i),s=o[0];if(i=o[1],null===s)break;n.push(s)}i<t.length&&(this.receiveBuffer=t.subarray(i))}catch(e){var a="undefined"==e.hasOwnProperty("stack")?e.stack.toString():"No Error Stack Available";return void this._disconnected(y.INTERNAL_ERROR.code,_(y.INTERNAL_ERROR,[e.message,a]))}return n},R.prototype._handleMessage=function(e){this._trace("Client._handleMessage",e);try{switch(e.type){case i:if(this._connectTimeout.cancel(),this._reconnectTimeout&&this._reconnectTimeout.cancel(),this.connectOptions.cleanSession){for(var t in this._sentMessages){var r=this._sentMessages[t];n.removeItem("Sent:"+this._localKey+r.messageIdentifier)}for(var t in this._sentMessages={},this._receivedMessages){var f=this._receivedMessages[t];n.removeItem("Received:"+this._localKey+f.messageIdentifier)}this._receivedMessages={}}if(0!==e.returnCode){this._disconnected(y.CONNACK_RETURNCODE.code,_(y.CONNACK_RETURNCODE,[e.returnCode,w[e.returnCode]]));break}this.connected=!0,this.connectOptions.uris&&(this.hostIndex=this.connectOptions.uris.length);var d=[];for(var p in this._sentMessages)this._sentMessages.hasOwnProperty(p)&&d.push(this._sentMessages[p]);if(this._buffered_msg_queue.length>0)for(var m=null;m=this._buffered_msg_queue.pop();)d.push(m),this.onMessageDelivered&&(this._notify_msg_sent[m]=this.onMessageDelivered(m.payloadMessage));d=d.sort((function(e,t){return e.sequence-t.sequence}));for(var b=0,S=d.length;b<S;b++)if((r=d[b]).type==o&&r.pubRecReceived){var E=new M(u,{messageIdentifier:r.messageIdentifier});this._schedule_message(E)}else this._schedule_message(r);this.connectOptions.onSuccess&&this.connectOptions.onSuccess({invocationContext:this.connectOptions.invocationContext});var A=!1;this._reconnecting&&(A=!0,this._reconnectInterval=1,this._reconnecting=!1),this._connected(A,this._wsuri),this._process_queue();break;case o:this._receivePublish(e);break;case s:(r=this._sentMessages[e.messageIdentifier])&&(delete this._sentMessages[e.messageIdentifier],n.removeItem("Sent:"+this._localKey+e.messageIdentifier),this.onMessageDelivered&&this.onMessageDelivered(r.payloadMessage));break;case a:(r=this._sentMessages[e.messageIdentifier])&&(r.pubRecReceived=!0,E=new M(u,{messageIdentifier:e.messageIdentifier}),this.store("Sent:",r),this._schedule_message(E));break;case u:f=this._receivedMessages[e.messageIdentifier],n.removeItem("Received:"+this._localKey+e.messageIdentifier),f&&(this._receiveMessage(f),delete this._receivedMessages[e.messageIdentifier]);var I=new M(c,{messageIdentifier:e.messageIdentifier});this._schedule_message(I);break;case c:r=this._sentMessages[e.messageIdentifier],delete this._sentMessages[e.messageIdentifier],n.removeItem("Sent:"+this._localKey+e.messageIdentifier),this.onMessageDelivered&&this.onMessageDelivered(r.payloadMessage);break;case l:(r=this._sentMessages[e.messageIdentifier])&&(r.timeOut&&r.timeOut.cancel(),128===e.returnCode[0]?r.onFailure&&r.onFailure(e.returnCode):r.onSuccess&&r.onSuccess(e.returnCode),delete this._sentMessages[e.messageIdentifier]);break;case h:(r=this._sentMessages[e.messageIdentifier])&&(r.timeOut&&r.timeOut.cancel(),r.callback&&r.callback(),delete this._sentMessages[e.messageIdentifier]);break;case v:this.sendPinger.reset();break;case g:this._disconnected(y.INVALID_MQTT_MESSAGE_TYPE.code,_(y.INVALID_MQTT_MESSAGE_TYPE,[e.type]));break;default:this._disconnected(y.INVALID_MQTT_MESSAGE_TYPE.code,_(y.INVALID_MQTT_MESSAGE_TYPE,[e.type]))}}catch(e){var k="undefined"==e.hasOwnProperty("stack")?e.stack.toString():"No Error Stack Available";return void this._disconnected(y.INTERNAL_ERROR.code,_(y.INTERNAL_ERROR,[e.message,k]))}},R.prototype._on_socket_error=function(e){this._reconnecting||this._disconnected(y.SOCKET_ERROR.code,_(y.SOCKET_ERROR,[e.data]))},R.prototype._on_socket_close=function(){this._reconnecting||this._disconnected(y.SOCKET_CLOSE.code,_(y.SOCKET_CLOSE))},R.prototype._socket_send=function(e){if(1==e.type){var t=this._traceMask(e,"password");this._trace("Client._socket_send",t)}else this._trace("Client._socket_send",e);this.socket.send(e.encode()),this.sendPinger.reset()},R.prototype._receivePublish=function(e){switch(e.payloadMessage.qos){case"undefined":case 0:this._receiveMessage(e);break;case 1:var t=new M(s,{messageIdentifier:e.messageIdentifier});this._schedule_message(t),this._receiveMessage(e);break;case 2:this._receivedMessages[e.messageIdentifier]=e,this.store("Received:",e);var n=new M(a,{messageIdentifier:e.messageIdentifier});this._schedule_message(n);break;default:throw Error("Invaild qos="+e.payloadMessage.qos)}},R.prototype._receiveMessage=function(e){this.onMessageArrived&&this.onMessageArrived(e.payloadMessage)},R.prototype._connected=function(e,t){this.onConnected&&this.onConnected(e,t)},R.prototype._reconnect=function(){this._trace("Client._reconnect"),this.connected||(this._reconnecting=!0,this.sendPinger.cancel(),this.receivePinger.cancel(),this._reconnectInterval<128&&(this._reconnectInterval=2*this._reconnectInterval),this.connectOptions.uris?(this.hostIndex=0,this._doConnect(this.connectOptions.uris[0])):this._doConnect(this.uri))},R.prototype._disconnected=function(e,t){if(this._trace("Client._disconnected",e,t),void 0!==e&&this._reconnecting)this._reconnectTimeout=new N(this,this._reconnectInterval,this._reconnect);else if(this.sendPinger.cancel(),this.receivePinger.cancel(),this._connectTimeout&&(this._connectTimeout.cancel(),this._connectTimeout=null),this._msg_queue=[],this._buffered_msg_queue=[],this._notify_msg_sent={},this.socket&&(this.socket.onopen=null,this.socket.onmessage=null,this.socket.onerror=null,this.socket.onclose=null,1===this.socket.readyState&&this.socket.close(),delete this.socket),this.connectOptions.uris&&this.hostIndex<this.connectOptions.uris.length-1)this.hostIndex++,this._doConnect(this.connectOptions.uris[this.hostIndex]);else if(void 0===e&&(e=y.OK.code,t=_(y.OK)),this.connected){if(this.connected=!1,this.onConnectionLost&&this.onConnectionLost({errorCode:e,errorMessage:t,reconnect:this.connectOptions.reconnect,uri:this._wsuri}),e!==y.OK.code&&this.connectOptions.reconnect)return this._reconnectInterval=1,void this._reconnect()}else 4===this.connectOptions.mqttVersion&&!1===this.connectOptions.mqttVersionExplicit?(this._trace("Failed to connect V4, dropping back to V3"),this.connectOptions.mqttVersion=3,this.connectOptions.uris?(this.hostIndex=0,this._doConnect(this.connectOptions.uris[0])):this._doConnect(this.uri)):this.connectOptions.onFailure&&this.connectOptions.onFailure({invocationContext:this.connectOptions.invocationContext,errorCode:e,errorMessage:t})},R.prototype._trace=function(){if(this.traceFunction){var e=Array.prototype.slice.call(arguments);for(var t in e)void 0!==e[t]&&e.splice(t,1,JSON.stringify(e[t]));var n=e.join("");this.traceFunction({severity:"Debug",message:n})}if(null!==this._traceBuffer){t=0;for(var r=arguments.length;t<r;t++)this._traceBuffer.length==this._MAX_TRACE_ENTRIES&&this._traceBuffer.shift(),0===t||void 0===arguments[t]?this._traceBuffer.push(arguments[t]):this._traceBuffer.push(" "+JSON.stringify(arguments[t]))}},R.prototype._traceMask=function(e,t){var n={};for(var r in e)e.hasOwnProperty(r)&&(n[r]=r==t?"******":e[r]);return n};var L=function(e){var t,n;if(!("string"==typeof e||e instanceof ArrayBuffer||ArrayBuffer.isView(e)&&!(e instanceof DataView)))throw _(y.INVALID_ARGUMENT,[e,"newPayload"]);t=e;var r=0,i=!1,o=!1;Object.defineProperties(this,{payloadString:{enumerable:!0,get:function(){return"string"==typeof t?t:T(t,0,t.length)}},payloadBytes:{enumerable:!0,get:function(){if("string"==typeof t){var e=new ArrayBuffer(x(t)),n=new Uint8Array(e);return C(t,n,0),n}return t}},destinationName:{enumerable:!0,get:function(){return n},set:function(e){if("string"!=typeof e)throw new Error(_(y.INVALID_ARGUMENT,[e,"newDestinationName"]));n=e}},qos:{enumerable:!0,get:function(){return r},set:function(e){if(0!==e&&1!==e&&2!==e)throw new Error("Invalid argument:"+e);r=e}},retained:{enumerable:!0,get:function(){return i},set:function(e){if("boolean"!=typeof e)throw new Error(_(y.INVALID_ARGUMENT,[e,"newRetained"]));i=e}},topic:{enumerable:!0,get:function(){return n},set:function(e){n=e}},duplicate:{enumerable:!0,get:function(){return o},set:function(e){o=e}}})};return{Client:function(e,t,n,r){var i;if("string"!=typeof e)throw new Error(_(y.INVALID_TYPE,[typeof e,"host"]));if(2==arguments.length){r=t;var o=(i=e).match(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/);if(!o)throw new Error(_(y.INVALID_ARGUMENT,[e,"host"]));e=o[4]||o[2],t=parseInt(o[7]),n=o[8]}else{if(3==arguments.length&&(r=n,n="/mqtt"),"number"!=typeof t||t<0)throw new Error(_(y.INVALID_TYPE,[typeof t,"port"]));if("string"!=typeof n)throw new Error(_(y.INVALID_TYPE,[typeof n,"path"]));var s=-1!==e.indexOf(":")&&"["!==e.slice(0,1)&&"]"!==e.slice(-1);i="ws://"+(s?"["+e+"]":e)+":"+t+n}for(var a=0,u=0;u<r.length;u++){var c=r.charCodeAt(u);55296<=c&&c<=56319&&u++,a++}if("string"!=typeof r||a>65535)throw new Error(_(y.INVALID_ARGUMENT,[r,"clientId"]));var f=new R(i,e,t,n,r);Object.defineProperties(this,{host:{get:function(){return e},set:function(){throw new Error(_(y.UNSUPPORTED_OPERATION))}},port:{get:function(){return t},set:function(){throw new Error(_(y.UNSUPPORTED_OPERATION))}},path:{get:function(){return n},set:function(){throw new Error(_(y.UNSUPPORTED_OPERATION))}},uri:{get:function(){return i},set:function(){throw new Error(_(y.UNSUPPORTED_OPERATION))}},clientId:{get:function(){return f.clientId},set:function(){throw new Error(_(y.UNSUPPORTED_OPERATION))}},onConnected:{get:function(){return f.onConnected},set:function(e){if("function"!=typeof e)throw new Error(_(y.INVALID_TYPE,[typeof e,"onConnected"]));f.onConnected=e}},disconnectedPublishing:{get:function(){return f.disconnectedPublishing},set:function(e){f.disconnectedPublishing=e}},disconnectedBufferSize:{get:function(){return f.disconnectedBufferSize},set:function(e){f.disconnectedBufferSize=e}},onConnectionLost:{get:function(){return f.onConnectionLost},set:function(e){if("function"!=typeof e)throw new Error(_(y.INVALID_TYPE,[typeof e,"onConnectionLost"]));f.onConnectionLost=e}},onMessageDelivered:{get:function(){return f.onMessageDelivered},set:function(e){if("function"!=typeof e)throw new Error(_(y.INVALID_TYPE,[typeof e,"onMessageDelivered"]));f.onMessageDelivered=e}},onMessageArrived:{get:function(){return f.onMessageArrived},set:function(e){if("function"!=typeof e)throw new Error(_(y.INVALID_TYPE,[typeof e,"onMessageArrived"]));f.onMessageArrived=e}},trace:{get:function(){return f.traceFunction},set:function(e){if("function"!=typeof e)throw new Error(_(y.INVALID_TYPE,[typeof e,"onTrace"]));f.traceFunction=e}}}),this.connect=function(e){if(m(e=e||{},{timeout:"number",userName:"string",password:"string",willMessage:"object",keepAliveInterval:"number",cleanSession:"boolean",useSSL:"boolean",invocationContext:"object",onSuccess:"function",onFailure:"function",hosts:"object",ports:"object",reconnect:"boolean",mqttVersion:"number",mqttVersionExplicit:"boolean",uris:"object"}),void 0===e.keepAliveInterval&&(e.keepAliveInterval=60),e.mqttVersion>4||e.mqttVersion<3)throw new Error(_(y.INVALID_ARGUMENT,[e.mqttVersion,"connectOptions.mqttVersion"]));if(void 0===e.mqttVersion?(e.mqttVersionExplicit=!1,e.mqttVersion=4):e.mqttVersionExplicit=!0,void 0!==e.password&&void 0===e.userName)throw new Error(_(y.INVALID_ARGUMENT,[e.password,"connectOptions.password"]));if(e.willMessage){if(!(e.willMessage instanceof L))throw new Error(_(y.INVALID_TYPE,[e.willMessage,"connectOptions.willMessage"]));if(e.willMessage.stringPayload=null,void 0===e.willMessage.destinationName)throw new Error(_(y.INVALID_TYPE,[typeof e.willMessage.destinationName,"connectOptions.willMessage.destinationName"]))}if(void 0===e.cleanSession&&(e.cleanSession=!0),e.hosts){if(!(e.hosts instanceof Array))throw new Error(_(y.INVALID_ARGUMENT,[e.hosts,"connectOptions.hosts"]));if(e.hosts.length<1)throw new Error(_(y.INVALID_ARGUMENT,[e.hosts,"connectOptions.hosts"]));for(var t=!1,r=0;r<e.hosts.length;r++){if("string"!=typeof e.hosts[r])throw new Error(_(y.INVALID_TYPE,[typeof e.hosts[r],"connectOptions.hosts["+r+"]"]));if(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/.test(e.hosts[r])){if(0===r)t=!0;else if(!t)throw new Error(_(y.INVALID_ARGUMENT,[e.hosts[r],"connectOptions.hosts["+r+"]"]))}else if(t)throw new Error(_(y.INVALID_ARGUMENT,[e.hosts[r],"connectOptions.hosts["+r+"]"]))}if(t)e.uris=e.hosts;else{if(!e.ports)throw new Error(_(y.INVALID_ARGUMENT,[e.ports,"connectOptions.ports"]));if(!(e.ports instanceof Array))throw new Error(_(y.INVALID_ARGUMENT,[e.ports,"connectOptions.ports"]));if(e.hosts.length!==e.ports.length)throw new Error(_(y.INVALID_ARGUMENT,[e.ports,"connectOptions.ports"]));for(e.uris=[],r=0;r<e.hosts.length;r++){if("number"!=typeof e.ports[r]||e.ports[r]<0)throw new Error(_(y.INVALID_TYPE,[typeof e.ports[r],"connectOptions.ports["+r+"]"]));var o=e.hosts[r],s=e.ports[r],a=-1!==o.indexOf(":");i="ws://"+(a?"["+o+"]":o)+":"+s+n,e.uris.push(i)}}}f.connect(e)},this.subscribe=function(e,t){if("string"!=typeof e&&e.constructor!==Array)throw new Error("Invalid argument:"+e);if(m(t=t||{},{qos:"number",invocationContext:"object",onSuccess:"function",onFailure:"function",timeout:"number"}),t.timeout&&!t.onFailure)throw new Error("subscribeOptions.timeout specified with no onFailure callback.");if(void 0!==t.qos&&0!==t.qos&&1!==t.qos&&2!==t.qos)throw new Error(_(y.INVALID_ARGUMENT,[t.qos,"subscribeOptions.qos"]));f.subscribe(e,t)},this.unsubscribe=function(e,t){if("string"!=typeof e&&e.constructor!==Array)throw new Error("Invalid argument:"+e);if(m(t=t||{},{invocationContext:"object",onSuccess:"function",onFailure:"function",timeout:"number"}),t.timeout&&!t.onFailure)throw new Error("unsubscribeOptions.timeout specified with no onFailure callback.");f.unsubscribe(e,t)},this.send=function(e,t,n,r){var i;if(0===arguments.length)throw new Error("Invalid argument.length");if(1==arguments.length){if(!(e instanceof L)&&"string"!=typeof e)throw new Error("Invalid argument:"+typeof e);if(void 0===(i=e).destinationName)throw new Error(_(y.INVALID_ARGUMENT,[i.destinationName,"Message.destinationName"]));f.send(i)}else(i=new L(t)).destinationName=e,arguments.length>=3&&(i.qos=n),arguments.length>=4&&(i.retained=r),f.send(i)},this.publish=function(e,t,n,r){var i;if(0===arguments.length)throw new Error("Invalid argument.length");if(1==arguments.length){if(!(e instanceof L)&&"string"!=typeof e)throw new Error("Invalid argument:"+typeof e);if(void 0===(i=e).destinationName)throw new Error(_(y.INVALID_ARGUMENT,[i.destinationName,"Message.destinationName"]));f.send(i)}else(i=new L(t)).destinationName=e,arguments.length>=3&&(i.qos=n),arguments.length>=4&&(i.retained=r),f.send(i)},this.disconnect=function(){f.disconnect()},this.getTraceLog=function(){return f.getTraceLog()},this.startTrace=function(){f.startTrace()},this.stopTrace=function(){f.stopTrace()},this.isConnected=function(){return f.connected}},Message:L}}(void 0!==t?t:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},e.exports=n()}).call(this,n(31))},function(e,t,n){"use strict";var r,i,o=n(486);function s(e,t,n){if(e._observer)a(e._observer,t,n);else if(e._observers){var r=[];e._observers.forEach((function(e){r.push(e)})),r.forEach((function(e){a(e,t,n)}))}}function a(e,t,n){if(!e.closed)switch(t){case"next":return e.next(n);case"error":return e.error(n);case"complete":return e.complete(n)}}function u(e){return e._observer||e._observers&&e._observers.size>0}function c(e){var t=this;this._observer=null,this._observers=null,this._observable=new o((function(n){return function(e,t){!u(e)&&t&&t.start&&t.start()}(t,e),function(e,t){e._observers?e._observers.add(t):e._observer?(e._observers=new Set,e._observers.add(e._observer),e._observers.add(t),e._observer=null):e._observer=t}(t,n),function(){!function(e,t){e._observers?e._observers.delete(t):e._observer===t&&(e._observer=null)}(t,n),function(e,t){!u(e)&&t&&t.pause&&t.pause()}(t,e)}}))}r=c.prototype,i={get observable(){return this._observable},get observed(){return u(this)},next:function(e){s(this,"next",e)},error:function(e){s(this,"error",e)},complete:function(e){s(this,"complete",e)}},Object.keys(i).forEach((function(e){var t=Object.getOwnPropertyDescriptor(i,e);t.enumerable=!1,Object.defineProperty(r,e,t)})),e.exports=c},function(e,t,n){"use strict";n.d(t,"a",(function(){return Nt}));var r=n(44),i=n(33),o=n(88),s=n(19);function a(e){for(var t=arguments.length,n=Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];throw Error("[Immer] minified error nr: "+e+(n.length?" "+n.map((function(e){return"'"+e+"'"})).join(","):"")+". Find the full error at: https://bit.ly/3cXEKWf")}function u(e){return!!e&&!!e[Z]}function c(e){return!!e&&(function(e){if(!e||"object"!=typeof e)return!1;var t=Object.getPrototypeOf(e);return!t||t===Object.prototype}(e)||Array.isArray(e)||!!e[J]||!!e.constructor[J]||g(e)||m(e))}function f(e,t,n){void 0===n&&(n=!1),0===l(e)?(n?Object.keys:X)(e).forEach((function(r){n&&"symbol"==typeof r||t(r,e[r],e)})):e.forEach((function(n,r){return t(r,n,e)}))}function l(e){var t=e[Z];return t?t.i>3?t.i-4:t.i:Array.isArray(e)?1:g(e)?2:m(e)?3:0}function d(e,t){return 2===l(e)?e.has(t):Object.prototype.hasOwnProperty.call(e,t)}function h(e,t){return 2===l(e)?e.get(t):e[t]}function p(e,t,n){var r=l(e);2===r?e.set(t,n):3===r?(e.delete(t),e.add(n)):e[t]=n}function v(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}function g(e){return G&&e instanceof Map}function m(e){return W&&e instanceof Set}function b(e){return e.o||e.t}function y(e){if(Array.isArray(e))return Array.prototype.slice.call(e);var t=Q(e);delete t[Z];for(var n=X(t),r=0;r<n.length;r++){var i=n[r],o=t[i];!1===o.writable&&(o.writable=!0,o.configurable=!0),(o.get||o.set)&&(t[i]={configurable:!0,writable:!0,enumerable:o.enumerable,value:e[i]})}return Object.create(Object.getPrototypeOf(e),t)}function w(e,t){return void 0===t&&(t=!1),S(e)||u(e)||!c(e)||(l(e)>1&&(e.set=e.add=e.clear=e.delete=_),Object.freeze(e),t&&f(e,(function(e,t){return w(t,!0)}),!0)),e}function _(){a(2)}function S(e){return null==e||"object"!=typeof e||Object.isFrozen(e)}function E(e){var t=ee[e];return t||a(18,e),t}function M(e,t){ee[e]||(ee[e]=t)}function A(){return H}function I(e,t){t&&(E("Patches"),e.u=[],e.s=[],e.v=t)}function k(e){O(e),e.p.forEach(C),e.p=null}function O(e){e===H&&(H=e.l)}function x(e){return H={p:[],l:H,h:e,m:!0,_:0}}function C(e){var t=e[Z];0===t.i||1===t.i?t.j():t.g=!0}function T(e,t){t._=t.p.length;var n=t.p[0],r=void 0!==e&&e!==n;return t.h.O||E("ES5").S(t,e,r),r?(n[Z].P&&(k(t),a(4)),c(e)&&(e=P(t,e),t.l||R(t,e)),t.u&&E("Patches").M(n[Z],e,t.u,t.s)):e=P(t,n,[]),k(t),t.u&&t.v(t.u,t.s),e!==Y?e:void 0}function P(e,t,n){if(S(t))return t;var r=t[Z];if(!r)return f(t,(function(i,o){return N(e,r,t,i,o,n)}),!0),t;if(r.A!==e)return t;if(!r.P)return R(e,r.t,!0),r.t;if(!r.I){r.I=!0,r.A._--;var i=4===r.i||5===r.i?r.o=y(r.k):r.o;f(3===r.i?new Set(i):i,(function(t,o){return N(e,r,i,t,o,n)})),R(e,i,!1),n&&e.u&&E("Patches").R(r,n,e.u,e.s)}return r.o}function N(e,t,n,r,i,o){if(u(i)){var s=P(e,i,o&&t&&3!==t.i&&!d(t.D,r)?o.concat(r):void 0);if(p(n,r,s),!u(s))return;e.m=!1}if(c(i)&&!S(i)){if(!e.h.N&&e._<1)return;P(e,i),t&&t.A.l||R(e,i)}}function R(e,t,n){void 0===n&&(n=!1),e.h.N&&e.m&&w(t,n)}function L(e,t){var n=e[Z];return(n?b(n):e)[t]}function j(e,t){if(t in e)for(var n=Object.getPrototypeOf(e);n;){var r=Object.getOwnPropertyDescriptor(n,t);if(r)return r;n=Object.getPrototypeOf(n)}}function D(e){e.P||(e.P=!0,e.l&&D(e.l))}function U(e){e.o||(e.o=y(e.t))}function B(e,t,n){var r=g(t)?E("MapSet").T(t,n):m(t)?E("MapSet").F(t,n):e.O?function(e,t){var n=Array.isArray(e),r={i:n?1:0,A:t?t.A:A(),P:!1,I:!1,D:{},l:t,t:e,k:null,o:null,j:null,C:!1},i=r,o=te;n&&(i=[r],o=ne);var s=Proxy.revocable(i,o),a=s.revoke,u=s.proxy;return r.k=u,r.j=a,u}(t,n):E("ES5").J(t,n);return(n?n.A:A()).p.push(r),r}function F(e){return u(e)||a(22,e),function e(t){if(!c(t))return t;var n,r=t[Z],i=l(t);if(r){if(!r.P&&(r.i<4||!E("ES5").K(r)))return r.t;r.I=!0,n=z(t,i),r.I=!1}else n=z(t,i);return f(n,(function(t,i){r&&h(r.t,t)===i||p(n,t,e(i))})),3===i?new Set(n):n}(e)}function z(e,t){switch(t){case 2:return new Map(e);case 3:return Array.from(e)}return y(e)}function q(){function e(t){if(!c(t))return t;if(Array.isArray(t))return t.map(e);if(g(t))return new Map(Array.from(t.entries()).map((function(t){return[t[0],e(t[1])]})));if(m(t))return new Set(Array.from(t).map(e));var n=Object.create(Object.getPrototypeOf(t));for(var r in t)n[r]=e(t[r]);return n}function t(t){return u(t)?e(t):t}var n="add";M("Patches",{$:function(t,r){return r.forEach((function(r){for(var i=r.path,o=r.op,s=t,u=0;u<i.length-1;u++){var c=l(s),f=i[u];0!==c&&1!==c||"__proto__"!==f&&"constructor"!==f||a(24),"function"==typeof s&&"prototype"===f&&a(24),"object"!=typeof(s=h(s,f))&&a(15,i.join("/"))}var d=l(s),p=e(r.value),v=i[i.length-1];switch(o){case"replace":switch(d){case 2:return s.set(v,p);case 3:a(16);default:return s[v]=p}case n:switch(d){case 1:return s.splice(v,0,p);case 2:return s.set(v,p);case 3:return s.add(p);default:return s[v]=p}case"remove":switch(d){case 1:return s.splice(v,1);case 2:return s.delete(v);case 3:return s.delete(r.value);default:return delete s[v]}default:a(17,o)}})),t},R:function(e,r,i,o){switch(e.i){case 0:case 4:case 2:return function(e,r,i,o){var s=e.t,a=e.o;f(e.D,(function(e,u){var c=h(s,e),f=h(a,e),l=u?d(s,e)?"replace":n:"remove";if(c!==f||"replace"!==l){var p=r.concat(e);i.push("remove"===l?{op:l,path:p}:{op:l,path:p,value:f}),o.push(l===n?{op:"remove",path:p}:"remove"===l?{op:n,path:p,value:t(c)}:{op:"replace",path:p,value:t(c)})}}))}(e,r,i,o);case 5:case 1:return function(e,r,i,o){var s=e.t,a=e.D,u=e.o;if(u.length<s.length){var c=[u,s];s=c[0],u=c[1];var f=[o,i];i=f[0],o=f[1]}for(var l=0;l<s.length;l++)if(a[l]&&u[l]!==s[l]){var d=r.concat([l]);i.push({op:"replace",path:d,value:t(u[l])}),o.push({op:"replace",path:d,value:t(s[l])})}for(var h=s.length;h<u.length;h++){var p=r.concat([h]);i.push({op:n,path:p,value:t(u[h])})}s.length<u.length&&o.push({op:"replace",path:r.concat(["length"]),value:s.length})}(e,r,i,o);case 3:return function(e,t,r,i){var o=e.t,s=e.o,a=0;o.forEach((function(e){if(!s.has(e)){var o=t.concat([a]);r.push({op:"remove",path:o,value:e}),i.unshift({op:n,path:o,value:e})}a++})),a=0,s.forEach((function(e){if(!o.has(e)){var s=t.concat([a]);r.push({op:n,path:s,value:e}),i.unshift({op:"remove",path:s,value:e})}a++}))}(e,r,i,o)}},M:function(e,t,n,r){n.push({op:"replace",path:[],value:t}),r.push({op:"replace",path:[],value:e.t})}})}var K,H,V="undefined"!=typeof Symbol&&"symbol"==typeof Symbol("x"),G="undefined"!=typeof Map,W="undefined"!=typeof Set,$="undefined"!=typeof Proxy&&void 0!==Proxy.revocable&&"undefined"!=typeof Reflect,Y=V?Symbol.for("immer-nothing"):((K={})["immer-nothing"]=!0,K),J=V?Symbol.for("immer-draftable"):"__$immer_draftable",Z=V?Symbol.for("immer-state"):"__$immer_state",X=("undefined"!=typeof Symbol&&Symbol.iterator,"undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:Object.getOwnPropertyNames),Q=Object.getOwnPropertyDescriptors||function(e){var t={};return X(e).forEach((function(n){t[n]=Object.getOwnPropertyDescriptor(e,n)})),t},ee={},te={get:function(e,t){if(t===Z)return e;var n=b(e);if(!d(n,t))return function(e,t,n){var r,i=j(t,n);return i?"value"in i?i.value:null===(r=i.get)||void 0===r?void 0:r.call(e.k):void 0}(e,n,t);var r=n[t];return e.I||!c(r)?r:r===L(e.t,t)?(U(e),e.o[t]=B(e.A.h,r,e)):r},has:function(e,t){return t in b(e)},ownKeys:function(e){return Reflect.ownKeys(b(e))},set:function(e,t,n){var r=j(b(e),t);if(null==r?void 0:r.set)return r.set.call(e.k,n),!0;if(!e.P){var i=L(b(e),t),o=null==i?void 0:i[Z];if(o&&o.t===n)return e.o[t]=n,e.D[t]=!1,!0;if(v(n,i)&&(void 0!==n||d(e.t,t)))return!0;U(e),D(e)}return e.o[t]=n,e.D[t]=!0,!0},deleteProperty:function(e,t){return void 0!==L(e.t,t)||t in e.t?(e.D[t]=!1,U(e),D(e)):delete e.D[t],e.o&&delete e.o[t],!0},getOwnPropertyDescriptor:function(e,t){var n=b(e),r=Reflect.getOwnPropertyDescriptor(n,t);return r?{writable:!0,configurable:1!==e.i||"length"!==t,enumerable:r.enumerable,value:n[t]}:r},defineProperty:function(){a(11)},getPrototypeOf:function(e){return Object.getPrototypeOf(e.t)},setPrototypeOf:function(){a(12)}},ne={};f(te,(function(e,t){ne[e]=function(){return arguments[0]=arguments[0][0],t.apply(this,arguments)}})),ne.deleteProperty=function(e,t){return te.deleteProperty.call(this,e[0],t)},ne.set=function(e,t,n){return te.set.call(this,e[0],t,n,e[0])};var re,ie=new(function(){function e(e){this.O=$,this.N=!0,"boolean"==typeof(null==e?void 0:e.useProxies)&&this.setUseProxies(e.useProxies),"boolean"==typeof(null==e?void 0:e.autoFreeze)&&this.setAutoFreeze(e.autoFreeze),this.produce=this.produce.bind(this),this.produceWithPatches=this.produceWithPatches.bind(this)}var t=e.prototype;return t.produce=function(e,t,n){if("function"==typeof e&&"function"!=typeof t){var r=t;t=e;var i=this;return function(e){var n=this;void 0===e&&(e=r);for(var o=arguments.length,s=Array(o>1?o-1:0),a=1;a<o;a++)s[a-1]=arguments[a];return i.produce(e,(function(e){var r;return(r=t).call.apply(r,[n,e].concat(s))}))}}var o;if("function"!=typeof t&&a(6),void 0!==n&&"function"!=typeof n&&a(7),c(e)){var s=x(this),u=B(this,e,void 0),f=!0;try{o=t(u),f=!1}finally{f?k(s):O(s)}return"undefined"!=typeof Promise&&o instanceof Promise?o.then((function(e){return I(s,n),T(e,s)}),(function(e){throw k(s),e})):(I(s,n),T(o,s))}if(!e||"object"!=typeof e){if((o=t(e))===Y)return;return void 0===o&&(o=e),this.N&&w(o,!0),o}a(21,e)},t.produceWithPatches=function(e,t){var n,r,i=this;return"function"==typeof e?function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return i.produceWithPatches(t,(function(t){return e.apply(void 0,[t].concat(r))}))}:[this.produce(e,t,(function(e,t){n=e,r=t})),n,r]},t.createDraft=function(e){c(e)||a(8),u(e)&&(e=F(e));var t=x(this),n=B(this,e,void 0);return n[Z].C=!0,O(t),n},t.finishDraft=function(e,t){var n=(e&&e[Z]).A;return I(n,t),T(void 0,n)},t.setAutoFreeze=function(e){this.N=e},t.setUseProxies=function(e){e&&!$&&a(20),this.O=e},t.applyPatches=function(e,t){var n;for(n=t.length-1;n>=0;n--){var r=t[n];if(0===r.path.length&&"replace"===r.op){e=r.value;break}}var i=E("Patches").$;return u(e)?i(e,t):this.produce(e,(function(e){return i(e,t.slice(n+1))}))},e}()),oe=(ie.produce,ie.produceWithPatches.bind(ie),ie.setAutoFreeze.bind(ie)),se=(ie.setUseProxies.bind(ie),ie.applyPatches.bind(ie),ie.createDraft.bind(ie),ie.finishDraft.bind(ie),n(109),n(14)),ae=n(9),ue=n(245),ce=function(){function e(){this._queue=[],this._pending=!1}return e.prototype.isLocked=function(){return this._pending},e.prototype.acquire=function(){var e=this,t=new Promise((function(t){return e._queue.push(t)}));return this._pending||this._dispatchNext(),t},e.prototype.runExclusive=function(e){return this.acquire().then((function(t){var n;try{n=e()}catch(e){throw t(),e}return Promise.resolve(n).then((function(e){return t(),e}),(function(e){throw t(),e}))}))},e.prototype._dispatchNext=function(){this._queue.length>0?(this._pending=!0,this._queue.shift()(this._dispatchNext.bind(this))):this._pending=!1},e}(),fe=n(257),le=n.n(fe),de=n(4),he=n(3),pe=function(){return Object(i.b)().isBrowser&&window.indexedDB||Object(i.c)()&&self.indexedDB?n(495).default:new(0,n(496).AsyncStorageAdapter)},ve=function(){return(ve=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},ge=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},me=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},be=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},ye=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},we=new r.a("DataStore"),_e=function(){function e(e,t,n,r,i,o){this.schema=e,this.namespaceResolver=t,this.getModelConstructorByModelName=n,this.modelInstanceCreator=r,this.adapter=i,this.sessionId=o,this.adapter=pe(),this.pushStream=new le.a}return e.getNamespace=function(){return{name:he.b,relationships:{},enums:{},models:{},nonModels:{}}},e.prototype.init=function(){return ge(this,void 0,void 0,(function(){var e,t;return me(this,(function(n){switch(n.label){case 0:return void 0===this.initialized?[3,2]:[4,this.initialized];case 1:return n.sent(),[2];case 2:return we.debug("Starting Storage"),this.initialized=new Promise((function(n,r){e=n,t=r})),this.adapter.setUp(this.schema,this.namespaceResolver,this.modelInstanceCreator,this.getModelConstructorByModelName,this.sessionId).then(e,t),[4,this.initialized];case 3:return n.sent(),[2]}}))}))},e.prototype.save=function(e,t,n,r){return ge(this,void 0,void 0,(function(){var i,o=this;return me(this,(function(s){switch(s.label){case 0:return[4,this.init()];case 1:return s.sent(),[4,this.adapter.save(e,t)];case 2:return(i=s.sent()).forEach((function(e){var i,s=ye(e,2),a=s[0],u=s[1];if(u===de.c.UPDATE&&r&&r.length){i={},r.map((function(e){return e.path&&e.path[0]})).forEach((function(e){i[e]=a[e]}));var c=a.id,f=a._version,l=a._lastChangedAt,d=a._deleted;i=ve(ve({},i),{id:c,_version:f,_lastChangedAt:l,_deleted:d})}var h=i||a,p=Object.getPrototypeOf(a).constructor;o.pushStream.next({model:p,opType:u,element:h,mutator:n,condition:ae.a.getPredicates(t,!1)})})),[2,i]}}))}))},e.prototype.delete=function(e,t,n){return ge(this,void 0,void 0,(function(){var r,i,o,s,a=this;return me(this,(function(u){switch(u.label){case 0:return[4,this.init()];case 1:return u.sent(),[4,this.adapter.delete(e,t)];case 2:return s=ye.apply(void 0,[u.sent(),2]),i=s[0],r=s[1],o=new Set(i.map((function(e){return e.id}))),Object(he.s)(e)||Array.isArray(r)||(r=[r]),r.forEach((function(r){var i,s=Object.getPrototypeOf(r).constructor;Object(he.s)(e)||(i=o.has(r.id)?ae.a.getPredicates(t,!1):void 0),a.pushStream.next({model:s,opType:de.c.DELETE,element:r,mutator:n,condition:i})})),[2,[i,r]]}}))}))},e.prototype.query=function(e,t,n){return ge(this,void 0,void 0,(function(){return me(this,(function(r){switch(r.label){case 0:return[4,this.init()];case 1:return r.sent(),[4,this.adapter.query(e,t,n)];case 2:return[2,r.sent()]}}))}))},e.prototype.queryOne=function(e,t){return void 0===t&&(t=de.d.FIRST),ge(this,void 0,void 0,(function(){return me(this,(function(n){switch(n.label){case 0:return[4,this.init()];case 1:return n.sent(),[4,this.adapter.queryOne(e,t)];case 2:return[2,n.sent()]}}))}))},e.prototype.observe=function(e,t,n){var r=!e,i=ae.a.getPredicates(t,!1)||{},o=i.predicates,s=i.type,a=!!o,u=this.pushStream.observable.filter((function(e){var t=e.mutator;return!n||t!==n})).map((function(e){e.mutator;return be(e,["mutator"])}));return r||(u=u.filter((function(t){var n=t.model,r=t.element;return e===n&&(!a||Object(he.y)(r,s,o))}))),u},e.prototype.clear=function(e){return void 0===e&&(e=!0),ge(this,void 0,void 0,(function(){return me(this,(function(t){switch(t.label){case 0:return this.initialized=void 0,[4,this.adapter.clear()];case 1:return t.sent(),e&&this.pushStream.complete(),[2]}}))}))},e.prototype.batchSave=function(e,t,n){return ge(this,void 0,void 0,(function(){var r,i=this;return me(this,(function(o){switch(o.label){case 0:return[4,this.init()];case 1:return o.sent(),[4,this.adapter.batchSave(e,t)];case 2:return(r=o.sent()).forEach((function(t){var r=ye(t,2),o=r[0],s=r[1];i.pushStream.next({model:e,opType:s,element:o,mutator:n,condition:void 0})})),[2,r]}}))}))},e}(),Se=function(){function e(e,t,n,r,i,o){this.mutex=new ce,this.storage=new _e(e,t,n,r,i,o)}return e.prototype.runExclusive=function(e){return this.mutex.runExclusive(e.bind(this,this.storage))},e.prototype.save=function(e,t,n,r){return ge(this,void 0,void 0,(function(){return me(this,(function(i){return[2,this.runExclusive((function(i){return i.save(e,t,n,r)}))]}))}))},e.prototype.delete=function(e,t,n){return ge(this,void 0,void 0,(function(){return me(this,(function(r){return[2,this.runExclusive((function(r){if(Object(he.s)(e)){var i=e;return r.delete(i,t,n)}var o=e;return r.delete(o,t,n)}))]}))}))},e.prototype.query=function(e,t,n){return ge(this,void 0,void 0,(function(){return me(this,(function(r){return[2,this.runExclusive((function(r){return r.query(e,t,n)}))]}))}))},e.prototype.queryOne=function(e,t){return void 0===t&&(t=de.d.FIRST),ge(this,void 0,void 0,(function(){return me(this,(function(n){return[2,this.runExclusive((function(n){return n.queryOne(e,t)}))]}))}))},e.getNamespace=function(){return _e.getNamespace()},e.prototype.observe=function(e,t,n){return this.storage.observe(e,t,n)},e.prototype.clear=function(){return ge(this,void 0,void 0,(function(){return me(this,(function(e){switch(e.label){case 0:return[4,this.storage.clear()];case 1:return e.sent(),[2]}}))}))},e.prototype.batchSave=function(e,t){return this.storage.batchSave(e,t)},e.prototype.init=function(){return ge(this,void 0,void 0,(function(){return me(this,(function(e){return[2,this.storage.init()]}))}))},e}(),Ee=n(34),Me=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Ae=(new(function(){function e(){}return e.prototype.networkMonitor=function(t){if(Object(i.b)().isNode)return se.a.from([{online:!0}]);var n=Object(i.c)()?self:window;return new se.a((function(t){t.next({online:n.navigator.onLine});var r=function(){return t.next({online:!0})},i=function(){return t.next({online:!1})};return n.addEventListener("online",r),n.addEventListener("offline",i),e._observers.push(t),function(){n.removeEventListener("online",r),n.removeEventListener("offline",i),e._observers=e._observers.filter((function(e){return e!==t}))}}))},e._observerOverride=function(t){var n,r,i=function(n){if(n.closed)return e._observers=e._observers.filter((function(e){return e!==n})),"continue";n.next(t)};try{for(var o=Me(e._observers),s=o.next();!s.done;s=o.next()){i(s.value)}}catch(e){n={error:e}}finally{try{s&&!s.done&&(r=o.return)&&r.call(o)}finally{if(n)throw n.error}}},e._observers=[],e}())).networkMonitor(),Ie=function(){return(Ie=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},ke=(new r.a("DataStore"),function(){function e(){this.connectionStatus={online:!1}}return e.prototype.status=function(){var e=this;if(this.observer)throw new Error("Subscriber already exists");return new se.a((function(t){return e.observer=t,e.subscription=Ae.subscribe((function(n){var r=n.online;e.connectionStatus.online=r;var i=Ie({},e.connectionStatus);t.next(i)})),function(){e.unsubscribe()}}))},e.prototype.unsubscribe=function(){this.subscription&&this.subscription.unsubscribe()},e.prototype.socketDisconnected=function(){var e=this;this.observer&&"function"==typeof this.observer.next&&(this.observer.next({online:!1}),setTimeout((function(){var t=Ie({},e.connectionStatus);e.observer.next(t)}),5e3))},e}()),Oe=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},xe=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},Ce=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},Te=function(){function e(e,t){this.outbox=e,this.ownSymbol=t}return e.prototype.merge=function(e,t){return Oe(this,void 0,void 0,(function(){var n,r,i,o,s;return xe(this,(function(a){switch(a.label){case 0:return[4,this.outbox.getForModel(e,t)];case 1:return r=a.sent(),i=t._deleted,0!==r.length?[3,5]:i?(n=de.c.DELETE,[4,e.delete(t,void 0,this.ownSymbol)]):[3,3];case 2:return a.sent(),[3,5];case 3:return[4,e.save(t,void 0,this.ownSymbol)];case 4:o=Ce.apply(void 0,[a.sent(),1]),s=Ce(o[0],2),n=s[1],a.label=5;case 5:return[2,n]}}))}))},e.prototype.mergePage=function(e,t,n){return Oe(this,void 0,void 0,(function(){return xe(this,(function(r){switch(r.label){case 0:return[4,e.batchSave(t,n,this.ownSymbol)];case 1:return[2,r.sent()]}}))}))},e}(),Pe=n(13),Ne=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},Re=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},Le=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},je=function(){function e(e,t,n,r){this.schema=e,this.namespaceResolver=t,this.MutationEvent=n,this.ownSymbol=r}return e.prototype.enqueue=function(e,t){return Ne(this,void 0,void 0,(function(){var n=this;return Re(this,(function(r){return e.runExclusive((function(e){return Ne(n,void 0,void 0,(function(){var n,r,i,o,s,a,u,c=this;return Re(this,(function(f){switch(f.label){case 0:return n=this.schema.namespaces[he.c].models.MutationEvent,r=ae.a.createFromExisting(n,(function(e){return e.modelId("eq",t.modelId).id("ne",c.inProgressMutationEventId)})),[4,e.query(this.MutationEvent,r)];case 1:return i=Le.apply(void 0,[f.sent(),1]),void 0!==(o=i[0])?[3,3]:[4,e.save(t,void 0,this.ownSymbol)];case 2:return f.sent(),[2];case 3:return s=t.operation,o.operation!==Pe.a.CREATE?[3,8]:s!==Pe.a.DELETE?[3,5]:[4,e.delete(this.MutationEvent,r)];case 4:return f.sent(),[3,7];case 5:return[4,e.save(this.MutationEvent.copyOf(o,(function(e){e.data=t.data})),void 0,this.ownSymbol)];case 6:f.sent(),f.label=7;case 7:return[3,12];case 8:return a=t.condition,u=JSON.parse(a),0!==Object.keys(u).length?[3,10]:[4,e.delete(this.MutationEvent,r)];case 9:f.sent(),f.label=10;case 10:return[4,e.save(t,void 0,this.ownSymbol)];case 11:f.sent(),f.label=12;case 12:return[2]}}))}))})),[2]}))}))},e.prototype.dequeue=function(e){return Ne(this,void 0,void 0,(function(){var t;return Re(this,(function(n){switch(n.label){case 0:return[4,this.peek(e)];case 1:return t=n.sent(),[4,e.delete(t)];case 2:return n.sent(),this.inProgressMutationEventId=void 0,[2,t]}}))}))},e.prototype.peek=function(e){return Ne(this,void 0,void 0,(function(){var t;return Re(this,(function(n){switch(n.label){case 0:return[4,e.queryOne(this.MutationEvent,de.d.FIRST)];case 1:return t=n.sent(),this.inProgressMutationEventId=t?t.id:void 0,[2,t]}}))}))},e.prototype.getForModel=function(e,t){return Ne(this,void 0,void 0,(function(){var n;return Re(this,(function(r){switch(r.label){case 0:return n=this.schema.namespaces[he.c].models.MutationEvent,[4,e.query(this.MutationEvent,ae.a.createFromExisting(n,(function(e){return e.modelId("eq",t.id)})))];case 1:return[2,r.sent()]}}))}))},e.prototype.getModelIds=function(e){return Ne(this,void 0,void 0,(function(){var t,n;return Re(this,(function(r){switch(r.label){case 0:return[4,e.query(this.MutationEvent)];case 1:return t=r.sent(),n=new Set,t.forEach((function(e){var t=e.modelId;return n.add(t)})),[2,n]}}))}))},e}(),De=n(52),Ue=n(514),Be=function(){return(Be=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},Fe=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},ze=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},qe=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},Ke=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},He=new r.a("DataStore"),Ve=function(){function e(e,t,n,r,i,o,s,a){this.schema=e,this.storage=t,this.userClasses=n,this.outbox=r,this.modelInstanceCreator=i,this.MutationEvent=o,this.conflictHandler=s,this.errorHandler=a,this.typeQuery=new WeakMap,this.processing=!1,this.generateQueries()}return e.prototype.generateQueries=function(){var e=this;Object.values(this.schema.namespaces).forEach((function(t){Object.values(t.models).filter((function(e){return e.syncable})).forEach((function(n){var r=Ke(Object(Pe.b)(t,n,"CREATE"),1)[0],i=Ke(Object(Pe.b)(t,n,"UPDATE"),1)[0],o=Ke(Object(Pe.b)(t,n,"DELETE"),1)[0];e.typeQuery.set(n,[r,i,o])}))}))},e.prototype.isReady=function(){return void 0!==this.observer},e.prototype.start=function(){var e=this;return new se.a((function(t){return e.observer=t,e.resume(),function(){e.pause()}}))},e.prototype.resume=function(){return Fe(this,void 0,void 0,(function(){var e,t,n,r,i,o,s,a,u,c,f,l,d,h,p;return ze(this,(function(v){switch(v.label){case 0:if(this.processing||!this.isReady())return[2];this.processing=!0,t=he.d,v.label=1;case 1:return(n=this.processing)?[4,this.outbox.peek(this.storage)]:[3,3];case 2:n=void 0!==(e=v.sent()),v.label=3;case 3:if(!n)return[3,12];r=e.model,i=e.operation,o=e.data,s=e.condition,a=this.userClasses[r],u=void 0,c=void 0,f=void 0,v.label=4;case 4:return v.trys.push([4,6,,7]),[4,this.jitteredRetry(t,r,i,o,s,a,this.MutationEvent,e)];case 5:return p=Ke.apply(void 0,[v.sent(),3]),u=p[0],c=p[1],f=p[2],[3,7];case 6:return"Offline"===(l=v.sent()).message||"RetryMutation"===l.message?[3,1]:[3,7];case 7:return void 0!==u?[3,9]:(He.debug("done retrying"),[4,this.outbox.dequeue(this.storage)]);case 8:return v.sent(),[3,1];case 9:return d=u.data[c],[4,this.outbox.dequeue(this.storage)];case 10:return v.sent(),[4,this.outbox.peek(this.storage)];case 11:return h=void 0!==v.sent(),this.observer.next({operation:i,modelDefinition:f,model:d,hasMore:h}),[3,1];case 12:return this.pause(),[2]}}))}))},e.prototype.jitteredRetry=function(e,t,n,r,i,o,s,a){return Fe(this,void 0,void 0,(function(){var u=this;return ze(this,(function(c){switch(c.label){case 0:return[4,Object(Ue.b)((function(t,n,r,i,o,s,a){return Fe(u,void 0,void 0,(function(){var u,c,f,l,d,h,p,v,g,m,b,y,w,_,S,E,M,A,I,k,O;return ze(this,(function(x){switch(x.label){case 0:u=Ke(this.createQueryVariables(e,t,n,r,i),5),c=u[0],f=u[1],l=u[2],d=u[3],h=u[4],p={query:c,variables:f},v=0,g=this.opTypeFromTransformerOperation(n),x.label=1;case 1:return x.trys.push([1,3,,13]),[4,De.a.graphql(p)];case 2:return[2,[x.sent(),d,h]];case 3:if(!((m=x.sent()).errors&&m.errors.length>0))return[3,12];if(b=Ke(m.errors,1),"Network Error"===(y=b[0]).message){if(!this.processing)throw new Ue.a("Offline");throw new Error("Network Error")}return"ConflictUnhandled"!==y.errorType?[3,11]:(v++,w=void 0,v>10?(w=de.a,[3,7]):[3,4]);case 4:return x.trys.push([4,6,,7]),[4,this.conflictHandler({modelConstructor:o,localModel:this.modelInstanceCreator(o,f.input),remoteModel:this.modelInstanceCreator(o,y.data),operation:g,attempts:v})];case 5:return w=x.sent(),[3,7];case 6:return _=x.sent(),He.warn("conflict trycatch",_),[3,13];case 7:return w!==de.a?[3,9]:(S=Ke(Object(Pe.b)(this.schema.namespaces[e],h,"GET"),1),E=Ke(S[0],3),M=E[1],A=E[2],[4,De.a.graphql({query:A,variables:{id:f.input.id}})]);case 8:return[2,[x.sent(),M,h]];case 9:return I=this.schema.namespaces[e],k=Object(Pe.d)(I.relationships,h,g,o,w,l,s,this.modelInstanceCreator,a.id),[4,this.storage.save(k)];case 10:throw x.sent(),new Ue.a("RetryMutation");case 11:try{this.errorHandler({localModel:this.modelInstanceCreator(o,f.input),message:y.message,operation:n,errorType:y.errorType,errorInfo:y.errorInfo,remoteModel:y.data?this.modelInstanceCreator(o,y.data):null})}catch(e){He.warn("failed to execute errorHandler",e)}finally{return[2,y.data?[{data:(O={},O[d]=y.data,O)},d,h]:[]]}x.label=12;case 12:return[3,13];case 13:if(p)return[3,1];x.label=14;case 14:return[2]}}))}))}),[t,n,r,i,o,s,a])];case 1:return[2,c.sent()]}}))}))},e.prototype.createQueryVariables=function(e,t,n,r,i){var o=this.schema.namespaces[e].models[t],s=this.typeQuery.get(o),a=Ke(s.find((function(e){return Ke(e,1)[0]===n})),3),u=a[1],c=a[2],f=JSON.parse(r),l=f._version,d=qe(f,["_version"]),h=n===Pe.a.DELETE?{id:d.id}:Object.values(o.fields).filter((function(e){var t=e.name,r=e.type,i=e.association;return Object(de.h)(r)?!(!Object(de.m)(i)||"BELONGS_TO"!==i.connectionType):n!==Pe.a.UPDATE||d.hasOwnProperty(t)})).map((function(e){var t=e.name,n=e.type,r=e.association,i=t,o=d[t];return Object(de.h)(n)&&Object(de.m)(r)&&(i=r.targetName,o=d[i]),[i,o]})).reduce((function(e,t){var n=Ke(t,2),r=n[0],i=n[1];return e[r]=i,e}),{}),p=Be(Be({},h),{_version:l}),v=JSON.parse(i);return[c,Be({input:p},n===Pe.a.CREATE?{}:{condition:Object.keys(v).length>0?v:null}),v,u,o]},e.prototype.opTypeFromTransformerOperation=function(e){switch(e){case Pe.a.CREATE:return de.c.INSERT;case Pe.a.DELETE:return de.c.DELETE;case Pe.a.UPDATE:return de.c.UPDATE;case Pe.a.GET:break;default:Object(he.f)(e)}},e.prototype.pause=function(){this.processing=!1},e}(),Ge=n(154),We=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},$e=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},Ye=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},Je=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Ze=new r.a("DataStore"),Xe=function(){function e(e,t,n,r){void 0===t&&(t=1e4),void 0===n&&(n=1e3),this.schema=e,this.maxRecordsToSync=t,this.syncPageSize=n,this.syncPredicates=r,this.typeQuery=new WeakMap,this.generateQueries()}return e.prototype.generateQueries=function(){var e=this;Object.values(this.schema.namespaces).forEach((function(t){Object.values(t.models).filter((function(e){return e.syncable})).forEach((function(n){var r=Ye(Object(Pe.b)(t,n,"LIST"),1),i=Ye(r[0]).slice(1);e.typeQuery.set(n,i)}))}))},e.prototype.graphqlFilterFromPredicate=function(e){if(!this.syncPredicates)return null;var t=ae.a.getPredicates(this.syncPredicates.get(e),!1);return t?Object(Pe.h)(t):null},e.prototype.retrievePage=function(e,t,n,r,i){return void 0===r&&(r=null),We(this,void 0,void 0,(function(){var o,s,a,u,c,f,l,d,h;return $e(this,(function(p){switch(p.label){case 0:return o=Ye(this.typeQuery.get(e),2),s=o[0],a=o[1],u={limit:r,nextToken:n,lastSync:t,filter:i},[4,this.jitteredRetry(a,u,s)];case 1:return c=p.sent().data,f=c[s],l=f.items,d=f.nextToken,h=f.startedAt,[2,{nextToken:d,startedAt:h,items:l}]}}))}))},e.prototype.jitteredRetry=function(e,t,n){return We(this,void 0,void 0,(function(){var r=this;return $e(this,(function(i){switch(i.label){case 0:return[4,Object(Ue.b)((function(e,t){return We(r,void 0,void 0,(function(){var r,i;return $e(this,(function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),[4,De.a.graphql({query:e,variables:t})];case 1:return[2,o.sent()];case 2:if(r=o.sent(),r.errors.some((function(e){return"Unauthorized"===e.errorType})))return(i=r).data[n].items=i.data[n].items.filter((function(e){return null!==e})),Ze.warn("queryError","User is unauthorized, some items could not be returned."),[2,i];throw r;case 3:return[2]}}))}))}),[e,t])];case 1:return[2,i.sent()]}}))}))},e.prototype.start=function(e){var t=this,n=!0,r=void 0!==this.maxRecordsToSync?this.maxRecordsToSync:1e4,i=void 0!==this.syncPageSize?this.syncPageSize:1e3,o=new Map;return new se.a((function(s){var a=Object.values(t.schema.namespaces).reduce((function(t,n){var r,i;try{for(var o=Je(Array.from(n.modelTopologicalOrdering.keys())),s=o.next();!s.done;s=o.next()){var a=s.value,u=e.get(n.models[a]);t.set(n.models[a],u)}}catch(e){r={error:e}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(r)throw r.error}}return t}),new Map),u=Array.from(a.entries()).filter((function(e){return Ye(e,1)[0].syncable})).map((function(e){var a=Ye(e,2),u=a[0],c=Ye(a[1],2),f=c[0],l=c[1];return We(t,void 0,void 0,(function(){var e,t,a,c,d,h,p,v,g,m=this;return $e(this,(function(b){switch(b.label){case 0:return e=!1,t=null,a=null,c=null,d=0,h=this.graphqlFilterFromPredicate(u),p=this.schema.namespaces[f].modelTopologicalOrdering.get(u.name),v=p.map((function(e){return o.get(f+"_"+e)})),g=new Promise((function(o){return We(m,void 0,void 0,(function(){var p,g;return $e(this,(function(m){switch(m.label){case 0:return[4,Promise.all(v)];case 1:m.sent(),m.label=2;case 2:return n?(p=Math.min(r-d,i),[4,this.retrievePage(u,l,t,p,h)]):[2];case 3:g=m.sent(),c=g.items,t=g.nextToken,a=g.startedAt,d+=c.length,e=null===t||d>=r,s.next({namespace:f,modelDefinition:u,items:c,done:e,startedAt:a,isFullSync:!l}),m.label=4;case 4:if(!e)return[3,2];m.label=5;case 5:return o(),[2]}}))}))})),o.set(f+"_"+u.name,g),[4,g];case 1:return b.sent(),[2]}}))}))}));return Promise.all(u).then((function(){s.complete()})),function(){n=!1}}))},e}(),Qe=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},et=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},tt=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},nt=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},rt=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(tt(arguments[t]));return e},it=Object(i.b)().isNode,ot=new r.a("DataStore"),st=Symbol("sync");!function(e){e.SYNC_ENGINE_STORAGE_SUBSCRIBED="storageSubscribed",e.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED="subscriptionsEstablished",e.SYNC_ENGINE_SYNC_QUERIES_STARTED="syncQueriesStarted",e.SYNC_ENGINE_SYNC_QUERIES_READY="syncQueriesReady",e.SYNC_ENGINE_MODEL_SYNCED="modelSynced",e.SYNC_ENGINE_OUTBOX_MUTATION_ENQUEUED="outboxMutationEnqueued",e.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED="outboxMutationProcessed",e.SYNC_ENGINE_OUTBOX_STATUS="outboxStatus",e.SYNC_ENGINE_NETWORK_STATUS="networkStatus",e.SYNC_ENGINE_READY="ready"}(re||(re={}));var at=function(){function e(e,t,n,r,i,o,s,a,u,c,f,l){void 0===l&&(l={}),this.schema=e,this.namespaceResolver=t,this.modelClasses=n,this.userModelClasses=r,this.storage=i,this.modelInstanceCreator=o,this.maxRecordsToSync=s,this.syncPageSize=a,this.syncPredicates=f,this.amplifyConfig=l,this.online=!1;var d=this.modelClasses.MutationEvent;this.outbox=new je(this.schema,this.namespaceResolver,d,st),this.modelMerger=new Te(this.outbox,st),this.syncQueriesProcessor=new Xe(this.schema,this.maxRecordsToSync,this.syncPageSize,this.syncPredicates),this.subscriptionsProcessor=new Ge.b(this.schema,this.syncPredicates,this.amplifyConfig),this.mutationsProcessor=new Ve(this.schema,this.storage,this.userModelClasses,this.outbox,this.modelInstanceCreator,d,u,c),this.datastoreConnectivity=new ke}return e.prototype.start=function(e){var t=this;return new se.a((function(n){ot.log("starting sync engine...");var r=[];return Qe(t,void 0,void 0,(function(){var t,i,o,s=this;return et(this,(function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),[4,this.setupModels(e)];case 1:return a.sent(),[3,3];case 2:return t=a.sent(),n.error(t),[2];case 3:return i=new Promise((function(e){s.datastoreConnectivity.status().subscribe((function(t){var i=t.online;return Qe(s,void 0,void 0,(function(){var t,o,s,a,u,c=this;return et(this,(function(f){switch(f.label){case 0:return!i||this.online?[3,10]:(this.online=i,n.next({type:re.SYNC_ENGINE_NETWORK_STATUS,data:{active:this.online}}),o=void 0,it?(ot.warn("Realtime disabled when in a server-side environment"),[3,6]):[3,1]);case 1:u=tt(this.subscriptionsProcessor.start(),2),t=u[0],o=u[1],f.label=2;case 2:return f.trys.push([2,4,,5]),[4,new Promise((function(e,n){var i=t.subscribe({next:function(t){t===Ge.a.CONNECTED&&e()},error:function(e){n(e),c.disconnectionHandler()(e)}});r.push(i)}))];case 3:return f.sent(),[3,5];case 4:return s=f.sent(),n.error(s),[2];case 5:ot.log("Realtime ready"),n.next({type:re.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED}),f.label=6;case 6:return f.trys.push([6,8,,9]),[4,new Promise((function(e,t){var i=c.syncQueriesObservable().subscribe({next:function(t){t.type===re.SYNC_ENGINE_SYNC_QUERIES_READY&&e(),n.next(t)},complete:function(){e()},error:function(e){t(e)}});i&&r.push(i)}))];case 7:return f.sent(),[3,9];case 8:return a=f.sent(),n.error(a),[2];case 9:return r.push(this.mutationsProcessor.start().subscribe((function(e){var t=e.modelDefinition,r=e.model,i=e.hasMore,o=c.userModelClasses[t.name],s=c.modelInstanceCreator(o,r);c.storage.runExclusive((function(e){return c.modelMerger.merge(e,s)})),n.next({type:re.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED,data:{model:o,element:s}}),n.next({type:re.SYNC_ENGINE_OUTBOX_STATUS,data:{isEmpty:!i}})}))),it||r.push(o.subscribe((function(e){var t=tt(e,3),n=(t[0],t[1]),r=t[2],i=c.userModelClasses[n.name],o=c.modelInstanceCreator(i,r);c.storage.runExclusive((function(e){return c.modelMerger.merge(e,o)}))}))),[3,11];case 10:i||(this.online=i,n.next({type:re.SYNC_ENGINE_NETWORK_STATUS,data:{active:this.online}}),r.forEach((function(e){return e.unsubscribe()})),r=[]),f.label=11;case 11:return e(),[2]}}))}))}))})),this.storage.observe(null,null,st).filter((function(e){var t=e.model;return!0===s.getModelDefinition(t).syncable})).subscribe({next:function(e){var t=e.opType,r=e.model,o=e.element,a=e.condition;return Qe(s,void 0,void 0,(function(){var e,s,u,c;return et(this,(function(f){switch(f.label){case 0:return e=this.schema.namespaces[this.namespaceResolver(r)],s=this.modelClasses.MutationEvent,u=Object(Pe.g)(a),c=Object(Pe.d)(e.relationships,this.getModelDefinition(r),t,r,o,u,s,this.modelInstanceCreator),[4,this.outbox.enqueue(this.storage,c)];case 1:return f.sent(),n.next({type:re.SYNC_ENGINE_OUTBOX_MUTATION_ENQUEUED,data:{model:r,element:o}}),n.next({type:re.SYNC_ENGINE_OUTBOX_STATUS,data:{isEmpty:!1}}),[4,i];case 2:return f.sent(),this.online&&this.mutationsProcessor.resume(),[2]}}))}))}}),n.next({type:re.SYNC_ENGINE_STORAGE_SUBSCRIBED}),[4,this.outbox.peek(this.storage)];case 4:return o=void 0===a.sent(),n.next({type:re.SYNC_ENGINE_OUTBOX_STATUS,data:{isEmpty:o}}),[4,i];case 5:return a.sent(),n.next({type:re.SYNC_ENGINE_READY}),[2]}}))})),function(){r.forEach((function(e){return e.unsubscribe()}))}}))},e.prototype.getModelsMetadataWithNextFullSync=function(e){return Qe(this,void 0,void 0,(function(){var t,n=this;return et(this,(function(r){switch(r.label){case 0:return t=Map.bind,[4,this.getModelsMetadata()];case 1:return[2,new(t.apply(Map,[void 0,r.sent().map((function(t){var r=t.namespace,i=t.model,o=t.lastSync,s=t.lastFullSync,a=t.fullSyncInterval,u=(t.lastSyncPredicate,!s||s+a<e?0:o);return[n.schema.namespaces[r].models[i],[r,u]]}))]))]}}))}))},e.prototype.syncQueriesObservable=function(){var e=this;return this.online?new se.a((function(t){var n,r;return Qe(e,void 0,void 0,(function(){var e,i,o=this;return et(this,(function(s){switch(s.label){case 0:e=function(){var e,s,a,u,c,f,l,d,h;return et(this,(function(p){switch(p.label){case 0:return e=new WeakMap,[4,i.getModelsMetadataWithNextFullSync(Date.now())];case 1:return s=p.sent(),a=new Set(s.keys()),[4,new Promise((function(r){n=o.syncQueriesProcessor.start(s).subscribe({next:function(i){var s=i.namespace,h=i.modelDefinition,p=i.items,v=i.done,g=i.startedAt,m=i.isFullSync;return Qe(o,void 0,void 0,(function(){var i,o,b,y,w,_,S=this;return et(this,(function(E){switch(E.label){case 0:return i=this.userModelClasses[h.name],e.has(i)||(e.set(i,{new:0,updated:0,deleted:0}),f=Object(he.i)(),d=void 0===d?g:Math.max(d,g)),[4,this.storage.runExclusive((function(t){return Qe(S,void 0,void 0,(function(){var n,r,o,s,a,u,c,f,l,d,h,v,g,m,b;return et(this,(function(y){switch(y.label){case 0:return[4,this.outbox.getModelIds(t)];case 1:n=y.sent(),r=[],o=p.filter((function(e){return!n.has(e.id)||(r.push(e),!1)})),s=[],y.label=2;case 2:y.trys.push([2,7,8,9]),a=nt(r),u=a.next(),y.label=3;case 3:return u.done?[3,6]:(c=u.value,[4,this.modelMerger.merge(t,c)]);case 4:void 0!==(f=y.sent())&&s.push([c,f]),y.label=5;case 5:return u=a.next(),[3,3];case 6:return[3,9];case 7:return l=y.sent(),m={error:l},[3,9];case 8:try{u&&!u.done&&(b=a.return)&&b.call(a)}finally{if(m)throw m.error}return[7];case 9:return h=(d=s.push).apply,v=[s],[4,this.modelMerger.mergePage(t,i,o)];case 10:return h.apply(d,v.concat([rt.apply(void 0,[y.sent()])])),g=e.get(i),s.forEach((function(e){var t=tt(e,2)[1];switch(t){case de.c.INSERT:g.new++;break;case de.c.UPDATE:g.updated++;break;case de.c.DELETE:g.deleted++;break;default:Object(he.f)(t)}})),[2]}}))}))}))];case 1:return E.sent(),v?(o=h.name,[4,this.getModelMetadata(s,o)]):[3,4];case 2:return b=E.sent(),y=b.lastFullSync,w=b.fullSyncInterval,c=w,u=void 0===u?y:Math.max(u,m?g:y),b=this.modelClasses.ModelMetadata.copyOf(b,(function(e){e.lastSync=g,e.lastFullSync=m?g:b.lastFullSync})),[4,this.storage.save(b,void 0,st)];case 3:E.sent(),_=e.get(i),t.next({type:re.SYNC_ENGINE_MODEL_SYNCED,data:{model:i,isFullSync:m,isDeltaSync:!m,counts:_}}),a.delete(h),0===a.size&&(l=Object(he.i)()-f,r(),t.next({type:re.SYNC_ENGINE_SYNC_QUERIES_READY}),n.unsubscribe()),E.label=4;case 4:return[2]}}))}))},error:function(e){t.error(e)}}),t.next({type:re.SYNC_ENGINE_SYNC_QUERIES_STARTED,data:{models:Array.from(a).map((function(e){return e.name}))}})}))];case 2:return p.sent(),h=u+c-(d+l),ot.debug("Next fullSync in "+h/1e3+" seconds. ("+new Date(Date.now()+h)+")"),[4,new Promise((function(e){r=setTimeout(e,h)}))];case 3:return p.sent(),[2]}}))},i=this,s.label=1;case 1:return t.closed?[3,3]:[5,e()];case 2:return s.sent(),[3,1];case 3:return[2]}}))})),function(){n&&n.unsubscribe(),r&&clearTimeout(r)}})):se.a.of()},e.prototype.disconnectionHandler=function(){var e=this;return function(t){Ee.a.CONNECTION_CLOSED!==t&&Ee.a.TIMEOUT_DISCONNECT!==t||e.datastoreConnectivity.socketDisconnected()}},e.prototype.unsubscribeConnectivity=function(){this.datastoreConnectivity.unsubscribe()},e.prototype.setupModels=function(e){return Qe(this,void 0,void 0,(function(){var t,n,r,i,o,s,a,u,c,f,l,d,h,p=this;return et(this,(function(v){switch(v.label){case 0:t=e.fullSyncInterval,n=this.modelClasses.ModelMetadata,r=[],Object.values(this.schema.namespaces).forEach((function(e){Object.values(e.models).filter((function(e){return e.syncable})).forEach((function(t){r.push([e.name,t])}))})),o=r.map((function(e){var r=tt(e,2),o=r[0],s=r[1];return Qe(p,void 0,void 0,(function(){var e,r,a,u,c,f,l,d,h;return et(this,(function(p){switch(p.label){case 0:return[4,this.getModelMetadata(o,s.name)];case 1:return e=p.sent(),r=ae.a.getPredicates(this.syncPredicates.get(s),!1),a=r?JSON.stringify(r):null,void 0!==e?[3,3]:[4,this.storage.save(this.modelInstanceCreator(n,{model:s.name,namespace:o,lastSync:null,fullSyncInterval:t,lastFullSync:null,lastSyncPredicate:a}),void 0,st)];case 2:return f=tt.apply(void 0,[p.sent(),1]),l=tt(f[0],1),i=l[0],[3,5];case 3:return u=e.lastSyncPredicate?e.lastSyncPredicate:null,c=u!==a,[4,this.storage.save(this.modelClasses.ModelMetadata.copyOf(e,(function(e){e.fullSyncInterval=t,c&&(e.lastSync=null,e.lastFullSync=null,e.lastSyncPredicate=a)})))];case 4:d=tt.apply(void 0,[p.sent(),1]),h=tt(d[0],1),i=h[0],p.label=5;case 5:return[2,i]}}))}))})),s={},v.label=1;case 1:return v.trys.push([1,6,7,8]),[4,Promise.all(o)];case 2:a=nt.apply(void 0,[v.sent()]),u=a.next(),v.label=3;case 3:if(u.done)return[3,5];c=u.value,f=c.model,s[f]=c,v.label=4;case 4:return u=a.next(),[3,3];case 5:return[3,8];case 6:return l=v.sent(),d={error:l},[3,8];case 7:try{u&&!u.done&&(h=a.return)&&h.call(a)}finally{if(d)throw d.error}return[7];case 8:return[2,s]}}))}))},e.prototype.getModelsMetadata=function(){return Qe(this,void 0,void 0,(function(){var e;return et(this,(function(t){switch(t.label){case 0:return e=this.modelClasses.ModelMetadata,[4,this.storage.query(e)];case 1:return[2,t.sent()]}}))}))},e.prototype.getModelMetadata=function(e,t){return Qe(this,void 0,void 0,(function(){var n,r,i;return et(this,(function(o){switch(o.label){case 0:return n=this.modelClasses.ModelMetadata,r=ae.a.createFromExisting(this.schema.namespaces[he.c].models[n.name],(function(n){return n.namespace("eq",e).model("eq",t)})),[4,this.storage.query(n,r,{page:0,limit:1})];case 1:return i=tt.apply(void 0,[o.sent(),1]),[2,i[0]]}}))}))},e.prototype.getModelDefinition=function(e){var t=this.namespaceResolver(e);return this.schema.namespaces[t].models[e.name]},e.getNamespace=function(){return{name:he.c,relationships:{},enums:{OperationType:{name:"OperationType",values:["CREATE","UPDATE","DELETE"]}},nonModels:{},models:{MutationEvent:{name:"MutationEvent",pluralName:"MutationEvents",syncable:!1,fields:{id:{name:"id",type:"ID",isRequired:!0,isArray:!1},model:{name:"model",type:"String",isRequired:!0,isArray:!1},data:{name:"data",type:"String",isRequired:!0,isArray:!1},modelId:{name:"modelId",type:"String",isRequired:!0,isArray:!1},operation:{name:"operation",type:{enum:"Operationtype"},isArray:!1,isRequired:!0},condition:{name:"condition",type:"String",isArray:!1,isRequired:!0}}},ModelMetadata:{name:"ModelMetadata",pluralName:"ModelsMetadata",syncable:!1,fields:{id:{name:"id",type:"ID",isRequired:!0,isArray:!1},namespace:{name:"namespace",type:"String",isRequired:!0,isArray:!1},model:{name:"model",type:"String",isRequired:!0,isArray:!1},lastSync:{name:"lastSync",type:"Int",isRequired:!1,isArray:!1},lastFullSync:{name:"lastFullSync",type:"Int",isRequired:!1,isArray:!1},fullSyncInterval:{name:"fullSyncInterval",type:"Int",isRequired:!0,isArray:!1}}}}}},e}();var ut=function(){return(ut=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},ct=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},ft=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},lt=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i<r.length;i++)t.indexOf(r[i])<0&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]])}return n},dt=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s};oe(!0),q();var ht,pt,vt,gt,mt,bt=new r.a("DataStore"),yt=(Object(he.v)(Date.now()),i.a.browserOrNode().isNode),wt=new WeakMap,_t=new WeakMap,St=function(e){var t=wt.get(e);return ht.namespaces[t].models[e.name]},Et=function(e){return Object(he.s)(e)&&wt.has(e)},Mt=function(e){return wt.get(e)},At=new WeakSet;function It(e,t){return At.add(t),new e(t)}var kt;function Ot(e){return"string"==typeof e}function xt(e){var t=e.localModel,n=e.modelConstructor,r=e.remoteModel._version;return It(n,ut(ut({},t),{_version:r}))}function Ct(e){bt.warn(e)}function Tt(e,t){var n;switch(e){case he.a:n=pt[t];break;case he.d:n=vt[t];break;case he.c:n=gt[t];break;case he.b:n=mt[t];break;default:Object(he.f)(e)}if(Et(n))return n;var r="Model name is not valid for namespace. modelName: "+t+", namespace: "+e;throw bt.error(r),new Error(r)}function Pt(e,t){return ct(this,void 0,void 0,(function(){var n,r,i=this;return ft(this,(function(o){switch(o.label){case 0:return n=pt.Setting,r=ht.namespaces[he.a].models.Setting,[4,e.runExclusive((function(e){return ct(i,void 0,void 0,(function(){var i,o;return ft(this,(function(s){switch(s.label){case 0:return[4,e.query(n,ae.a.createFromExisting(r,(function(e){return e.key("eq","schemaVersion")})),{page:0,limit:1})];case 1:return i=dt.apply(void 0,[s.sent(),1]),void 0===(o=i[0])||void 0===o.value?[3,4]:JSON.parse(o.value)===t?[3,3]:[4,e.clear(!1)];case 2:s.sent(),s.label=3;case 3:return[3,6];case 4:return[4,e.save(It(n,{key:"schemaVersion",value:JSON.stringify(t)}))];case 5:s.sent(),s.label=6;case 6:return[2]}}))}))}))];case 1:return o.sent(),[2]}}))}))}var Nt=new(function(){function e(){var e=this;this.amplifyConfig={},this.syncPredicates=new WeakMap,this.start=function(){return ct(e,void 0,void 0,(function(){var e,t,n,r=this;return ft(this,(function(i){switch(i.label){case 0:return void 0!==this.initialized?[3,1]:(bt.debug("Starting DataStore"),this.initialized=new Promise((function(e,t){r.initResolve=e,r.initReject=t})),[3,3]);case 1:return[4,this.initialized];case 2:return i.sent(),[2];case 3:return this.storage=new Se(ht,Mt,Tt,It,void 0,this.sessionId),[4,this.storage.init()];case 4:return i.sent(),[4,Pt(this.storage,ht.version)];case 5:return i.sent(),(e=this.amplifyConfig.aws_appsync_graphqlEndpoint)?(bt.debug("GraphQL endpoint available",e),t=this,[4,this.processSyncExpressions()]):[3,7];case 6:return t.syncPredicates=i.sent(),this.sync=new at(ht,Mt,gt,vt,this.storage,It,this.maxRecordsToSync,this.syncPageSize,this.conflictHandler,this.errorHandler,this.syncPredicates,this.amplifyConfig),n=1e3*this.fullSyncInterval*60,kt=this.sync.start({fullSyncInterval:n}).subscribe({next:function(e){var t=e.type,n=e.data;t===(yt?re.SYNC_ENGINE_SYNC_QUERIES_READY:re.SYNC_ENGINE_STORAGE_SUBSCRIBED)&&r.initResolve(),o.a.dispatch("datastore",{event:t,data:n})},error:function(e){bt.warn("Sync error",e),r.initReject()}}),[3,8];case 7:bt.warn("Data won't be synchronized. No GraphQL endpoint configured. Did you forget `Amplify.configure(awsconfig)`?",{config:this.amplifyConfig}),this.initResolve(),i.label=8;case 8:return[4,this.initialized];case 9:return i.sent(),[2]}}))}))},this.query=function(t,n,r){return ct(e,void 0,void 0,(function(){var e,i,o,s,a;return ft(this,(function(u){switch(u.label){case 0:return[4,this.start()];case 1:if(u.sent(),!Et(t))throw e="Constructor is not for a valid model",bt.error(e,{modelConstructor:t}),new Error(e);return"string"==typeof n&&void 0!==r&&bt.warn("Pagination is ignored when querying by id"),i=St(t),o=Ot(n)?ae.a.createForId(i,n):Object(ae.c)(n)?void 0:ae.a.createFromExisting(i,n),s=this.processPagination(i,r),bt.debug("params ready",{modelConstructor:t,predicate:ae.a.getPredicates(o,!1),pagination:ut(ut({},s),{sort:ue.a.getPredicates(s.sort,!1)})}),[4,this.storage.query(t,o,s)];case 2:return a=u.sent(),[2,Ot(n)?a[0]:a]}}))}))},this.save=function(t,n){return ct(e,void 0,void 0,(function(){var e,r,i,o,s,a,u=this;return ft(this,(function(c){switch(c.label){case 0:return[4,this.start()];case 1:if(c.sent(),e=_t.get(t),r=t?t.constructor:void 0,!Et(r))throw i="Object is not an instance of a valid model",bt.error(i,{model:t}),new Error(i);return o=St(r),s=ae.a.createFromExisting(o,n),[4,this.storage.runExclusive((function(n){return ct(u,void 0,void 0,(function(){return ft(this,(function(i){switch(i.label){case 0:return[4,n.save(t,s,void 0,e)];case 1:return i.sent(),[2,n.query(r,ae.a.createForId(o,t.id))]}}))}))}))];case 2:return a=dt.apply(void 0,[c.sent(),1]),[2,a[0]]}}))}))},this.setConflictHandler=function(t){var n=t.DataStore;return n?n.conflictHandler:e.conflictHandler===xt&&t.conflictHandler?t.conflictHandler:e.conflictHandler||xt},this.setErrorHandler=function(t){var n=t.DataStore;return n?n.errorHandler:e.errorHandler===Ct&&t.errorHandler?t.errorHandler:e.errorHandler||Ct},this.delete=function(t,n){return ct(e,void 0,void 0,(function(){var e,r,i,o,s,a,u,c,f;return ft(this,(function(l){switch(l.label){case 0:return[4,this.start()];case 1:if(l.sent(),!t)throw u="Model or Model Constructor required",bt.error(u,{modelOrConstructor:t}),new Error(u);if(!Et(t))return[3,3];if(o=t,!n)throw u="Id to delete or criteria required. Do you want to delete all? Pass Predicates.ALL",bt.error(u,{idOrCriteria:n}),new Error(u);if("string"==typeof n)e=ae.a.createForId(St(o),n);else if(!(e=ae.a.createFromExisting(St(o),n))||!ae.a.isValidPredicate(e))throw u="Criteria required. Do you want to delete all? Pass Predicates.ALL",bt.error(u,{condition:e}),new Error(u);return[4,this.storage.delete(o,e)];case 2:return r=dt.apply(void 0,[l.sent(),1]),[2,r[0]];case 3:if(i=t,o=Object.getPrototypeOf(i||{}).constructor,!Et(o))throw u="Object is not an instance of a valid model",bt.error(u,{model:i}),new Error(u);if(s=St(o),a=ae.a.createForId(s,i.id),n){if("function"!=typeof n)throw u="Invalid criteria",bt.error(u,{idOrCriteria:n}),new Error(u);e=n(a)}else e=a;return[4,this.storage.delete(i,e)];case 4:return c=dt.apply(void 0,[l.sent(),1]),f=dt(c[0],1),[2,f[0]]}}))}))},this.observe=function(t,n){var r,i=t&&Et(t)?t:void 0;if(t&&void 0===i){var o=t,s=o&&Object.getPrototypeOf(o).constructor;if(Et(s))return n&&bt.warn("idOrCriteria is ignored when using a model instance",{model:o,idOrCriteria:n}),e.observe(s,o.id);var a="The model is not an instance of a PersistentModelConstructor";throw bt.error(a,{model:o}),new Error(a)}if(void 0!==n&&void 0===i){a="Cannot provide criteria without a modelConstructor";throw bt.error(a,n),new Error(a)}if(i&&!Et(i)){a="Constructor is not for a valid model";throw bt.error(a,{modelConstructor:i}),new Error(a)}return r="string"==typeof n?ae.a.createForId(St(i),n):i&&ae.a.createFromExisting(St(i),n),new se.a((function(t){var n;return ct(e,void 0,void 0,(function(){return ft(this,(function(e){switch(e.label){case 0:return[4,this.start()];case 1:return e.sent(),n=this.storage.observe(i,r).filter((function(e){var t=e.model;return Mt(t)===he.d})).subscribe(t),[2]}}))})),function(){n&&n.unsubscribe()}}))},this.configure=function(t){void 0===t&&(t={});var n=t.DataStore,r=(t.conflictHandler,t.errorHandler,t.maxRecordsToSync),i=t.syncPageSize,o=t.fullSyncInterval,s=t.syncExpressions,a=lt(t,["DataStore","conflictHandler","errorHandler","maxRecordsToSync","syncPageSize","fullSyncInterval","syncExpressions"]);e.amplifyConfig=ut(ut({},a),e.amplifyConfig),e.conflictHandler=e.setConflictHandler(t),e.errorHandler=e.setErrorHandler(t),e.syncExpressions=n&&n.syncExpressions||e.syncExpressions||s,e.maxRecordsToSync=n&&n.maxRecordsToSync||e.maxRecordsToSync||r,e.syncPageSize=n&&n.syncPageSize||e.syncPageSize||i,e.fullSyncInterval=n&&n.fullSyncInterval||e.fullSyncInterval||o||1440,e.sessionId=e.retrieveSessionId()},this.clear=function(){return ct(this,void 0,void 0,(function(){return ft(this,(function(e){switch(e.label){case 0:return void 0===this.storage?[2]:(kt&&!kt.closed&&kt.unsubscribe(),[4,this.storage.clear()]);case 1:return e.sent(),this.sync&&this.sync.unsubscribeConnectivity(),this.initialized=void 0,this.storage=void 0,this.sync=void 0,this.syncPredicates=new WeakMap,[2]}}))}))},this.stop=function(){return ct(this,void 0,void 0,(function(){return ft(this,(function(e){switch(e.label){case 0:return void 0===this.initialized?[3,2]:[4,this.start()];case 1:e.sent(),e.label=2;case 2:return kt&&!kt.closed&&kt.unsubscribe(),this.sync&&this.sync.unsubscribeConnectivity(),this.initialized=void 0,this.sync=void 0,[2]}}))}))}}return e.prototype.getModuleName=function(){return"DataStore"},e.prototype.processPagination=function(e,t){var n,r=t||{},i=r.limit,o=r.page,s=r.sort;if(void 0!==o&&void 0===i)throw new Error("Limit is required when requesting a page");if(void 0!==o){if("number"!=typeof o)throw new Error("Page should be a number");if(o<0)throw new Error("Page can't be negative")}if(void 0!==i){if("number"!=typeof i)throw new Error("Limit should be a number");if(i<0)throw new Error("Limit can't be negative")}return s&&(n=ue.a.createFromExisting(e,t.sort)),{limit:i,page:o,sort:n}},e.prototype.processSyncExpressions=function(){return ct(this,void 0,void 0,(function(){var e,t=this;return ft(this,(function(n){switch(n.label){case 0:return this.syncExpressions&&this.syncExpressions.length?[4,Promise.all(this.syncExpressions.map((function(e){return ct(t,void 0,void 0,(function(){var t,n,r,i,o,s;return ft(this,(function(a){switch(a.label){case 0:return[4,e];case 1:return t=a.sent(),n=t.modelConstructor,r=t.conditionProducer,i=St(n),[4,this.unwrapPromise(r)];case 2:return o=a.sent(),Object(ae.c)(o)?[2,[i,null]]:(s=this.createFromCondition(i,o),[2,[i,s]])}}))}))})))]:[2,new WeakMap];case 1:return e=n.sent(),[2,this.weakMapFromEntries(e)]}}))}))},e.prototype.createFromCondition=function(e,t){try{return ae.a.createFromExisting(e,t)}catch(e){throw bt.error("Error creating Sync Predicate"),e}},e.prototype.unwrapPromise=function(e){return ct(this,void 0,void 0,(function(){var t;return ft(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,e()];case 1:return[2,n.sent()];case 2:if((t=n.sent())instanceof TypeError)return[2,e];throw t;case 3:return[2]}}))}))},e.prototype.weakMapFromEntries=function(e){return e.reduce((function(e,t){var n=dt(t,2),r=n[0],i=n[1];if(e.has(r)){var o=r.name;return bt.warn("You can only utilize one Sync Expression per model.\n Subsequent sync expressions for the "+o+" model will be ignored."),e}return i&&e.set(r,i),e}),new WeakMap)},e.prototype.retrieveSessionId=function(){try{var e=sessionStorage.getItem("datastoreSessionId");if(e){var t=this.amplifyConfig.aws_appsync_graphqlEndpoint.split("/")[2];return e+"-"+dt(t.split("."),1)[0]}}catch(e){return}},e}());s.a.register(Nt)},,,,,,,,,,,function(e,t,n){"use strict";t.byteLength=function(e){var t=c(e),n=t[0],r=t[1];return 3*(n+r)/4-r},t.toByteArray=function(e){var t,n,r=c(e),s=r[0],a=r[1],u=new o(function(e,t,n){return 3*(t+n)/4-n}(0,s,a)),f=0,l=a>0?s-4:s;for(n=0;n<l;n+=4)t=i[e.charCodeAt(n)]<<18|i[e.charCodeAt(n+1)]<<12|i[e.charCodeAt(n+2)]<<6|i[e.charCodeAt(n+3)],u[f++]=t>>16&255,u[f++]=t>>8&255,u[f++]=255&t;2===a&&(t=i[e.charCodeAt(n)]<<2|i[e.charCodeAt(n+1)]>>4,u[f++]=255&t);1===a&&(t=i[e.charCodeAt(n)]<<10|i[e.charCodeAt(n+1)]<<4|i[e.charCodeAt(n+2)]>>2,u[f++]=t>>8&255,u[f++]=255&t);return u},t.fromByteArray=function(e){for(var t,n=e.length,i=n%3,o=[],s=0,a=n-i;s<a;s+=16383)o.push(f(e,s,s+16383>a?a:s+16383));1===i?(t=e[n-1],o.push(r[t>>2]+r[t<<4&63]+"==")):2===i&&(t=(e[n-2]<<8)+e[n-1],o.push(r[t>>10]+r[t>>4&63]+r[t<<2&63]+"="));return o.join("")};for(var r=[],i=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,u=s.length;a<u;++a)r[a]=s[a],i[s.charCodeAt(a)]=a;function c(e){var t=e.length;if(t%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function f(e,t,n){for(var i,o,s=[],a=t;a<n;a+=3)i=(e[a]<<16&16711680)+(e[a+1]<<8&65280)+(255&e[a+2]),s.push(r[(o=i)>>18&63]+r[o>>12&63]+r[o>>6&63]+r[63&o]);return s.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},function(e,t){ -/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */ -t.read=function(e,t,n,r,i){var o,s,a=8*i-r-1,u=(1<<a)-1,c=u>>1,f=-7,l=n?i-1:0,d=n?-1:1,h=e[t+l];for(l+=d,o=h&(1<<-f)-1,h>>=-f,f+=a;f>0;o=256*o+e[t+l],l+=d,f-=8);for(s=o&(1<<-f)-1,o>>=-f,f+=r;f>0;s=256*s+e[t+l],l+=d,f-=8);if(0===o)o=1-c;else{if(o===u)return s?NaN:1/0*(h?-1:1);s+=Math.pow(2,r),o-=c}return(h?-1:1)*s*Math.pow(2,o-r)},t.write=function(e,t,n,r,i,o){var s,a,u,c=8*o-i-1,f=(1<<c)-1,l=f>>1,d=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,h=r?0:o-1,p=r?1:-1,v=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,s=f):(s=Math.floor(Math.log(t)/Math.LN2),t*(u=Math.pow(2,-s))<1&&(s--,u*=2),(t+=s+l>=1?d/u:d*Math.pow(2,1-l))*u>=2&&(s++,u/=2),s+l>=f?(a=0,s=f):s+l>=1?(a=(t*u-1)*Math.pow(2,i),s+=l):(a=t*Math.pow(2,l-1)*Math.pow(2,i),s=0));i>=8;e[n+h]=255&a,h+=p,a/=256,i-=8);for(s=s<<i|a,c+=i;c>0;e[n+h]=255&s,h+=p,s/=256,c-=8);e[n+h-p]|=128*v}},function(e,t,n){var r,i,o,s;e.exports=(r=n(32),o=(i=r).lib.Base,s=i.enc.Utf8,void(i.algo.HMAC=o.extend({init:function(e,t){e=this._hasher=new e.init,"string"==typeof t&&(t=s.parse(t));var n=e.blockSize,r=4*n;t.sigBytes>r&&(t=e.finalize(t)),t.clamp();for(var i=this._oKey=t.clone(),o=this._iKey=t.clone(),a=i.words,u=o.words,c=0;c<n;c++)a[c]^=1549556828,u[c]^=909522486;i.sigBytes=o.sigBytes=r,this.reset()},reset:function(){var e=this._hasher;e.reset(),e.update(this._iKey)},update:function(e){return this._hasher.update(e),this},finalize:function(e){var t=this._hasher,n=t.finalize(e);return t.reset(),t.finalize(this._oKey.clone().concat(n))}})))},function(e,t,n){"use strict";t.randomBytes=t.rng=t.pseudoRandomBytes=t.prng=n(66),t.createHash=t.Hash=n(79),t.createHmac=t.Hmac=n(175);var r=n(297),i=Object.keys(r),o=["sha1","sha224","sha256","sha384","sha512","md5","rmd160"].concat(i);t.getHashes=function(){return o};var s=n(178);t.pbkdf2=s.pbkdf2,t.pbkdf2Sync=s.pbkdf2Sync;var a=n(299);t.Cipher=a.Cipher,t.createCipher=a.createCipher,t.Cipheriv=a.Cipheriv,t.createCipheriv=a.createCipheriv,t.Decipher=a.Decipher,t.createDecipher=a.createDecipher,t.Decipheriv=a.Decipheriv,t.createDecipheriv=a.createDecipheriv,t.getCiphers=a.getCiphers,t.listCiphers=a.listCiphers;var u=n(314);t.DiffieHellmanGroup=u.DiffieHellmanGroup,t.createDiffieHellmanGroup=u.createDiffieHellmanGroup,t.getDiffieHellman=u.getDiffieHellman,t.createDiffieHellman=u.createDiffieHellman,t.DiffieHellman=u.DiffieHellman;var c=n(319);t.createSign=c.createSign,t.Sign=c.Sign,t.createVerify=c.createVerify,t.Verify=c.Verify,t.createECDH=n(360);var f=n(361);t.publicEncrypt=f.publicEncrypt,t.privateEncrypt=f.privateEncrypt,t.publicDecrypt=f.publicDecrypt,t.privateDecrypt=f.privateDecrypt;var l=n(364);t.randomFill=l.randomFill,t.randomFillSync=l.randomFillSync,t.createCredentials=function(){throw new Error(["sorry, createCredentials is not implemented yet","we accept pull requests","https://github.com/crypto-browserify/crypto-browserify"].join("\n"))},t.constants={DH_CHECK_P_NOT_SAFE_PRIME:2,DH_CHECK_P_NOT_PRIME:1,DH_UNABLE_TO_CHECK_GENERATOR:4,DH_NOT_SUITABLE_GENERATOR:8,NPN_ENABLED:1,ALPN_ENABLED:1,RSA_PKCS1_PADDING:1,RSA_SSLV23_PADDING:2,RSA_NO_PADDING:3,RSA_PKCS1_OAEP_PADDING:4,RSA_X931_PADDING:5,RSA_PKCS1_PSS_PADDING:6,POINT_CONVERSION_COMPRESSED:2,POINT_CONVERSION_UNCOMPRESSED:4,POINT_CONVERSION_HYBRID:6}},function(e,t,n){(t=e.exports=n(163)).Stream=t,t.Readable=t,t.Writable=n(167),t.Duplex=n(68),t.Transform=n(168),t.PassThrough=n(279),t.finished=n(115),t.pipeline=n(280)},function(e,t){},function(e,t,n){"use strict";function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}var s=n(6).Buffer,a=n(276).inspect,u=a&&a.custom||"inspect";e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}var t,n,c;return t=e,(n=[{key:"push",value:function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length}},{key:"unshift",value:function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length}},{key:"shift",value:function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n}},{key:"concat",value:function(e){if(0===this.length)return s.alloc(0);for(var t,n,r,i=s.allocUnsafe(e>>>0),o=this.head,a=0;o;)t=o.data,n=i,r=a,s.prototype.copy.call(t,n,r),a+=o.data.length,o=o.next;return i}},{key:"consume",value:function(e,t){var n;return e<this.head.data.length?(n=this.head.data.slice(0,e),this.head.data=this.head.data.slice(e)):n=e===this.head.data.length?this.shift():t?this._getString(e):this._getBuffer(e),n}},{key:"first",value:function(){return this.head.data}},{key:"_getString",value:function(e){var t=this.head,n=1,r=t.data;for(e-=r.length;t=t.next;){var i=t.data,o=e>i.length?i.length:e;if(o===i.length?r+=i:r+=i.slice(0,e),0==(e-=o)){o===i.length?(++n,t.next?this.head=t.next:this.head=this.tail=null):(this.head=t,t.data=i.slice(o));break}++n}return this.length-=n,r}},{key:"_getBuffer",value:function(e){var t=s.allocUnsafe(e),n=this.head,r=1;for(n.data.copy(t),e-=n.data.length;n=n.next;){var i=n.data,o=e>i.length?i.length:e;if(i.copy(t,t.length-e,0,o),0==(e-=o)){o===i.length?(++r,n.next?this.head=n.next:this.head=this.tail=null):(this.head=n,n.data=i.slice(o));break}++r}return this.length-=r,t}},{key:u,value:function(e,t){return a(this,function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}({},t,{depth:0,customInspect:!1}))}}])&&o(t.prototype,n),c&&o(t,c),e}()},function(e,t){},function(e,t,n){"use strict";(function(t){var r;function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var o=n(115),s=Symbol("lastResolve"),a=Symbol("lastReject"),u=Symbol("error"),c=Symbol("ended"),f=Symbol("lastPromise"),l=Symbol("handlePromise"),d=Symbol("stream");function h(e,t){return{value:e,done:t}}function p(e){var t=e[s];if(null!==t){var n=e[d].read();null!==n&&(e[f]=null,e[s]=null,e[a]=null,t(h(n,!1)))}}function v(e){t.nextTick(p,e)}var g=Object.getPrototypeOf((function(){})),m=Object.setPrototypeOf((i(r={get stream(){return this[d]},next:function(){var e=this,n=this[u];if(null!==n)return Promise.reject(n);if(this[c])return Promise.resolve(h(void 0,!0));if(this[d].destroyed)return new Promise((function(n,r){t.nextTick((function(){e[u]?r(e[u]):n(h(void 0,!0))}))}));var r,i=this[f];if(i)r=new Promise(function(e,t){return function(n,r){e.then((function(){t[c]?n(h(void 0,!0)):t[l](n,r)}),r)}}(i,this));else{var o=this[d].read();if(null!==o)return Promise.resolve(h(o,!1));r=new Promise(this[l])}return this[f]=r,r}},Symbol.asyncIterator,(function(){return this})),i(r,"return",(function(){var e=this;return new Promise((function(t,n){e[d].destroy(null,(function(e){e?n(e):t(h(void 0,!0))}))}))})),r),g);e.exports=function(e){var t,n=Object.create(m,(i(t={},d,{value:e,writable:!0}),i(t,s,{value:null,writable:!0}),i(t,a,{value:null,writable:!0}),i(t,u,{value:null,writable:!0}),i(t,c,{value:e._readableState.endEmitted,writable:!0}),i(t,l,{value:function(e,t){var r=n[d].read();r?(n[f]=null,n[s]=null,n[a]=null,e(h(r,!1))):(n[s]=e,n[a]=t)},writable:!0}),t));return n[f]=null,o(e,(function(e){if(e&&"ERR_STREAM_PREMATURE_CLOSE"!==e.code){var t=n[a];return null!==t&&(n[f]=null,n[s]=null,n[a]=null,t(e)),void(n[u]=e)}var r=n[s];null!==r&&(n[f]=null,n[s]=null,n[a]=null,r(h(void 0,!0))),n[c]=!0})),e.on("readable",v.bind(null,n)),n}}).call(this,n(20))},function(e,t){e.exports=function(){throw new Error("Readable.from is not available in the browser")}},function(e,t,n){"use strict";e.exports=i;var r=n(168);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e)}n(7)(i,r),i.prototype._transform=function(e,t,n){n(null,e)}},function(e,t,n){"use strict";var r;var i=n(67).codes,o=i.ERR_MISSING_ARGS,s=i.ERR_STREAM_DESTROYED;function a(e){if(e)throw e}function u(e,t,i,o){o=function(e){var t=!1;return function(){t||(t=!0,e.apply(void 0,arguments))}}(o);var a=!1;e.on("close",(function(){a=!0})),void 0===r&&(r=n(115)),r(e,{readable:t,writable:i},(function(e){if(e)return o(e);a=!0,o()}));var u=!1;return function(t){if(!a&&!u)return u=!0,function(e){return e.setHeader&&"function"==typeof e.abort}(e)?e.abort():"function"==typeof e.destroy?e.destroy():void o(t||new s("pipe"))}}function c(e){e()}function f(e,t){return e.pipe(t)}function l(e){return e.length?"function"!=typeof e[e.length-1]?a:e.pop():a}e.exports=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];var r,i=l(t);if(Array.isArray(t[0])&&(t=t[0]),t.length<2)throw new o("streams");var s=t.map((function(e,n){var o=n<t.length-1;return u(e,o,n>0,(function(e){r||(r=e),e&&s.forEach(c),o||(s.forEach(c),i(r))}))}));return t.reduce(f)}},function(e,t,n){var r=n(7),i=n(69),o=n(8).Buffer,s=[1518500249,1859775393,-1894007588,-899497514],a=new Array(80);function u(){this.init(),this._w=a,i.call(this,64,56)}function c(e){return e<<30|e>>>2}function f(e,t,n,r){return 0===e?t&n|~t&r:2===e?t&n|t&r|n&r:t^n^r}r(u,i),u.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},u.prototype._update=function(e){for(var t,n=this._w,r=0|this._a,i=0|this._b,o=0|this._c,a=0|this._d,u=0|this._e,l=0;l<16;++l)n[l]=e.readInt32BE(4*l);for(;l<80;++l)n[l]=n[l-3]^n[l-8]^n[l-14]^n[l-16];for(var d=0;d<80;++d){var h=~~(d/20),p=0|((t=r)<<5|t>>>27)+f(h,i,o,a)+u+n[d]+s[h];u=a,a=o,o=c(i),i=r,r=p}this._a=r+this._a|0,this._b=i+this._b|0,this._c=o+this._c|0,this._d=a+this._d|0,this._e=u+this._e|0},u.prototype._hash=function(){var e=o.allocUnsafe(20);return e.writeInt32BE(0|this._a,0),e.writeInt32BE(0|this._b,4),e.writeInt32BE(0|this._c,8),e.writeInt32BE(0|this._d,12),e.writeInt32BE(0|this._e,16),e},e.exports=u},function(e,t,n){var r=n(7),i=n(69),o=n(8).Buffer,s=[1518500249,1859775393,-1894007588,-899497514],a=new Array(80);function u(){this.init(),this._w=a,i.call(this,64,56)}function c(e){return e<<5|e>>>27}function f(e){return e<<30|e>>>2}function l(e,t,n,r){return 0===e?t&n|~t&r:2===e?t&n|t&r|n&r:t^n^r}r(u,i),u.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},u.prototype._update=function(e){for(var t,n=this._w,r=0|this._a,i=0|this._b,o=0|this._c,a=0|this._d,u=0|this._e,d=0;d<16;++d)n[d]=e.readInt32BE(4*d);for(;d<80;++d)n[d]=(t=n[d-3]^n[d-8]^n[d-14]^n[d-16])<<1|t>>>31;for(var h=0;h<80;++h){var p=~~(h/20),v=c(r)+l(p,i,o,a)+u+n[h]+s[p]|0;u=a,a=o,o=f(i),i=r,r=v}this._a=r+this._a|0,this._b=i+this._b|0,this._c=o+this._c|0,this._d=a+this._d|0,this._e=u+this._e|0},u.prototype._hash=function(){var e=o.allocUnsafe(20);return e.writeInt32BE(0|this._a,0),e.writeInt32BE(0|this._b,4),e.writeInt32BE(0|this._c,8),e.writeInt32BE(0|this._d,12),e.writeInt32BE(0|this._e,16),e},e.exports=u},function(e,t,n){var r=n(7),i=n(169),o=n(69),s=n(8).Buffer,a=new Array(64);function u(){this.init(),this._w=a,o.call(this,64,56)}r(u,i),u.prototype.init=function(){return this._a=3238371032,this._b=914150663,this._c=812702999,this._d=4144912697,this._e=4290775857,this._f=1750603025,this._g=1694076839,this._h=3204075428,this},u.prototype._hash=function(){var e=s.allocUnsafe(28);return e.writeInt32BE(this._a,0),e.writeInt32BE(this._b,4),e.writeInt32BE(this._c,8),e.writeInt32BE(this._d,12),e.writeInt32BE(this._e,16),e.writeInt32BE(this._f,20),e.writeInt32BE(this._g,24),e},e.exports=u},function(e,t,n){var r=n(7),i=n(170),o=n(69),s=n(8).Buffer,a=new Array(160);function u(){this.init(),this._w=a,o.call(this,128,112)}r(u,i),u.prototype.init=function(){return this._ah=3418070365,this._bh=1654270250,this._ch=2438529370,this._dh=355462360,this._eh=1731405415,this._fh=2394180231,this._gh=3675008525,this._hh=1203062813,this._al=3238371032,this._bl=914150663,this._cl=812702999,this._dl=4144912697,this._el=4290775857,this._fl=1750603025,this._gl=1694076839,this._hl=3204075428,this},u.prototype._hash=function(){var e=s.allocUnsafe(48);function t(t,n,r){e.writeInt32BE(t,r),e.writeInt32BE(n,r+4)}return t(this._ah,this._al,0),t(this._bh,this._bl,8),t(this._ch,this._cl,16),t(this._dh,this._dl,24),t(this._eh,this._el,32),t(this._fh,this._fl,40),e},e.exports=u},function(e,t,n){e.exports=i;var r=n(49).EventEmitter;function i(){r.call(this)}n(7)(i,r),i.Readable=n(118),i.Writable=n(292),i.Duplex=n(293),i.Transform=n(294),i.PassThrough=n(295),i.Stream=i,i.prototype.pipe=function(e,t){var n=this;function i(t){e.writable&&!1===e.write(t)&&n.pause&&n.pause()}function o(){n.readable&&n.resume&&n.resume()}n.on("data",i),e.on("drain",o),e._isStdio||t&&!1===t.end||(n.on("end",a),n.on("close",u));var s=!1;function a(){s||(s=!0,e.end())}function u(){s||(s=!0,"function"==typeof e.destroy&&e.destroy())}function c(e){if(f(),0===r.listenerCount(this,"error"))throw e}function f(){n.removeListener("data",i),e.removeListener("drain",o),n.removeListener("end",a),n.removeListener("close",u),n.removeListener("error",c),e.removeListener("error",c),n.removeListener("end",f),n.removeListener("close",f),e.removeListener("close",f)}return n.on("error",c),e.on("error",c),n.on("end",f),n.on("close",f),e.on("close",f),e.emit("pipe",n),e}},function(e,t){},function(e,t,n){"use strict";var r=n(119).Buffer,i=n(288);e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}return e.prototype.push=function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n},e.prototype.concat=function(e){if(0===this.length)return r.alloc(0);if(1===this.length)return this.head.data;for(var t,n,i,o=r.allocUnsafe(e>>>0),s=this.head,a=0;s;)t=s.data,n=o,i=a,t.copy(n,i),a+=s.data.length,s=s.next;return o},e}(),i&&i.inspect&&i.inspect.custom&&(e.exports.prototype[i.inspect.custom]=function(){var e=i.inspect({length:this.length});return this.constructor.name+" "+e})},function(e,t){},function(e,t,n){(function(e){var r=void 0!==e&&e||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function o(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new o(i.call(setTimeout,r,arguments),clearTimeout)},t.setInterval=function(){return new o(i.call(setInterval,r,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},o.prototype.unref=o.prototype.ref=function(){},o.prototype.close=function(){this._clearFn.call(r,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},n(290),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(31))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var r,i,o,s,a,u=1,c={},f=!1,l=e.document,d=Object.getPrototypeOf&&Object.getPrototypeOf(e);d=d&&d.setTimeout?d:e,"[object process]"==={}.toString.call(e.process)?r=function(e){t.nextTick((function(){p(e)}))}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((o=new MessageChannel).port1.onmessage=function(e){p(e.data)},r=function(e){o.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(i=l.documentElement,r=function(e){var t=l.createElement("script");t.onreadystatechange=function(){p(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):r=function(e){setTimeout(p,0,e)}:(s="setImmediate$"+Math.random()+"$",a=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(s)&&p(+t.data.slice(s.length))},e.addEventListener?e.addEventListener("message",a,!1):e.attachEvent("onmessage",a),r=function(t){e.postMessage(s+t,"*")}),d.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n<t.length;n++)t[n]=arguments[n+1];var i={callback:e,args:t};return c[u]=i,r(u),u++},d.clearImmediate=h}function h(e){delete c[e]}function p(e){if(f)setTimeout(p,0,e);else{var t=c[e];if(t){f=!0;try{!function(e){var t=e.callback,n=e.args;switch(n.length){case 0:t();break;case 1:t(n[0]);break;case 2:t(n[0],n[1]);break;case 3:t(n[0],n[1],n[2]);break;default:t.apply(void 0,n)}}(t)}finally{h(e),f=!1}}}}}("undefined"==typeof self?void 0===e?this:e:self)}).call(this,n(31),n(20))},function(e,t,n){"use strict";e.exports=o;var r=n(174),i=Object.create(n(80));function o(e){if(!(this instanceof o))return new o(e);r.call(this,e)}i.inherits=n(7),i.inherits(o,r),o.prototype._transform=function(e,t,n){n(null,e)}},function(e,t,n){e.exports=n(120)},function(e,t,n){e.exports=n(60)},function(e,t,n){e.exports=n(118).Transform},function(e,t,n){e.exports=n(118).PassThrough},function(e,t,n){"use strict";var r=n(7),i=n(8).Buffer,o=n(56),s=i.alloc(128);function a(e,t){o.call(this,"digest"),"string"==typeof t&&(t=i.from(t)),this._alg=e,this._key=t,t.length>64?t=e(t):t.length<64&&(t=i.concat([t,s],64));for(var n=this._ipad=i.allocUnsafe(64),r=this._opad=i.allocUnsafe(64),a=0;a<64;a++)n[a]=54^t[a],r[a]=92^t[a];this._hash=[n]}r(a,o),a.prototype._update=function(e){this._hash.push(e)},a.prototype._final=function(){var e=this._alg(i.concat(this._hash));return this._alg(i.concat([this._opad,e]))},e.exports=a},function(e,t,n){e.exports=n(177)},function(e,t,n){(function(t,r){var i,o=n(8).Buffer,s=n(179),a=n(180),u=n(181),c=n(182),f=t.crypto&&t.crypto.subtle,l={sha:"SHA-1","sha-1":"SHA-1",sha1:"SHA-1",sha256:"SHA-256","sha-256":"SHA-256",sha384:"SHA-384","sha-384":"SHA-384","sha-512":"SHA-512",sha512:"SHA-512"},d=[];function h(e,t,n,r,i){return f.importKey("raw",e,{name:"PBKDF2"},!1,["deriveBits"]).then((function(e){return f.deriveBits({name:"PBKDF2",salt:t,iterations:n,hash:{name:i}},e,r<<3)})).then((function(e){return o.from(e)}))}e.exports=function(e,n,p,v,g,m){"function"==typeof g&&(m=g,g=void 0);var b=l[(g=g||"sha1").toLowerCase()];if(!b||"function"!=typeof t.Promise)return r.nextTick((function(){var t;try{t=u(e,n,p,v,g)}catch(e){return m(e)}m(null,t)}));if(s(p,v),e=c(e,a,"Password"),n=c(n,a,"Salt"),"function"!=typeof m)throw new Error("No callback provided to pbkdf2");!function(e,t){e.then((function(e){r.nextTick((function(){t(null,e)}))}),(function(e){r.nextTick((function(){t(e)}))}))}(function(e){if(t.process&&!t.process.browser)return Promise.resolve(!1);if(!f||!f.importKey||!f.deriveBits)return Promise.resolve(!1);if(void 0!==d[e])return d[e];var n=h(i=i||o.alloc(8),i,10,128,e).then((function(){return!0})).catch((function(){return!1}));return d[e]=n,n}(b).then((function(t){return t?h(e,n,p,v,b):u(e,n,p,v,g)})),m)}}).call(this,n(31),n(20))},function(e,t,n){var r=n(300),i=n(122),o=n(123),s=n(313),a=n(94);function u(e,t,n){if(e=e.toLowerCase(),o[e])return i.createCipheriv(e,t,n);if(s[e])return new r({key:t,iv:n,mode:e});throw new TypeError("invalid suite type")}function c(e,t,n){if(e=e.toLowerCase(),o[e])return i.createDecipheriv(e,t,n);if(s[e])return new r({key:t,iv:n,mode:e,decrypt:!0});throw new TypeError("invalid suite type")}t.createCipher=t.Cipher=function(e,t){var n,r;if(e=e.toLowerCase(),o[e])n=o[e].key,r=o[e].iv;else{if(!s[e])throw new TypeError("invalid suite type");n=8*s[e].key,r=s[e].iv}var i=a(t,!1,n,r);return u(e,i.key,i.iv)},t.createCipheriv=t.Cipheriv=u,t.createDecipher=t.Decipher=function(e,t){var n,r;if(e=e.toLowerCase(),o[e])n=o[e].key,r=o[e].iv;else{if(!s[e])throw new TypeError("invalid suite type");n=8*s[e].key,r=s[e].iv}var i=a(t,!1,n,r);return c(e,i.key,i.iv)},t.createDecipheriv=t.Decipheriv=c,t.listCiphers=t.getCiphers=function(){return Object.keys(s).concat(i.getCiphers())}},function(e,t,n){var r=n(56),i=n(301),o=n(7),s=n(8).Buffer,a={"des-ede3-cbc":i.CBC.instantiate(i.EDE),"des-ede3":i.EDE,"des-ede-cbc":i.CBC.instantiate(i.EDE),"des-ede":i.EDE,"des-cbc":i.CBC.instantiate(i.DES),"des-ecb":i.DES};function u(e){r.call(this);var t,n=e.mode.toLowerCase(),i=a[n];t=e.decrypt?"decrypt":"encrypt";var o=e.key;s.isBuffer(o)||(o=s.from(o)),"des-ede"!==n&&"des-ede-cbc"!==n||(o=s.concat([o,o.slice(0,8)]));var u=e.iv;s.isBuffer(u)||(u=s.from(u)),this._des=i.create({key:o,iv:u,type:t})}a.des=a["des-cbc"],a.des3=a["des-ede3-cbc"],e.exports=u,o(u,r),u.prototype._update=function(e){return s.from(this._des.update(e))},u.prototype._final=function(){return s.from(this._des.final())}},function(e,t,n){"use strict";t.utils=n(183),t.Cipher=n(121),t.DES=n(184),t.CBC=n(302),t.EDE=n(303)},function(e,t,n){"use strict";var r=n(46),i=n(7),o={};function s(e){r.equal(e.length,8,"Invalid IV length"),this.iv=new Array(8);for(var t=0;t<this.iv.length;t++)this.iv[t]=e[t]}t.instantiate=function(e){function t(t){e.call(this,t),this._cbcInit()}i(t,e);for(var n=Object.keys(o),r=0;r<n.length;r++){var s=n[r];t.prototype[s]=o[s]}return t.create=function(e){return new t(e)},t},o._cbcInit=function(){var e=new s(this.options.iv);this._cbcState=e},o._update=function(e,t,n,r){var i=this._cbcState,o=this.constructor.super_.prototype,s=i.iv;if("encrypt"===this.type){for(var a=0;a<this.blockSize;a++)s[a]^=e[t+a];o._update.call(this,s,0,n,r);for(a=0;a<this.blockSize;a++)s[a]=n[r+a]}else{o._update.call(this,e,t,n,r);for(a=0;a<this.blockSize;a++)n[r+a]^=s[a];for(a=0;a<this.blockSize;a++)s[a]=e[t+a]}}},function(e,t,n){"use strict";var r=n(46),i=n(7),o=n(121),s=n(184);function a(e,t){r.equal(t.length,24,"Invalid key length");var n=t.slice(0,8),i=t.slice(8,16),o=t.slice(16,24);this.ciphers="encrypt"===e?[s.create({type:"encrypt",key:n}),s.create({type:"decrypt",key:i}),s.create({type:"encrypt",key:o})]:[s.create({type:"decrypt",key:o}),s.create({type:"encrypt",key:i}),s.create({type:"decrypt",key:n})]}function u(e){o.call(this,e);var t=new a(this.type,this.options.key);this._edeState=t}i(u,o),e.exports=u,u.create=function(e){return new u(e)},u.prototype._update=function(e,t,n,r){var i=this._edeState;i.ciphers[0]._update(e,t,n,r),i.ciphers[1]._update(n,r,n,r),i.ciphers[2]._update(n,r,n,r)},u.prototype._pad=s.prototype._pad,u.prototype._unpad=s.prototype._unpad},function(e,t,n){var r=n(123),i=n(188),o=n(8).Buffer,s=n(189),a=n(56),u=n(93),c=n(94);function f(e,t,n){a.call(this),this._cache=new d,this._cipher=new u.AES(t),this._prev=o.from(n),this._mode=e,this._autopadding=!0}n(7)(f,a),f.prototype._update=function(e){var t,n;this._cache.add(e);for(var r=[];t=this._cache.get();)n=this._mode.encrypt(this,t),r.push(n);return o.concat(r)};var l=o.alloc(16,16);function d(){this.cache=o.allocUnsafe(0)}function h(e,t,n){var a=r[e.toLowerCase()];if(!a)throw new TypeError("invalid suite type");if("string"==typeof t&&(t=o.from(t)),t.length!==a.key/8)throw new TypeError("invalid key length "+t.length);if("string"==typeof n&&(n=o.from(n)),"GCM"!==a.mode&&n.length!==a.iv)throw new TypeError("invalid iv length "+n.length);return"stream"===a.type?new s(a.module,t,n):"auth"===a.type?new i(a.module,t,n):new f(a.module,t,n)}f.prototype._final=function(){var e=this._cache.flush();if(this._autopadding)return e=this._mode.encrypt(this,e),this._cipher.scrub(),e;if(!e.equals(l))throw this._cipher.scrub(),new Error("data not multiple of block length")},f.prototype.setAutoPadding=function(e){return this._autopadding=!!e,this},d.prototype.add=function(e){this.cache=o.concat([this.cache,e])},d.prototype.get=function(){if(this.cache.length>15){var e=this.cache.slice(0,16);return this.cache=this.cache.slice(16),e}return null},d.prototype.flush=function(){for(var e=16-this.cache.length,t=o.allocUnsafe(e),n=-1;++n<e;)t.writeUInt8(e,n);return o.concat([this.cache,t])},t.createCipheriv=h,t.createCipher=function(e,t){var n=r[e.toLowerCase()];if(!n)throw new TypeError("invalid suite type");var i=c(t,!1,n.key,n.iv);return h(e,i.key,i.iv)}},function(e,t){t.encrypt=function(e,t){return e._cipher.encryptBlock(t)},t.decrypt=function(e,t){return e._cipher.decryptBlock(t)}},function(e,t,n){var r=n(81);t.encrypt=function(e,t){var n=r(t,e._prev);return e._prev=e._cipher.encryptBlock(n),e._prev},t.decrypt=function(e,t){var n=e._prev;e._prev=t;var i=e._cipher.decryptBlock(t);return r(i,n)}},function(e,t,n){var r=n(8).Buffer,i=n(81);function o(e,t,n){var o=t.length,s=i(t,e._cache);return e._cache=e._cache.slice(o),e._prev=r.concat([e._prev,n?t:s]),s}t.encrypt=function(e,t,n){for(var i,s=r.allocUnsafe(0);t.length;){if(0===e._cache.length&&(e._cache=e._cipher.encryptBlock(e._prev),e._prev=r.allocUnsafe(0)),!(e._cache.length<=t.length)){s=r.concat([s,o(e,t,n)]);break}i=e._cache.length,s=r.concat([s,o(e,t.slice(0,i),n)]),t=t.slice(i)}return s}},function(e,t,n){var r=n(8).Buffer;function i(e,t,n){var i=e._cipher.encryptBlock(e._prev)[0]^t;return e._prev=r.concat([e._prev.slice(1),r.from([n?t:i])]),i}t.encrypt=function(e,t,n){for(var o=t.length,s=r.allocUnsafe(o),a=-1;++a<o;)s[a]=i(e,t[a],n);return s}},function(e,t,n){var r=n(8).Buffer;function i(e,t,n){for(var r,i,s=-1,a=0;++s<8;)r=t&1<<7-s?128:0,a+=(128&(i=e._cipher.encryptBlock(e._prev)[0]^r))>>s%8,e._prev=o(e._prev,n?r:i);return a}function o(e,t){var n=e.length,i=-1,o=r.allocUnsafe(e.length);for(e=r.concat([e,r.from([t])]);++i<n;)o[i]=e[i]<<1|e[i+1]>>7;return o}t.encrypt=function(e,t,n){for(var o=t.length,s=r.allocUnsafe(o),a=-1;++a<o;)s[a]=i(e,t[a],n);return s}},function(e,t,n){(function(e){var r=n(81);function i(e){return e._prev=e._cipher.encryptBlock(e._prev),e._prev}t.encrypt=function(t,n){for(;t._cache.length<n.length;)t._cache=e.concat([t._cache,i(t)]);var o=t._cache.slice(0,n.length);return t._cache=t._cache.slice(n.length),r(n,o)}}).call(this,n(6).Buffer)},function(e,t,n){var r=n(8).Buffer,i=r.alloc(16,0);function o(e){var t=r.allocUnsafe(16);return t.writeUInt32BE(e[0]>>>0,0),t.writeUInt32BE(e[1]>>>0,4),t.writeUInt32BE(e[2]>>>0,8),t.writeUInt32BE(e[3]>>>0,12),t}function s(e){this.h=e,this.state=r.alloc(16,0),this.cache=r.allocUnsafe(0)}s.prototype.ghash=function(e){for(var t=-1;++t<e.length;)this.state[t]^=e[t];this._multiply()},s.prototype._multiply=function(){for(var e,t,n,r=[(e=this.h).readUInt32BE(0),e.readUInt32BE(4),e.readUInt32BE(8),e.readUInt32BE(12)],i=[0,0,0,0],s=-1;++s<128;){for(0!=(this.state[~~(s/8)]&1<<7-s%8)&&(i[0]^=r[0],i[1]^=r[1],i[2]^=r[2],i[3]^=r[3]),n=0!=(1&r[3]),t=3;t>0;t--)r[t]=r[t]>>>1|(1&r[t-1])<<31;r[0]=r[0]>>>1,n&&(r[0]=r[0]^225<<24)}this.state=o(i)},s.prototype.update=function(e){var t;for(this.cache=r.concat([this.cache,e]);this.cache.length>=16;)t=this.cache.slice(0,16),this.cache=this.cache.slice(16),this.ghash(t)},s.prototype.final=function(e,t){return this.cache.length&&this.ghash(r.concat([this.cache,i],16)),this.ghash(o([0,e,0,t])),this.state},e.exports=s},function(e,t,n){var r=n(188),i=n(8).Buffer,o=n(123),s=n(189),a=n(56),u=n(93),c=n(94);function f(e,t,n){a.call(this),this._cache=new l,this._last=void 0,this._cipher=new u.AES(t),this._prev=i.from(n),this._mode=e,this._autopadding=!0}function l(){this.cache=i.allocUnsafe(0)}function d(e,t,n){var a=o[e.toLowerCase()];if(!a)throw new TypeError("invalid suite type");if("string"==typeof n&&(n=i.from(n)),"GCM"!==a.mode&&n.length!==a.iv)throw new TypeError("invalid iv length "+n.length);if("string"==typeof t&&(t=i.from(t)),t.length!==a.key/8)throw new TypeError("invalid key length "+t.length);return"stream"===a.type?new s(a.module,t,n,!0):"auth"===a.type?new r(a.module,t,n,!0):new f(a.module,t,n)}n(7)(f,a),f.prototype._update=function(e){var t,n;this._cache.add(e);for(var r=[];t=this._cache.get(this._autopadding);)n=this._mode.decrypt(this,t),r.push(n);return i.concat(r)},f.prototype._final=function(){var e=this._cache.flush();if(this._autopadding)return function(e){var t=e[15];if(t<1||t>16)throw new Error("unable to decrypt data");var n=-1;for(;++n<t;)if(e[n+(16-t)]!==t)throw new Error("unable to decrypt data");if(16===t)return;return e.slice(0,16-t)}(this._mode.decrypt(this,e));if(e)throw new Error("data not multiple of block length")},f.prototype.setAutoPadding=function(e){return this._autopadding=!!e,this},l.prototype.add=function(e){this.cache=i.concat([this.cache,e])},l.prototype.get=function(e){var t;if(e){if(this.cache.length>16)return t=this.cache.slice(0,16),this.cache=this.cache.slice(16),t}else if(this.cache.length>=16)return t=this.cache.slice(0,16),this.cache=this.cache.slice(16),t;return null},l.prototype.flush=function(){if(this.cache.length)return this.cache},t.createDecipher=function(e,t){var n=o[e.toLowerCase()];if(!n)throw new TypeError("invalid suite type");var r=c(t,!1,n.key,n.iv);return d(e,r.key,r.iv)},t.createDecipheriv=d},function(e,t){t["des-ecb"]={key:8,iv:0},t["des-cbc"]=t.des={key:8,iv:8},t["des-ede3-cbc"]=t.des3={key:24,iv:8},t["des-ede3"]={key:24,iv:0},t["des-ede-cbc"]={key:16,iv:8},t["des-ede"]={key:16,iv:0}},function(e,t,n){(function(e){var r=n(190),i=n(317),o=n(318);var s={binary:!0,hex:!0,base64:!0};t.DiffieHellmanGroup=t.createDiffieHellmanGroup=t.getDiffieHellman=function(t){var n=new e(i[t].prime,"hex"),r=new e(i[t].gen,"hex");return new o(n,r)},t.createDiffieHellman=t.DiffieHellman=function t(n,i,a,u){return e.isBuffer(i)||void 0===s[i]?t(n,"binary",i,a):(i=i||"binary",u=u||"binary",a=a||new e([2]),e.isBuffer(a)||(a=new e(a,u)),"number"==typeof n?new o(r(n,a),a,!0):(e.isBuffer(n)||(n=new e(n,i)),new o(n,a,!0)))}}).call(this,n(6).Buffer)},function(e,t){},function(e,t){},function(e){e.exports=JSON.parse('{"modp1":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff"},"modp2":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff"},"modp5":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff"},"modp14":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff"},"modp15":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a93ad2caffffffffffffffff"},"modp16":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff"},"modp17":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dcc4024ffffffffffffffff"},"modp18":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f44f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510bd7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc9190da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3dfffffffffffffffff"}}')},function(e,t,n){(function(t){var r=n(29),i=new(n(191)),o=new r(24),s=new r(11),a=new r(10),u=new r(3),c=new r(7),f=n(190),l=n(66);function d(e,n){return n=n||"utf8",t.isBuffer(e)||(e=new t(e,n)),this._pub=new r(e),this}function h(e,n){return n=n||"utf8",t.isBuffer(e)||(e=new t(e,n)),this._priv=new r(e),this}e.exports=v;var p={};function v(e,t,n){this.setGenerator(t),this.__prime=new r(e),this._prime=r.mont(this.__prime),this._primeLen=e.length,this._pub=void 0,this._priv=void 0,this._primeCode=void 0,n?(this.setPublicKey=d,this.setPrivateKey=h):this._primeCode=8}function g(e,n){var r=new t(e.toArray());return n?r.toString(n):r}Object.defineProperty(v.prototype,"verifyError",{enumerable:!0,get:function(){return"number"!=typeof this._primeCode&&(this._primeCode=function(e,t){var n=t.toString("hex"),r=[n,e.toString(16)].join("_");if(r in p)return p[r];var l,d=0;if(e.isEven()||!f.simpleSieve||!f.fermatTest(e)||!i.test(e))return d+=1,d+="02"===n||"05"===n?8:4,p[r]=d,d;switch(i.test(e.shrn(1))||(d+=2),n){case"02":e.mod(o).cmp(s)&&(d+=8);break;case"05":(l=e.mod(a)).cmp(u)&&l.cmp(c)&&(d+=8);break;default:d+=4}return p[r]=d,d}(this.__prime,this.__gen)),this._primeCode}}),v.prototype.generateKeys=function(){return this._priv||(this._priv=new r(l(this._primeLen))),this._pub=this._gen.toRed(this._prime).redPow(this._priv).fromRed(),this.getPublicKey()},v.prototype.computeSecret=function(e){var n=(e=(e=new r(e)).toRed(this._prime)).redPow(this._priv).fromRed(),i=new t(n.toArray()),o=this.getPrime();if(i.length<o.length){var s=new t(o.length-i.length);s.fill(0),i=t.concat([s,i])}return i},v.prototype.getPublicKey=function(e){return g(this._pub,e)},v.prototype.getPrivateKey=function(e){return g(this._priv,e)},v.prototype.getPrime=function(e){return g(this.__prime,e)},v.prototype.getGenerator=function(e){return g(this._gen,e)},v.prototype.setGenerator=function(e,n){return n=n||"utf8",t.isBuffer(e)||(e=new t(e,n)),this.__gen=e,this._gen=new r(e),this}}).call(this,n(6).Buffer)},function(e,t,n){var r=n(8).Buffer,i=n(79),o=n(320),s=n(7),a=n(328),u=n(359),c=n(177);function f(e){o.Writable.call(this);var t=c[e];if(!t)throw new Error("Unknown message digest");this._hashType=t.hash,this._hash=i(t.hash),this._tag=t.id,this._signType=t.sign}function l(e){o.Writable.call(this);var t=c[e];if(!t)throw new Error("Unknown message digest");this._hash=i(t.hash),this._tag=t.id,this._signType=t.sign}function d(e){return new f(e)}function h(e){return new l(e)}Object.keys(c).forEach((function(e){c[e].id=r.from(c[e].id,"hex"),c[e.toLowerCase()]=c[e]})),s(f,o.Writable),f.prototype._write=function(e,t,n){this._hash.update(e),n()},f.prototype.update=function(e,t){return"string"==typeof e&&(e=r.from(e,t)),this._hash.update(e),this},f.prototype.sign=function(e,t){this.end();var n=this._hash.digest(),r=a(n,e,this._hashType,this._signType,this._tag);return t?r.toString(t):r},s(l,o.Writable),l.prototype._write=function(e,t,n){this._hash.update(e),n()},l.prototype.update=function(e,t){return"string"==typeof e&&(e=r.from(e,t)),this._hash.update(e),this},l.prototype.verify=function(e,t,n){"string"==typeof t&&(t=r.from(t,n)),this.end();var i=this._hash.digest();return u(t,i,e,this._signType,this._tag)},e.exports={Sign:d,Verify:h,createSign:d,createVerify:h}},function(e,t,n){(t=e.exports=n(192)).Stream=t,t.Readable=t,t.Writable=n(196),t.Duplex=n(71),t.Transform=n(197),t.PassThrough=n(326),t.finished=n(125),t.pipeline=n(327)},function(e,t){},function(e,t,n){"use strict";function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}var s=n(6).Buffer,a=n(323).inspect,u=a&&a.custom||"inspect";e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}var t,n,c;return t=e,(n=[{key:"push",value:function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length}},{key:"unshift",value:function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length}},{key:"shift",value:function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n}},{key:"concat",value:function(e){if(0===this.length)return s.alloc(0);for(var t,n,r,i=s.allocUnsafe(e>>>0),o=this.head,a=0;o;)t=o.data,n=i,r=a,s.prototype.copy.call(t,n,r),a+=o.data.length,o=o.next;return i}},{key:"consume",value:function(e,t){var n;return e<this.head.data.length?(n=this.head.data.slice(0,e),this.head.data=this.head.data.slice(e)):n=e===this.head.data.length?this.shift():t?this._getString(e):this._getBuffer(e),n}},{key:"first",value:function(){return this.head.data}},{key:"_getString",value:function(e){var t=this.head,n=1,r=t.data;for(e-=r.length;t=t.next;){var i=t.data,o=e>i.length?i.length:e;if(o===i.length?r+=i:r+=i.slice(0,e),0==(e-=o)){o===i.length?(++n,t.next?this.head=t.next:this.head=this.tail=null):(this.head=t,t.data=i.slice(o));break}++n}return this.length-=n,r}},{key:"_getBuffer",value:function(e){var t=s.allocUnsafe(e),n=this.head,r=1;for(n.data.copy(t),e-=n.data.length;n=n.next;){var i=n.data,o=e>i.length?i.length:e;if(i.copy(t,t.length-e,0,o),0==(e-=o)){o===i.length?(++r,n.next?this.head=n.next:this.head=this.tail=null):(this.head=n,n.data=i.slice(o));break}++r}return this.length-=r,t}},{key:u,value:function(e,t){return a(this,function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}({},t,{depth:0,customInspect:!1}))}}])&&o(t.prototype,n),c&&o(t,c),e}()},function(e,t){},function(e,t,n){"use strict";(function(t){var r;function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var o=n(125),s=Symbol("lastResolve"),a=Symbol("lastReject"),u=Symbol("error"),c=Symbol("ended"),f=Symbol("lastPromise"),l=Symbol("handlePromise"),d=Symbol("stream");function h(e,t){return{value:e,done:t}}function p(e){var t=e[s];if(null!==t){var n=e[d].read();null!==n&&(e[f]=null,e[s]=null,e[a]=null,t(h(n,!1)))}}function v(e){t.nextTick(p,e)}var g=Object.getPrototypeOf((function(){})),m=Object.setPrototypeOf((i(r={get stream(){return this[d]},next:function(){var e=this,n=this[u];if(null!==n)return Promise.reject(n);if(this[c])return Promise.resolve(h(void 0,!0));if(this[d].destroyed)return new Promise((function(n,r){t.nextTick((function(){e[u]?r(e[u]):n(h(void 0,!0))}))}));var r,i=this[f];if(i)r=new Promise(function(e,t){return function(n,r){e.then((function(){t[c]?n(h(void 0,!0)):t[l](n,r)}),r)}}(i,this));else{var o=this[d].read();if(null!==o)return Promise.resolve(h(o,!1));r=new Promise(this[l])}return this[f]=r,r}},Symbol.asyncIterator,(function(){return this})),i(r,"return",(function(){var e=this;return new Promise((function(t,n){e[d].destroy(null,(function(e){e?n(e):t(h(void 0,!0))}))}))})),r),g);e.exports=function(e){var t,n=Object.create(m,(i(t={},d,{value:e,writable:!0}),i(t,s,{value:null,writable:!0}),i(t,a,{value:null,writable:!0}),i(t,u,{value:null,writable:!0}),i(t,c,{value:e._readableState.endEmitted,writable:!0}),i(t,l,{value:function(e,t){var r=n[d].read();r?(n[f]=null,n[s]=null,n[a]=null,e(h(r,!1))):(n[s]=e,n[a]=t)},writable:!0}),t));return n[f]=null,o(e,(function(e){if(e&&"ERR_STREAM_PREMATURE_CLOSE"!==e.code){var t=n[a];return null!==t&&(n[f]=null,n[s]=null,n[a]=null,t(e)),void(n[u]=e)}var r=n[s];null!==r&&(n[f]=null,n[s]=null,n[a]=null,r(h(void 0,!0))),n[c]=!0})),e.on("readable",v.bind(null,n)),n}}).call(this,n(20))},function(e,t){e.exports=function(){throw new Error("Readable.from is not available in the browser")}},function(e,t,n){"use strict";e.exports=i;var r=n(197);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e)}n(7)(i,r),i.prototype._transform=function(e,t,n){n(null,e)}},function(e,t,n){"use strict";var r;var i=n(70).codes,o=i.ERR_MISSING_ARGS,s=i.ERR_STREAM_DESTROYED;function a(e){if(e)throw e}function u(e,t,i,o){o=function(e){var t=!1;return function(){t||(t=!0,e.apply(void 0,arguments))}}(o);var a=!1;e.on("close",(function(){a=!0})),void 0===r&&(r=n(125)),r(e,{readable:t,writable:i},(function(e){if(e)return o(e);a=!0,o()}));var u=!1;return function(t){if(!a&&!u)return u=!0,function(e){return e.setHeader&&"function"==typeof e.abort}(e)?e.abort():"function"==typeof e.destroy?e.destroy():void o(t||new s("pipe"))}}function c(e){e()}function f(e,t){return e.pipe(t)}function l(e){return e.length?"function"!=typeof e[e.length-1]?a:e.pop():a}e.exports=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];var r,i=l(t);if(Array.isArray(t[0])&&(t=t[0]),t.length<2)throw new o("streams");var s=t.map((function(e,n){var o=n<t.length-1;return u(e,o,n>0,(function(e){r||(r=e),e&&s.forEach(c),o||(s.forEach(c),i(r))}))}));return t.reduce(f)}},function(e,t,n){var r=n(8).Buffer,i=n(175),o=n(126),s=n(127).ec,a=n(203),u=n(96),c=n(209);function f(e,t,n,o){if((e=r.from(e.toArray())).length<t.byteLength()){var s=r.alloc(t.byteLength()-e.length);e=r.concat([s,e])}var a=n.length,u=function(e,t){e=(e=l(e,t)).mod(t);var n=r.from(e.toArray());if(n.length<t.byteLength()){var i=r.alloc(t.byteLength()-n.length);n=r.concat([i,n])}return n}(n,t),c=r.alloc(a);c.fill(1);var f=r.alloc(a);return f=i(o,f).update(c).update(r.from([0])).update(e).update(u).digest(),c=i(o,f).update(c).digest(),{k:f=i(o,f).update(c).update(r.from([1])).update(e).update(u).digest(),v:c=i(o,f).update(c).digest()}}function l(e,t){var n=new a(e),r=(e.length<<3)-t.bitLength();return r>0&&n.ishrn(r),n}function d(e,t,n){var o,s;do{for(o=r.alloc(0);8*o.length<e.bitLength();)t.v=i(n,t.k).update(t.v).digest(),o=r.concat([o,t.v]);s=l(o,e),t.k=i(n,t.k).update(t.v).update(r.from([0])).digest(),t.v=i(n,t.k).update(t.v).digest()}while(-1!==s.cmp(e));return s}function h(e,t,n,r){return e.toRed(a.mont(n)).redPow(t).fromRed().mod(r)}e.exports=function(e,t,n,i,p){var v=u(t);if(v.curve){if("ecdsa"!==i&&"ecdsa/rsa"!==i)throw new Error("wrong private key type");return function(e,t){var n=c[t.curve.join(".")];if(!n)throw new Error("unknown curve "+t.curve.join("."));var i=new s(n).keyFromPrivate(t.privateKey).sign(e);return r.from(i.toDER())}(e,v)}if("dsa"===v.type){if("dsa"!==i)throw new Error("wrong private key type");return function(e,t,n){var i,o=t.params.priv_key,s=t.params.p,u=t.params.q,c=t.params.g,p=new a(0),v=l(e,u).mod(u),g=!1,m=f(o,u,e,n);for(;!1===g;)i=d(u,m,n),p=h(c,i,s,u),0===(g=i.invm(u).imul(v.add(o.mul(p))).mod(u)).cmpn(0)&&(g=!1,p=new a(0));return function(e,t){e=e.toArray(),t=t.toArray(),128&e[0]&&(e=[0].concat(e));128&t[0]&&(t=[0].concat(t));var n=[48,e.length+t.length+4,2,e.length];return n=n.concat(e,[2,t.length],t),r.from(n)}(p,g)}(e,v,n)}if("rsa"!==i&&"ecdsa/rsa"!==i)throw new Error("wrong private key type");e=r.concat([p,e]);for(var g=v.modulus.byteLength(),m=[0,1];e.length+m.length+1<g;)m.push(255);m.push(0);for(var b=-1;++b<e.length;)m.push(e[b]);return o(m,v)},e.exports.getKey=f,e.exports.makeKey=d},function(e,t,n){(function(e){!function(e,t){"use strict";function r(e,t){if(!e)throw new Error(t||"Assertion failed")}function i(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}function o(e,t,n){if(o.isBN(e))return e;this.negative=0,this.words=null,this.length=0,this.red=null,null!==e&&("le"!==t&&"be"!==t||(n=t,t=10),this._init(e||0,t||10,n||"be"))}var s;"object"==typeof e?e.exports=o:t.BN=o,o.BN=o,o.wordSize=26;try{s=n(330).Buffer}catch(e){}function a(e,t,n){for(var i=0,o=Math.min(e.length,n),s=0,a=t;a<o;a++){var u,c=e.charCodeAt(a)-48;i<<=4,i|=u=c>=49&&c<=54?c-49+10:c>=17&&c<=22?c-17+10:c,s|=u}return r(!(240&s),"Invalid character in "+e),i}function u(e,t,n,i){for(var o=0,s=0,a=Math.min(e.length,n),u=t;u<a;u++){var c=e.charCodeAt(u)-48;o*=i,s=c>=49?c-49+10:c>=17?c-17+10:c,r(c>=0&&s<i,"Invalid character"),o+=s}return o}function c(e,t){e.words=t.words,e.length=t.length,e.negative=t.negative,e.red=t.red}if(o.isBN=function(e){return e instanceof o||null!==e&&"object"==typeof e&&e.constructor.wordSize===o.wordSize&&Array.isArray(e.words)},o.max=function(e,t){return e.cmp(t)>0?e:t},o.min=function(e,t){return e.cmp(t)<0?e:t},o.prototype._init=function(e,t,n){if("number"==typeof e)return this._initNumber(e,t,n);if("object"==typeof e)return this._initArray(e,t,n);"hex"===t&&(t=16),r(t===(0|t)&&t>=2&&t<=36);var i=0;"-"===(e=e.toString().replace(/\s+/g,""))[0]&&i++,16===t?this._parseHex(e,i):this._parseBase(e,t,i),"-"===e[0]&&(this.negative=1),this._strip(),"le"===n&&this._initArray(this.toArray(),t,n)},o.prototype._initNumber=function(e,t,n){e<0&&(this.negative=1,e=-e),e<67108864?(this.words=[67108863&e],this.length=1):e<4503599627370496?(this.words=[67108863&e,e/67108864&67108863],this.length=2):(r(e<9007199254740992),this.words=[67108863&e,e/67108864&67108863,1],this.length=3),"le"===n&&this._initArray(this.toArray(),t,n)},o.prototype._initArray=function(e,t,n){if(r("number"==typeof e.length),e.length<=0)return this.words=[0],this.length=1,this;this.length=Math.ceil(e.length/3),this.words=new Array(this.length);for(var i=0;i<this.length;i++)this.words[i]=0;var o,s,a=0;if("be"===n)for(i=e.length-1,o=0;i>=0;i-=3)s=e[i]|e[i-1]<<8|e[i-2]<<16,this.words[o]|=s<<a&67108863,this.words[o+1]=s>>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===n)for(i=0,o=0;i<e.length;i+=3)s=e[i]|e[i+1]<<8|e[i+2]<<16,this.words[o]|=s<<a&67108863,this.words[o+1]=s>>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this._strip()},o.prototype._parseHex=function(e,t){this.length=Math.ceil((e.length-t)/6),this.words=new Array(this.length);for(var n=0;n<this.length;n++)this.words[n]=0;var r,i,o=0;for(n=e.length-6,r=0;n>=t;n-=6)i=a(e,n,n+6),this.words[r]|=i<<o&67108863,this.words[r+1]|=i>>>26-o&4194303,(o+=24)>=26&&(o-=26,r++);n+6!==t&&(i=a(e,t,n+6),this.words[r]|=i<<o&67108863,this.words[r+1]|=i>>>26-o&4194303),this._strip()},o.prototype._parseBase=function(e,t,n){this.words=[0],this.length=1;for(var r=0,i=1;i<=67108863;i*=t)r++;r--,i=i/t|0;for(var o=e.length-n,s=o%r,a=Math.min(o,o-s)+n,c=0,f=n;f<a;f+=r)c=u(e,f,f+r,t),this.imuln(i),this.words[0]+c<67108864?this.words[0]+=c:this._iaddn(c);if(0!==s){var l=1;for(c=u(e,f,e.length,t),f=0;f<s;f++)l*=t;this.imuln(l),this.words[0]+c<67108864?this.words[0]+=c:this._iaddn(c)}},o.prototype.copy=function(e){e.words=new Array(this.length);for(var t=0;t<this.length;t++)e.words[t]=this.words[t];e.length=this.length,e.negative=this.negative,e.red=this.red},o.prototype._move=function(e){c(e,this)},o.prototype.clone=function(){var e=new o(null);return this.copy(e),e},o.prototype._expand=function(e){for(;this.length<e;)this.words[this.length++]=0;return this},o.prototype._strip=function(){for(;this.length>1&&0===this.words[this.length-1];)this.length--;return this._normSign()},o.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},"undefined"!=typeof Symbol&&"function"==typeof Symbol.for)try{o.prototype[Symbol.for("nodejs.util.inspect.custom")]=f}catch(e){o.prototype.inspect=f}else o.prototype.inspect=f;function f(){return(this.red?"<BN-R: ":"<BN: ")+this.toString(16)+">"}var l=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],d=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],h=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];o.prototype.toString=function(e,t){var n;if(t=0|t||1,16===(e=e||10)||"hex"===e){n="";for(var i=0,o=0,s=0;s<this.length;s++){var a=this.words[s],u=(16777215&(a<<i|o)).toString(16);n=0!==(o=a>>>24-i&16777215)||s!==this.length-1?l[6-u.length]+u+n:u+n,(i+=2)>=26&&(i-=26,s--)}for(0!==o&&(n=o.toString(16)+n);n.length%t!=0;)n="0"+n;return 0!==this.negative&&(n="-"+n),n}if(e===(0|e)&&e>=2&&e<=36){var c=d[e],f=h[e];n="";var p=this.clone();for(p.negative=0;!p.isZero();){var v=p.modrn(f).toString(e);n=(p=p.idivn(f)).isZero()?v+n:l[c-v.length]+v+n}for(this.isZero()&&(n="0"+n);n.length%t!=0;)n="0"+n;return 0!==this.negative&&(n="-"+n),n}r(!1,"Base should be between 2 and 36")},o.prototype.toNumber=function(){var e=this.words[0];return 2===this.length?e+=67108864*this.words[1]:3===this.length&&1===this.words[2]?e+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-e:e},o.prototype.toJSON=function(){return this.toString(16,2)},s&&(o.prototype.toBuffer=function(e,t){return this.toArrayLike(s,e,t)}),o.prototype.toArray=function(e,t){return this.toArrayLike(Array,e,t)};function p(e,t,n){n.negative=t.negative^e.negative;var r=e.length+t.length|0;n.length=r,r=r-1|0;var i=0|e.words[0],o=0|t.words[0],s=i*o,a=67108863&s,u=s/67108864|0;n.words[0]=a;for(var c=1;c<r;c++){for(var f=u>>>26,l=67108863&u,d=Math.min(c,t.length-1),h=Math.max(0,c-e.length+1);h<=d;h++){var p=c-h|0;f+=(s=(i=0|e.words[p])*(o=0|t.words[h])+l)/67108864|0,l=67108863&s}n.words[c]=0|l,u=0|f}return 0!==u?n.words[c]=0|u:n.length--,n._strip()}o.prototype.toArrayLike=function(e,t,n){this._strip();var i=this.byteLength(),o=n||Math.max(1,i);r(i<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0");var s=function(e,t){return e.allocUnsafe?e.allocUnsafe(t):new e(t)}(e,o);return this["_toArrayLike"+("le"===t?"LE":"BE")](s,i),s},o.prototype._toArrayLikeLE=function(e,t){for(var n=0,r=0,i=0,o=0;i<this.length;i++){var s=this.words[i]<<o|r;e[n++]=255&s,n<e.length&&(e[n++]=s>>8&255),n<e.length&&(e[n++]=s>>16&255),6===o?(n<e.length&&(e[n++]=s>>24&255),r=0,o=0):(r=s>>>24,o+=2)}if(n<e.length)for(e[n++]=r;n<e.length;)e[n++]=0},o.prototype._toArrayLikeBE=function(e,t){for(var n=e.length-1,r=0,i=0,o=0;i<this.length;i++){var s=this.words[i]<<o|r;e[n--]=255&s,n>=0&&(e[n--]=s>>8&255),n>=0&&(e[n--]=s>>16&255),6===o?(n>=0&&(e[n--]=s>>24&255),r=0,o=0):(r=s>>>24,o+=2)}if(n>=0)for(e[n--]=r;n>=0;)e[n--]=0},Math.clz32?o.prototype._countBits=function(e){return 32-Math.clz32(e)}:o.prototype._countBits=function(e){var t=e,n=0;return t>=4096&&(n+=13,t>>>=13),t>=64&&(n+=7,t>>>=7),t>=8&&(n+=4,t>>>=4),t>=2&&(n+=2,t>>>=2),n+t},o.prototype._zeroBits=function(e){if(0===e)return 26;var t=e,n=0;return 0==(8191&t)&&(n+=13,t>>>=13),0==(127&t)&&(n+=7,t>>>=7),0==(15&t)&&(n+=4,t>>>=4),0==(3&t)&&(n+=2,t>>>=2),0==(1&t)&&n++,n},o.prototype.bitLength=function(){var e=this.words[this.length-1],t=this._countBits(e);return 26*(this.length-1)+t},o.prototype.zeroBits=function(){if(this.isZero())return 0;for(var e=0,t=0;t<this.length;t++){var n=this._zeroBits(this.words[t]);if(e+=n,26!==n)break}return e},o.prototype.byteLength=function(){return Math.ceil(this.bitLength()/8)},o.prototype.toTwos=function(e){return 0!==this.negative?this.abs().inotn(e).iaddn(1):this.clone()},o.prototype.fromTwos=function(e){return this.testn(e-1)?this.notn(e).iaddn(1).ineg():this.clone()},o.prototype.isNeg=function(){return 0!==this.negative},o.prototype.neg=function(){return this.clone().ineg()},o.prototype.ineg=function(){return this.isZero()||(this.negative^=1),this},o.prototype.iuor=function(e){for(;this.length<e.length;)this.words[this.length++]=0;for(var t=0;t<e.length;t++)this.words[t]=this.words[t]|e.words[t];return this._strip()},o.prototype.ior=function(e){return r(0==(this.negative|e.negative)),this.iuor(e)},o.prototype.or=function(e){return this.length>e.length?this.clone().ior(e):e.clone().ior(this)},o.prototype.uor=function(e){return this.length>e.length?this.clone().iuor(e):e.clone().iuor(this)},o.prototype.iuand=function(e){var t;t=this.length>e.length?e:this;for(var n=0;n<t.length;n++)this.words[n]=this.words[n]&e.words[n];return this.length=t.length,this._strip()},o.prototype.iand=function(e){return r(0==(this.negative|e.negative)),this.iuand(e)},o.prototype.and=function(e){return this.length>e.length?this.clone().iand(e):e.clone().iand(this)},o.prototype.uand=function(e){return this.length>e.length?this.clone().iuand(e):e.clone().iuand(this)},o.prototype.iuxor=function(e){var t,n;this.length>e.length?(t=this,n=e):(t=e,n=this);for(var r=0;r<n.length;r++)this.words[r]=t.words[r]^n.words[r];if(this!==t)for(;r<t.length;r++)this.words[r]=t.words[r];return this.length=t.length,this._strip()},o.prototype.ixor=function(e){return r(0==(this.negative|e.negative)),this.iuxor(e)},o.prototype.xor=function(e){return this.length>e.length?this.clone().ixor(e):e.clone().ixor(this)},o.prototype.uxor=function(e){return this.length>e.length?this.clone().iuxor(e):e.clone().iuxor(this)},o.prototype.inotn=function(e){r("number"==typeof e&&e>=0);var t=0|Math.ceil(e/26),n=e%26;this._expand(t),n>0&&t--;for(var i=0;i<t;i++)this.words[i]=67108863&~this.words[i];return n>0&&(this.words[i]=~this.words[i]&67108863>>26-n),this._strip()},o.prototype.notn=function(e){return this.clone().inotn(e)},o.prototype.setn=function(e,t){r("number"==typeof e&&e>=0);var n=e/26|0,i=e%26;return this._expand(n+1),this.words[n]=t?this.words[n]|1<<i:this.words[n]&~(1<<i),this._strip()},o.prototype.iadd=function(e){var t,n,r;if(0!==this.negative&&0===e.negative)return this.negative=0,t=this.isub(e),this.negative^=1,this._normSign();if(0===this.negative&&0!==e.negative)return e.negative=0,t=this.isub(e),e.negative=1,t._normSign();this.length>e.length?(n=this,r=e):(n=e,r=this);for(var i=0,o=0;o<r.length;o++)t=(0|n.words[o])+(0|r.words[o])+i,this.words[o]=67108863&t,i=t>>>26;for(;0!==i&&o<n.length;o++)t=(0|n.words[o])+i,this.words[o]=67108863&t,i=t>>>26;if(this.length=n.length,0!==i)this.words[this.length]=i,this.length++;else if(n!==this)for(;o<n.length;o++)this.words[o]=n.words[o];return this},o.prototype.add=function(e){var t;return 0!==e.negative&&0===this.negative?(e.negative=0,t=this.sub(e),e.negative^=1,t):0===e.negative&&0!==this.negative?(this.negative=0,t=e.sub(this),this.negative=1,t):this.length>e.length?this.clone().iadd(e):e.clone().iadd(this)},o.prototype.isub=function(e){if(0!==e.negative){e.negative=0;var t=this.iadd(e);return e.negative=1,t._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(e),this.negative=1,this._normSign();var n,r,i=this.cmp(e);if(0===i)return this.negative=0,this.length=1,this.words[0]=0,this;i>0?(n=this,r=e):(n=e,r=this);for(var o=0,s=0;s<r.length;s++)o=(t=(0|n.words[s])-(0|r.words[s])+o)>>26,this.words[s]=67108863&t;for(;0!==o&&s<n.length;s++)o=(t=(0|n.words[s])+o)>>26,this.words[s]=67108863&t;if(0===o&&s<n.length&&n!==this)for(;s<n.length;s++)this.words[s]=n.words[s];return this.length=Math.max(this.length,s),n!==this&&(this.negative=1),this._strip()},o.prototype.sub=function(e){return this.clone().isub(e)};var v=function(e,t,n){var r,i,o,s=e.words,a=t.words,u=n.words,c=0,f=0|s[0],l=8191&f,d=f>>>13,h=0|s[1],p=8191&h,v=h>>>13,g=0|s[2],m=8191&g,b=g>>>13,y=0|s[3],w=8191&y,_=y>>>13,S=0|s[4],E=8191&S,M=S>>>13,A=0|s[5],I=8191&A,k=A>>>13,O=0|s[6],x=8191&O,C=O>>>13,T=0|s[7],P=8191&T,N=T>>>13,R=0|s[8],L=8191&R,j=R>>>13,D=0|s[9],U=8191&D,B=D>>>13,F=0|a[0],z=8191&F,q=F>>>13,K=0|a[1],H=8191&K,V=K>>>13,G=0|a[2],W=8191&G,$=G>>>13,Y=0|a[3],J=8191&Y,Z=Y>>>13,X=0|a[4],Q=8191&X,ee=X>>>13,te=0|a[5],ne=8191&te,re=te>>>13,ie=0|a[6],oe=8191&ie,se=ie>>>13,ae=0|a[7],ue=8191&ae,ce=ae>>>13,fe=0|a[8],le=8191&fe,de=fe>>>13,he=0|a[9],pe=8191&he,ve=he>>>13;n.negative=e.negative^t.negative,n.length=19;var ge=(c+(r=Math.imul(l,z))|0)+((8191&(i=(i=Math.imul(l,q))+Math.imul(d,z)|0))<<13)|0;c=((o=Math.imul(d,q))+(i>>>13)|0)+(ge>>>26)|0,ge&=67108863,r=Math.imul(p,z),i=(i=Math.imul(p,q))+Math.imul(v,z)|0,o=Math.imul(v,q);var me=(c+(r=r+Math.imul(l,H)|0)|0)+((8191&(i=(i=i+Math.imul(l,V)|0)+Math.imul(d,H)|0))<<13)|0;c=((o=o+Math.imul(d,V)|0)+(i>>>13)|0)+(me>>>26)|0,me&=67108863,r=Math.imul(m,z),i=(i=Math.imul(m,q))+Math.imul(b,z)|0,o=Math.imul(b,q),r=r+Math.imul(p,H)|0,i=(i=i+Math.imul(p,V)|0)+Math.imul(v,H)|0,o=o+Math.imul(v,V)|0;var be=(c+(r=r+Math.imul(l,W)|0)|0)+((8191&(i=(i=i+Math.imul(l,$)|0)+Math.imul(d,W)|0))<<13)|0;c=((o=o+Math.imul(d,$)|0)+(i>>>13)|0)+(be>>>26)|0,be&=67108863,r=Math.imul(w,z),i=(i=Math.imul(w,q))+Math.imul(_,z)|0,o=Math.imul(_,q),r=r+Math.imul(m,H)|0,i=(i=i+Math.imul(m,V)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,V)|0,r=r+Math.imul(p,W)|0,i=(i=i+Math.imul(p,$)|0)+Math.imul(v,W)|0,o=o+Math.imul(v,$)|0;var ye=(c+(r=r+Math.imul(l,J)|0)|0)+((8191&(i=(i=i+Math.imul(l,Z)|0)+Math.imul(d,J)|0))<<13)|0;c=((o=o+Math.imul(d,Z)|0)+(i>>>13)|0)+(ye>>>26)|0,ye&=67108863,r=Math.imul(E,z),i=(i=Math.imul(E,q))+Math.imul(M,z)|0,o=Math.imul(M,q),r=r+Math.imul(w,H)|0,i=(i=i+Math.imul(w,V)|0)+Math.imul(_,H)|0,o=o+Math.imul(_,V)|0,r=r+Math.imul(m,W)|0,i=(i=i+Math.imul(m,$)|0)+Math.imul(b,W)|0,o=o+Math.imul(b,$)|0,r=r+Math.imul(p,J)|0,i=(i=i+Math.imul(p,Z)|0)+Math.imul(v,J)|0,o=o+Math.imul(v,Z)|0;var we=(c+(r=r+Math.imul(l,Q)|0)|0)+((8191&(i=(i=i+Math.imul(l,ee)|0)+Math.imul(d,Q)|0))<<13)|0;c=((o=o+Math.imul(d,ee)|0)+(i>>>13)|0)+(we>>>26)|0,we&=67108863,r=Math.imul(I,z),i=(i=Math.imul(I,q))+Math.imul(k,z)|0,o=Math.imul(k,q),r=r+Math.imul(E,H)|0,i=(i=i+Math.imul(E,V)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,V)|0,r=r+Math.imul(w,W)|0,i=(i=i+Math.imul(w,$)|0)+Math.imul(_,W)|0,o=o+Math.imul(_,$)|0,r=r+Math.imul(m,J)|0,i=(i=i+Math.imul(m,Z)|0)+Math.imul(b,J)|0,o=o+Math.imul(b,Z)|0,r=r+Math.imul(p,Q)|0,i=(i=i+Math.imul(p,ee)|0)+Math.imul(v,Q)|0,o=o+Math.imul(v,ee)|0;var _e=(c+(r=r+Math.imul(l,ne)|0)|0)+((8191&(i=(i=i+Math.imul(l,re)|0)+Math.imul(d,ne)|0))<<13)|0;c=((o=o+Math.imul(d,re)|0)+(i>>>13)|0)+(_e>>>26)|0,_e&=67108863,r=Math.imul(x,z),i=(i=Math.imul(x,q))+Math.imul(C,z)|0,o=Math.imul(C,q),r=r+Math.imul(I,H)|0,i=(i=i+Math.imul(I,V)|0)+Math.imul(k,H)|0,o=o+Math.imul(k,V)|0,r=r+Math.imul(E,W)|0,i=(i=i+Math.imul(E,$)|0)+Math.imul(M,W)|0,o=o+Math.imul(M,$)|0,r=r+Math.imul(w,J)|0,i=(i=i+Math.imul(w,Z)|0)+Math.imul(_,J)|0,o=o+Math.imul(_,Z)|0,r=r+Math.imul(m,Q)|0,i=(i=i+Math.imul(m,ee)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,ee)|0,r=r+Math.imul(p,ne)|0,i=(i=i+Math.imul(p,re)|0)+Math.imul(v,ne)|0,o=o+Math.imul(v,re)|0;var Se=(c+(r=r+Math.imul(l,oe)|0)|0)+((8191&(i=(i=i+Math.imul(l,se)|0)+Math.imul(d,oe)|0))<<13)|0;c=((o=o+Math.imul(d,se)|0)+(i>>>13)|0)+(Se>>>26)|0,Se&=67108863,r=Math.imul(P,z),i=(i=Math.imul(P,q))+Math.imul(N,z)|0,o=Math.imul(N,q),r=r+Math.imul(x,H)|0,i=(i=i+Math.imul(x,V)|0)+Math.imul(C,H)|0,o=o+Math.imul(C,V)|0,r=r+Math.imul(I,W)|0,i=(i=i+Math.imul(I,$)|0)+Math.imul(k,W)|0,o=o+Math.imul(k,$)|0,r=r+Math.imul(E,J)|0,i=(i=i+Math.imul(E,Z)|0)+Math.imul(M,J)|0,o=o+Math.imul(M,Z)|0,r=r+Math.imul(w,Q)|0,i=(i=i+Math.imul(w,ee)|0)+Math.imul(_,Q)|0,o=o+Math.imul(_,ee)|0,r=r+Math.imul(m,ne)|0,i=(i=i+Math.imul(m,re)|0)+Math.imul(b,ne)|0,o=o+Math.imul(b,re)|0,r=r+Math.imul(p,oe)|0,i=(i=i+Math.imul(p,se)|0)+Math.imul(v,oe)|0,o=o+Math.imul(v,se)|0;var Ee=(c+(r=r+Math.imul(l,ue)|0)|0)+((8191&(i=(i=i+Math.imul(l,ce)|0)+Math.imul(d,ue)|0))<<13)|0;c=((o=o+Math.imul(d,ce)|0)+(i>>>13)|0)+(Ee>>>26)|0,Ee&=67108863,r=Math.imul(L,z),i=(i=Math.imul(L,q))+Math.imul(j,z)|0,o=Math.imul(j,q),r=r+Math.imul(P,H)|0,i=(i=i+Math.imul(P,V)|0)+Math.imul(N,H)|0,o=o+Math.imul(N,V)|0,r=r+Math.imul(x,W)|0,i=(i=i+Math.imul(x,$)|0)+Math.imul(C,W)|0,o=o+Math.imul(C,$)|0,r=r+Math.imul(I,J)|0,i=(i=i+Math.imul(I,Z)|0)+Math.imul(k,J)|0,o=o+Math.imul(k,Z)|0,r=r+Math.imul(E,Q)|0,i=(i=i+Math.imul(E,ee)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,ee)|0,r=r+Math.imul(w,ne)|0,i=(i=i+Math.imul(w,re)|0)+Math.imul(_,ne)|0,o=o+Math.imul(_,re)|0,r=r+Math.imul(m,oe)|0,i=(i=i+Math.imul(m,se)|0)+Math.imul(b,oe)|0,o=o+Math.imul(b,se)|0,r=r+Math.imul(p,ue)|0,i=(i=i+Math.imul(p,ce)|0)+Math.imul(v,ue)|0,o=o+Math.imul(v,ce)|0;var Me=(c+(r=r+Math.imul(l,le)|0)|0)+((8191&(i=(i=i+Math.imul(l,de)|0)+Math.imul(d,le)|0))<<13)|0;c=((o=o+Math.imul(d,de)|0)+(i>>>13)|0)+(Me>>>26)|0,Me&=67108863,r=Math.imul(U,z),i=(i=Math.imul(U,q))+Math.imul(B,z)|0,o=Math.imul(B,q),r=r+Math.imul(L,H)|0,i=(i=i+Math.imul(L,V)|0)+Math.imul(j,H)|0,o=o+Math.imul(j,V)|0,r=r+Math.imul(P,W)|0,i=(i=i+Math.imul(P,$)|0)+Math.imul(N,W)|0,o=o+Math.imul(N,$)|0,r=r+Math.imul(x,J)|0,i=(i=i+Math.imul(x,Z)|0)+Math.imul(C,J)|0,o=o+Math.imul(C,Z)|0,r=r+Math.imul(I,Q)|0,i=(i=i+Math.imul(I,ee)|0)+Math.imul(k,Q)|0,o=o+Math.imul(k,ee)|0,r=r+Math.imul(E,ne)|0,i=(i=i+Math.imul(E,re)|0)+Math.imul(M,ne)|0,o=o+Math.imul(M,re)|0,r=r+Math.imul(w,oe)|0,i=(i=i+Math.imul(w,se)|0)+Math.imul(_,oe)|0,o=o+Math.imul(_,se)|0,r=r+Math.imul(m,ue)|0,i=(i=i+Math.imul(m,ce)|0)+Math.imul(b,ue)|0,o=o+Math.imul(b,ce)|0,r=r+Math.imul(p,le)|0,i=(i=i+Math.imul(p,de)|0)+Math.imul(v,le)|0,o=o+Math.imul(v,de)|0;var Ae=(c+(r=r+Math.imul(l,pe)|0)|0)+((8191&(i=(i=i+Math.imul(l,ve)|0)+Math.imul(d,pe)|0))<<13)|0;c=((o=o+Math.imul(d,ve)|0)+(i>>>13)|0)+(Ae>>>26)|0,Ae&=67108863,r=Math.imul(U,H),i=(i=Math.imul(U,V))+Math.imul(B,H)|0,o=Math.imul(B,V),r=r+Math.imul(L,W)|0,i=(i=i+Math.imul(L,$)|0)+Math.imul(j,W)|0,o=o+Math.imul(j,$)|0,r=r+Math.imul(P,J)|0,i=(i=i+Math.imul(P,Z)|0)+Math.imul(N,J)|0,o=o+Math.imul(N,Z)|0,r=r+Math.imul(x,Q)|0,i=(i=i+Math.imul(x,ee)|0)+Math.imul(C,Q)|0,o=o+Math.imul(C,ee)|0,r=r+Math.imul(I,ne)|0,i=(i=i+Math.imul(I,re)|0)+Math.imul(k,ne)|0,o=o+Math.imul(k,re)|0,r=r+Math.imul(E,oe)|0,i=(i=i+Math.imul(E,se)|0)+Math.imul(M,oe)|0,o=o+Math.imul(M,se)|0,r=r+Math.imul(w,ue)|0,i=(i=i+Math.imul(w,ce)|0)+Math.imul(_,ue)|0,o=o+Math.imul(_,ce)|0,r=r+Math.imul(m,le)|0,i=(i=i+Math.imul(m,de)|0)+Math.imul(b,le)|0,o=o+Math.imul(b,de)|0;var Ie=(c+(r=r+Math.imul(p,pe)|0)|0)+((8191&(i=(i=i+Math.imul(p,ve)|0)+Math.imul(v,pe)|0))<<13)|0;c=((o=o+Math.imul(v,ve)|0)+(i>>>13)|0)+(Ie>>>26)|0,Ie&=67108863,r=Math.imul(U,W),i=(i=Math.imul(U,$))+Math.imul(B,W)|0,o=Math.imul(B,$),r=r+Math.imul(L,J)|0,i=(i=i+Math.imul(L,Z)|0)+Math.imul(j,J)|0,o=o+Math.imul(j,Z)|0,r=r+Math.imul(P,Q)|0,i=(i=i+Math.imul(P,ee)|0)+Math.imul(N,Q)|0,o=o+Math.imul(N,ee)|0,r=r+Math.imul(x,ne)|0,i=(i=i+Math.imul(x,re)|0)+Math.imul(C,ne)|0,o=o+Math.imul(C,re)|0,r=r+Math.imul(I,oe)|0,i=(i=i+Math.imul(I,se)|0)+Math.imul(k,oe)|0,o=o+Math.imul(k,se)|0,r=r+Math.imul(E,ue)|0,i=(i=i+Math.imul(E,ce)|0)+Math.imul(M,ue)|0,o=o+Math.imul(M,ce)|0,r=r+Math.imul(w,le)|0,i=(i=i+Math.imul(w,de)|0)+Math.imul(_,le)|0,o=o+Math.imul(_,de)|0;var ke=(c+(r=r+Math.imul(m,pe)|0)|0)+((8191&(i=(i=i+Math.imul(m,ve)|0)+Math.imul(b,pe)|0))<<13)|0;c=((o=o+Math.imul(b,ve)|0)+(i>>>13)|0)+(ke>>>26)|0,ke&=67108863,r=Math.imul(U,J),i=(i=Math.imul(U,Z))+Math.imul(B,J)|0,o=Math.imul(B,Z),r=r+Math.imul(L,Q)|0,i=(i=i+Math.imul(L,ee)|0)+Math.imul(j,Q)|0,o=o+Math.imul(j,ee)|0,r=r+Math.imul(P,ne)|0,i=(i=i+Math.imul(P,re)|0)+Math.imul(N,ne)|0,o=o+Math.imul(N,re)|0,r=r+Math.imul(x,oe)|0,i=(i=i+Math.imul(x,se)|0)+Math.imul(C,oe)|0,o=o+Math.imul(C,se)|0,r=r+Math.imul(I,ue)|0,i=(i=i+Math.imul(I,ce)|0)+Math.imul(k,ue)|0,o=o+Math.imul(k,ce)|0,r=r+Math.imul(E,le)|0,i=(i=i+Math.imul(E,de)|0)+Math.imul(M,le)|0,o=o+Math.imul(M,de)|0;var Oe=(c+(r=r+Math.imul(w,pe)|0)|0)+((8191&(i=(i=i+Math.imul(w,ve)|0)+Math.imul(_,pe)|0))<<13)|0;c=((o=o+Math.imul(_,ve)|0)+(i>>>13)|0)+(Oe>>>26)|0,Oe&=67108863,r=Math.imul(U,Q),i=(i=Math.imul(U,ee))+Math.imul(B,Q)|0,o=Math.imul(B,ee),r=r+Math.imul(L,ne)|0,i=(i=i+Math.imul(L,re)|0)+Math.imul(j,ne)|0,o=o+Math.imul(j,re)|0,r=r+Math.imul(P,oe)|0,i=(i=i+Math.imul(P,se)|0)+Math.imul(N,oe)|0,o=o+Math.imul(N,se)|0,r=r+Math.imul(x,ue)|0,i=(i=i+Math.imul(x,ce)|0)+Math.imul(C,ue)|0,o=o+Math.imul(C,ce)|0,r=r+Math.imul(I,le)|0,i=(i=i+Math.imul(I,de)|0)+Math.imul(k,le)|0,o=o+Math.imul(k,de)|0;var xe=(c+(r=r+Math.imul(E,pe)|0)|0)+((8191&(i=(i=i+Math.imul(E,ve)|0)+Math.imul(M,pe)|0))<<13)|0;c=((o=o+Math.imul(M,ve)|0)+(i>>>13)|0)+(xe>>>26)|0,xe&=67108863,r=Math.imul(U,ne),i=(i=Math.imul(U,re))+Math.imul(B,ne)|0,o=Math.imul(B,re),r=r+Math.imul(L,oe)|0,i=(i=i+Math.imul(L,se)|0)+Math.imul(j,oe)|0,o=o+Math.imul(j,se)|0,r=r+Math.imul(P,ue)|0,i=(i=i+Math.imul(P,ce)|0)+Math.imul(N,ue)|0,o=o+Math.imul(N,ce)|0,r=r+Math.imul(x,le)|0,i=(i=i+Math.imul(x,de)|0)+Math.imul(C,le)|0,o=o+Math.imul(C,de)|0;var Ce=(c+(r=r+Math.imul(I,pe)|0)|0)+((8191&(i=(i=i+Math.imul(I,ve)|0)+Math.imul(k,pe)|0))<<13)|0;c=((o=o+Math.imul(k,ve)|0)+(i>>>13)|0)+(Ce>>>26)|0,Ce&=67108863,r=Math.imul(U,oe),i=(i=Math.imul(U,se))+Math.imul(B,oe)|0,o=Math.imul(B,se),r=r+Math.imul(L,ue)|0,i=(i=i+Math.imul(L,ce)|0)+Math.imul(j,ue)|0,o=o+Math.imul(j,ce)|0,r=r+Math.imul(P,le)|0,i=(i=i+Math.imul(P,de)|0)+Math.imul(N,le)|0,o=o+Math.imul(N,de)|0;var Te=(c+(r=r+Math.imul(x,pe)|0)|0)+((8191&(i=(i=i+Math.imul(x,ve)|0)+Math.imul(C,pe)|0))<<13)|0;c=((o=o+Math.imul(C,ve)|0)+(i>>>13)|0)+(Te>>>26)|0,Te&=67108863,r=Math.imul(U,ue),i=(i=Math.imul(U,ce))+Math.imul(B,ue)|0,o=Math.imul(B,ce),r=r+Math.imul(L,le)|0,i=(i=i+Math.imul(L,de)|0)+Math.imul(j,le)|0,o=o+Math.imul(j,de)|0;var Pe=(c+(r=r+Math.imul(P,pe)|0)|0)+((8191&(i=(i=i+Math.imul(P,ve)|0)+Math.imul(N,pe)|0))<<13)|0;c=((o=o+Math.imul(N,ve)|0)+(i>>>13)|0)+(Pe>>>26)|0,Pe&=67108863,r=Math.imul(U,le),i=(i=Math.imul(U,de))+Math.imul(B,le)|0,o=Math.imul(B,de);var Ne=(c+(r=r+Math.imul(L,pe)|0)|0)+((8191&(i=(i=i+Math.imul(L,ve)|0)+Math.imul(j,pe)|0))<<13)|0;c=((o=o+Math.imul(j,ve)|0)+(i>>>13)|0)+(Ne>>>26)|0,Ne&=67108863;var Re=(c+(r=Math.imul(U,pe))|0)+((8191&(i=(i=Math.imul(U,ve))+Math.imul(B,pe)|0))<<13)|0;return c=((o=Math.imul(B,ve))+(i>>>13)|0)+(Re>>>26)|0,Re&=67108863,u[0]=ge,u[1]=me,u[2]=be,u[3]=ye,u[4]=we,u[5]=_e,u[6]=Se,u[7]=Ee,u[8]=Me,u[9]=Ae,u[10]=Ie,u[11]=ke,u[12]=Oe,u[13]=xe,u[14]=Ce,u[15]=Te,u[16]=Pe,u[17]=Ne,u[18]=Re,0!==c&&(u[19]=c,n.length++),n};function g(e,t,n){n.negative=t.negative^e.negative,n.length=e.length+t.length;for(var r=0,i=0,o=0;o<n.length-1;o++){var s=i;i=0;for(var a=67108863&r,u=Math.min(o,t.length-1),c=Math.max(0,o-e.length+1);c<=u;c++){var f=o-c,l=(0|e.words[f])*(0|t.words[c]),d=67108863&l;a=67108863&(d=d+a|0),i+=(s=(s=s+(l/67108864|0)|0)+(d>>>26)|0)>>>26,s&=67108863}n.words[o]=a,r=s,s=i}return 0!==r?n.words[o]=r:n.length--,n._strip()}function m(e,t,n){return g(e,t,n)}function b(e,t){this.x=e,this.y=t}Math.imul||(v=p),o.prototype.mulTo=function(e,t){var n=this.length+e.length;return 10===this.length&&10===e.length?v(this,e,t):n<63?p(this,e,t):n<1024?g(this,e,t):m(this,e,t)},b.prototype.makeRBT=function(e){for(var t=new Array(e),n=o.prototype._countBits(e)-1,r=0;r<e;r++)t[r]=this.revBin(r,n,e);return t},b.prototype.revBin=function(e,t,n){if(0===e||e===n-1)return e;for(var r=0,i=0;i<t;i++)r|=(1&e)<<t-i-1,e>>=1;return r},b.prototype.permute=function(e,t,n,r,i,o){for(var s=0;s<o;s++)r[s]=t[e[s]],i[s]=n[e[s]]},b.prototype.transform=function(e,t,n,r,i,o){this.permute(o,e,t,n,r,i);for(var s=1;s<i;s<<=1)for(var a=s<<1,u=Math.cos(2*Math.PI/a),c=Math.sin(2*Math.PI/a),f=0;f<i;f+=a)for(var l=u,d=c,h=0;h<s;h++){var p=n[f+h],v=r[f+h],g=n[f+h+s],m=r[f+h+s],b=l*g-d*m;m=l*m+d*g,g=b,n[f+h]=p+g,r[f+h]=v+m,n[f+h+s]=p-g,r[f+h+s]=v-m,h!==a&&(b=u*l-c*d,d=u*d+c*l,l=b)}},b.prototype.guessLen13b=function(e,t){var n=1|Math.max(t,e),r=1&n,i=0;for(n=n/2|0;n;n>>>=1)i++;return 1<<i+1+r},b.prototype.conjugate=function(e,t,n){if(!(n<=1))for(var r=0;r<n/2;r++){var i=e[r];e[r]=e[n-r-1],e[n-r-1]=i,i=t[r],t[r]=-t[n-r-1],t[n-r-1]=-i}},b.prototype.normalize13b=function(e,t){for(var n=0,r=0;r<t/2;r++){var i=8192*Math.round(e[2*r+1]/t)+Math.round(e[2*r]/t)+n;e[r]=67108863&i,n=i<67108864?0:i/67108864|0}return e},b.prototype.convert13b=function(e,t,n,i){for(var o=0,s=0;s<t;s++)o+=0|e[s],n[2*s]=8191&o,o>>>=13,n[2*s+1]=8191&o,o>>>=13;for(s=2*t;s<i;++s)n[s]=0;r(0===o),r(0==(-8192&o))},b.prototype.stub=function(e){for(var t=new Array(e),n=0;n<e;n++)t[n]=0;return t},b.prototype.mulp=function(e,t,n){var r=2*this.guessLen13b(e.length,t.length),i=this.makeRBT(r),o=this.stub(r),s=new Array(r),a=new Array(r),u=new Array(r),c=new Array(r),f=new Array(r),l=new Array(r),d=n.words;d.length=r,this.convert13b(e.words,e.length,s,r),this.convert13b(t.words,t.length,c,r),this.transform(s,o,a,u,r,i),this.transform(c,o,f,l,r,i);for(var h=0;h<r;h++){var p=a[h]*f[h]-u[h]*l[h];u[h]=a[h]*l[h]+u[h]*f[h],a[h]=p}return this.conjugate(a,u,r),this.transform(a,u,d,o,r,i),this.conjugate(d,o,r),this.normalize13b(d,r),n.negative=e.negative^t.negative,n.length=e.length+t.length,n._strip()},o.prototype.mul=function(e){var t=new o(null);return t.words=new Array(this.length+e.length),this.mulTo(e,t)},o.prototype.mulf=function(e){var t=new o(null);return t.words=new Array(this.length+e.length),m(this,e,t)},o.prototype.imul=function(e){return this.clone().mulTo(e,this)},o.prototype.imuln=function(e){var t=e<0;t&&(e=-e),r("number"==typeof e),r(e<67108864);for(var n=0,i=0;i<this.length;i++){var o=(0|this.words[i])*e,s=(67108863&o)+(67108863&n);n>>=26,n+=o/67108864|0,n+=s>>>26,this.words[i]=67108863&s}return 0!==n&&(this.words[i]=n,this.length++),t?this.ineg():this},o.prototype.muln=function(e){return this.clone().imuln(e)},o.prototype.sqr=function(){return this.mul(this)},o.prototype.isqr=function(){return this.imul(this.clone())},o.prototype.pow=function(e){var t=function(e){for(var t=new Array(e.bitLength()),n=0;n<t.length;n++){var r=n/26|0,i=n%26;t[n]=e.words[r]>>>i&1}return t}(e);if(0===t.length)return new o(1);for(var n=this,r=0;r<t.length&&0===t[r];r++,n=n.sqr());if(++r<t.length)for(var i=n.sqr();r<t.length;r++,i=i.sqr())0!==t[r]&&(n=n.mul(i));return n},o.prototype.iushln=function(e){r("number"==typeof e&&e>=0);var t,n=e%26,i=(e-n)/26,o=67108863>>>26-n<<26-n;if(0!==n){var s=0;for(t=0;t<this.length;t++){var a=this.words[t]&o,u=(0|this.words[t])-a<<n;this.words[t]=u|s,s=a>>>26-n}s&&(this.words[t]=s,this.length++)}if(0!==i){for(t=this.length-1;t>=0;t--)this.words[t+i]=this.words[t];for(t=0;t<i;t++)this.words[t]=0;this.length+=i}return this._strip()},o.prototype.ishln=function(e){return r(0===this.negative),this.iushln(e)},o.prototype.iushrn=function(e,t,n){var i;r("number"==typeof e&&e>=0),i=t?(t-t%26)/26:0;var o=e%26,s=Math.min((e-o)/26,this.length),a=67108863^67108863>>>o<<o,u=n;if(i-=s,i=Math.max(0,i),u){for(var c=0;c<s;c++)u.words[c]=this.words[c];u.length=s}if(0===s);else if(this.length>s)for(this.length-=s,c=0;c<this.length;c++)this.words[c]=this.words[c+s];else this.words[0]=0,this.length=1;var f=0;for(c=this.length-1;c>=0&&(0!==f||c>=i);c--){var l=0|this.words[c];this.words[c]=f<<26-o|l>>>o,f=l&a}return u&&0!==f&&(u.words[u.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this._strip()},o.prototype.ishrn=function(e,t,n){return r(0===this.negative),this.iushrn(e,t,n)},o.prototype.shln=function(e){return this.clone().ishln(e)},o.prototype.ushln=function(e){return this.clone().iushln(e)},o.prototype.shrn=function(e){return this.clone().ishrn(e)},o.prototype.ushrn=function(e){return this.clone().iushrn(e)},o.prototype.testn=function(e){r("number"==typeof e&&e>=0);var t=e%26,n=(e-t)/26,i=1<<t;return!(this.length<=n)&&!!(this.words[n]&i)},o.prototype.imaskn=function(e){r("number"==typeof e&&e>=0);var t=e%26,n=(e-t)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=n)return this;if(0!==t&&n++,this.length=Math.min(n,this.length),0!==t){var i=67108863^67108863>>>t<<t;this.words[this.length-1]&=i}return this._strip()},o.prototype.maskn=function(e){return this.clone().imaskn(e)},o.prototype.iaddn=function(e){return r("number"==typeof e),r(e<67108864),e<0?this.isubn(-e):0!==this.negative?1===this.length&&(0|this.words[0])<=e?(this.words[0]=e-(0|this.words[0]),this.negative=0,this):(this.negative=0,this.isubn(e),this.negative=1,this):this._iaddn(e)},o.prototype._iaddn=function(e){this.words[0]+=e;for(var t=0;t<this.length&&this.words[t]>=67108864;t++)this.words[t]-=67108864,t===this.length-1?this.words[t+1]=1:this.words[t+1]++;return this.length=Math.max(this.length,t+1),this},o.prototype.isubn=function(e){if(r("number"==typeof e),r(e<67108864),e<0)return this.iaddn(-e);if(0!==this.negative)return this.negative=0,this.iaddn(e),this.negative=1,this;if(this.words[0]-=e,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var t=0;t<this.length&&this.words[t]<0;t++)this.words[t]+=67108864,this.words[t+1]-=1;return this._strip()},o.prototype.addn=function(e){return this.clone().iaddn(e)},o.prototype.subn=function(e){return this.clone().isubn(e)},o.prototype.iabs=function(){return this.negative=0,this},o.prototype.abs=function(){return this.clone().iabs()},o.prototype._ishlnsubmul=function(e,t,n){var i,o,s=e.length+n;this._expand(s);var a=0;for(i=0;i<e.length;i++){o=(0|this.words[i+n])+a;var u=(0|e.words[i])*t;a=((o-=67108863&u)>>26)-(u/67108864|0),this.words[i+n]=67108863&o}for(;i<this.length-n;i++)a=(o=(0|this.words[i+n])+a)>>26,this.words[i+n]=67108863&o;if(0===a)return this._strip();for(r(-1===a),a=0,i=0;i<this.length;i++)a=(o=-(0|this.words[i])+a)>>26,this.words[i]=67108863&o;return this.negative=1,this._strip()},o.prototype._wordDiv=function(e,t){var n=(this.length,e.length),r=this.clone(),i=e,s=0|i.words[i.length-1];0!==(n=26-this._countBits(s))&&(i=i.ushln(n),r.iushln(n),s=0|i.words[i.length-1]);var a,u=r.length-i.length;if("mod"!==t){(a=new o(null)).length=u+1,a.words=new Array(a.length);for(var c=0;c<a.length;c++)a.words[c]=0}var f=r.clone()._ishlnsubmul(i,1,u);0===f.negative&&(r=f,a&&(a.words[u]=1));for(var l=u-1;l>=0;l--){var d=67108864*(0|r.words[i.length+l])+(0|r.words[i.length+l-1]);for(d=Math.min(d/s|0,67108863),r._ishlnsubmul(i,d,l);0!==r.negative;)d--,r.negative=0,r._ishlnsubmul(i,1,l),r.isZero()||(r.negative^=1);a&&(a.words[l]=d)}return a&&a._strip(),r._strip(),"div"!==t&&0!==n&&r.iushrn(n),{div:a||null,mod:r}},o.prototype.divmod=function(e,t,n){return r(!e.isZero()),this.isZero()?{div:new o(0),mod:new o(0)}:0!==this.negative&&0===e.negative?(a=this.neg().divmod(e,t),"mod"!==t&&(i=a.div.neg()),"div"!==t&&(s=a.mod.neg(),n&&0!==s.negative&&s.iadd(e)),{div:i,mod:s}):0===this.negative&&0!==e.negative?(a=this.divmod(e.neg(),t),"mod"!==t&&(i=a.div.neg()),{div:i,mod:a.mod}):0!=(this.negative&e.negative)?(a=this.neg().divmod(e.neg(),t),"div"!==t&&(s=a.mod.neg(),n&&0!==s.negative&&s.isub(e)),{div:a.div,mod:s}):e.length>this.length||this.cmp(e)<0?{div:new o(0),mod:this}:1===e.length?"div"===t?{div:this.divn(e.words[0]),mod:null}:"mod"===t?{div:null,mod:new o(this.modrn(e.words[0]))}:{div:this.divn(e.words[0]),mod:new o(this.modrn(e.words[0]))}:this._wordDiv(e,t);var i,s,a},o.prototype.div=function(e){return this.divmod(e,"div",!1).div},o.prototype.mod=function(e){return this.divmod(e,"mod",!1).mod},o.prototype.umod=function(e){return this.divmod(e,"mod",!0).mod},o.prototype.divRound=function(e){var t=this.divmod(e);if(t.mod.isZero())return t.div;var n=0!==t.div.negative?t.mod.isub(e):t.mod,r=e.ushrn(1),i=e.andln(1),o=n.cmp(r);return o<0||1===i&&0===o?t.div:0!==t.div.negative?t.div.isubn(1):t.div.iaddn(1)},o.prototype.modrn=function(e){var t=e<0;t&&(e=-e),r(e<=67108863);for(var n=(1<<26)%e,i=0,o=this.length-1;o>=0;o--)i=(n*i+(0|this.words[o]))%e;return t?-i:i},o.prototype.modn=function(e){return this.modrn(e)},o.prototype.idivn=function(e){var t=e<0;t&&(e=-e),r(e<=67108863);for(var n=0,i=this.length-1;i>=0;i--){var o=(0|this.words[i])+67108864*n;this.words[i]=o/e|0,n=o%e}return this._strip(),t?this.ineg():this},o.prototype.divn=function(e){return this.clone().idivn(e)},o.prototype.egcd=function(e){r(0===e.negative),r(!e.isZero());var t=this,n=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i=new o(1),s=new o(0),a=new o(0),u=new o(1),c=0;t.isEven()&&n.isEven();)t.iushrn(1),n.iushrn(1),++c;for(var f=n.clone(),l=t.clone();!t.isZero();){for(var d=0,h=1;0==(t.words[0]&h)&&d<26;++d,h<<=1);if(d>0)for(t.iushrn(d);d-- >0;)(i.isOdd()||s.isOdd())&&(i.iadd(f),s.isub(l)),i.iushrn(1),s.iushrn(1);for(var p=0,v=1;0==(n.words[0]&v)&&p<26;++p,v<<=1);if(p>0)for(n.iushrn(p);p-- >0;)(a.isOdd()||u.isOdd())&&(a.iadd(f),u.isub(l)),a.iushrn(1),u.iushrn(1);t.cmp(n)>=0?(t.isub(n),i.isub(a),s.isub(u)):(n.isub(t),a.isub(i),u.isub(s))}return{a:a,b:u,gcd:n.iushln(c)}},o.prototype._invmp=function(e){r(0===e.negative),r(!e.isZero());var t=this,n=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i,s=new o(1),a=new o(0),u=n.clone();t.cmpn(1)>0&&n.cmpn(1)>0;){for(var c=0,f=1;0==(t.words[0]&f)&&c<26;++c,f<<=1);if(c>0)for(t.iushrn(c);c-- >0;)s.isOdd()&&s.iadd(u),s.iushrn(1);for(var l=0,d=1;0==(n.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(n.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(u),a.iushrn(1);t.cmp(n)>=0?(t.isub(n),s.isub(a)):(n.isub(t),a.isub(s))}return(i=0===t.cmpn(1)?s:a).cmpn(0)<0&&i.iadd(e),i},o.prototype.gcd=function(e){if(this.isZero())return e.abs();if(e.isZero())return this.abs();var t=this.clone(),n=e.clone();t.negative=0,n.negative=0;for(var r=0;t.isEven()&&n.isEven();r++)t.iushrn(1),n.iushrn(1);for(;;){for(;t.isEven();)t.iushrn(1);for(;n.isEven();)n.iushrn(1);var i=t.cmp(n);if(i<0){var o=t;t=n,n=o}else if(0===i||0===n.cmpn(1))break;t.isub(n)}return n.iushln(r)},o.prototype.invm=function(e){return this.egcd(e).a.umod(e)},o.prototype.isEven=function(){return 0==(1&this.words[0])},o.prototype.isOdd=function(){return 1==(1&this.words[0])},o.prototype.andln=function(e){return this.words[0]&e},o.prototype.bincn=function(e){r("number"==typeof e);var t=e%26,n=(e-t)/26,i=1<<t;if(this.length<=n)return this._expand(n+1),this.words[n]|=i,this;for(var o=i,s=n;0!==o&&s<this.length;s++){var a=0|this.words[s];o=(a+=o)>>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},o.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},o.prototype.cmpn=function(e){var t,n=e<0;if(0!==this.negative&&!n)return-1;if(0===this.negative&&n)return 1;if(this._strip(),this.length>1)t=1;else{n&&(e=-e),r(e<=67108863,"Number is too big");var i=0|this.words[0];t=i===e?0:i<e?-1:1}return 0!==this.negative?0|-t:t},o.prototype.cmp=function(e){if(0!==this.negative&&0===e.negative)return-1;if(0===this.negative&&0!==e.negative)return 1;var t=this.ucmp(e);return 0!==this.negative?0|-t:t},o.prototype.ucmp=function(e){if(this.length>e.length)return 1;if(this.length<e.length)return-1;for(var t=0,n=this.length-1;n>=0;n--){var r=0|this.words[n],i=0|e.words[n];if(r!==i){r<i?t=-1:r>i&&(t=1);break}}return t},o.prototype.gtn=function(e){return 1===this.cmpn(e)},o.prototype.gt=function(e){return 1===this.cmp(e)},o.prototype.gten=function(e){return this.cmpn(e)>=0},o.prototype.gte=function(e){return this.cmp(e)>=0},o.prototype.ltn=function(e){return-1===this.cmpn(e)},o.prototype.lt=function(e){return-1===this.cmp(e)},o.prototype.lten=function(e){return this.cmpn(e)<=0},o.prototype.lte=function(e){return this.cmp(e)<=0},o.prototype.eqn=function(e){return 0===this.cmpn(e)},o.prototype.eq=function(e){return 0===this.cmp(e)},o.red=function(e){return new A(e)},o.prototype.toRed=function(e){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),e.convertTo(this)._forceRed(e)},o.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},o.prototype._forceRed=function(e){return this.red=e,this},o.prototype.forceRed=function(e){return r(!this.red,"Already a number in reduction context"),this._forceRed(e)},o.prototype.redAdd=function(e){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,e)},o.prototype.redIAdd=function(e){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,e)},o.prototype.redSub=function(e){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,e)},o.prototype.redISub=function(e){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,e)},o.prototype.redShl=function(e){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,e)},o.prototype.redMul=function(e){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.mul(this,e)},o.prototype.redIMul=function(e){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.imul(this,e)},o.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},o.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},o.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},o.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},o.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},o.prototype.redPow=function(e){return r(this.red&&!e.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,e)};var y={k256:null,p224:null,p192:null,p25519:null};function w(e,t){this.name=e,this.p=new o(t,16),this.n=this.p.bitLength(),this.k=new o(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function _(){w.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function S(){w.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function E(){w.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){w.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function A(e){if("string"==typeof e){var t=o._prime(e);this.m=t.p,this.prime=t}else r(e.gtn(1),"modulus must be greater than 1"),this.m=e,this.prime=null}function I(e){A.call(this,e),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new o(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}w.prototype._tmp=function(){var e=new o(null);return e.words=new Array(Math.ceil(this.n/13)),e},w.prototype.ireduce=function(e){var t,n=e;do{this.split(n,this.tmp),t=(n=(n=this.imulK(n)).iadd(this.tmp)).bitLength()}while(t>this.n);var r=t<this.n?-1:n.ucmp(this.p);return 0===r?(n.words[0]=0,n.length=1):r>0?n.isub(this.p):void 0!==n.strip?n.strip():n._strip(),n},w.prototype.split=function(e,t){e.iushrn(this.n,0,t)},w.prototype.imulK=function(e){return e.imul(this.k)},i(_,w),_.prototype.split=function(e,t){for(var n=Math.min(e.length,9),r=0;r<n;r++)t.words[r]=e.words[r];if(t.length=n,e.length<=9)return e.words[0]=0,void(e.length=1);var i=e.words[9];for(t.words[t.length++]=4194303&i,r=10;r<e.length;r++){var o=0|e.words[r];e.words[r-10]=(4194303&o)<<4|i>>>22,i=o}i>>>=22,e.words[r-10]=i,0===i&&e.length>10?e.length-=10:e.length-=9},_.prototype.imulK=function(e){e.words[e.length]=0,e.words[e.length+1]=0,e.length+=2;for(var t=0,n=0;n<e.length;n++){var r=0|e.words[n];t+=977*r,e.words[n]=67108863&t,t=64*r+(t/67108864|0)}return 0===e.words[e.length-1]&&(e.length--,0===e.words[e.length-1]&&e.length--),e},i(S,w),i(E,w),i(M,w),M.prototype.imulK=function(e){for(var t=0,n=0;n<e.length;n++){var r=19*(0|e.words[n])+t,i=67108863&r;r>>>=26,e.words[n]=i,t=r}return 0!==t&&(e.words[e.length++]=t),e},o._prime=function(e){if(y[e])return y[e];var t;if("k256"===e)t=new _;else if("p224"===e)t=new S;else if("p192"===e)t=new E;else{if("p25519"!==e)throw new Error("Unknown prime "+e);t=new M}return y[e]=t,t},A.prototype._verify1=function(e){r(0===e.negative,"red works only with positives"),r(e.red,"red works only with red numbers")},A.prototype._verify2=function(e,t){r(0==(e.negative|t.negative),"red works only with positives"),r(e.red&&e.red===t.red,"red works only with red numbers")},A.prototype.imod=function(e){return this.prime?this.prime.ireduce(e)._forceRed(this):(c(e,e.umod(this.m)._forceRed(this)),e)},A.prototype.neg=function(e){return e.isZero()?e.clone():this.m.sub(e)._forceRed(this)},A.prototype.add=function(e,t){this._verify2(e,t);var n=e.add(t);return n.cmp(this.m)>=0&&n.isub(this.m),n._forceRed(this)},A.prototype.iadd=function(e,t){this._verify2(e,t);var n=e.iadd(t);return n.cmp(this.m)>=0&&n.isub(this.m),n},A.prototype.sub=function(e,t){this._verify2(e,t);var n=e.sub(t);return n.cmpn(0)<0&&n.iadd(this.m),n._forceRed(this)},A.prototype.isub=function(e,t){this._verify2(e,t);var n=e.isub(t);return n.cmpn(0)<0&&n.iadd(this.m),n},A.prototype.shl=function(e,t){return this._verify1(e),this.imod(e.ushln(t))},A.prototype.imul=function(e,t){return this._verify2(e,t),this.imod(e.imul(t))},A.prototype.mul=function(e,t){return this._verify2(e,t),this.imod(e.mul(t))},A.prototype.isqr=function(e){return this.imul(e,e.clone())},A.prototype.sqr=function(e){return this.mul(e,e)},A.prototype.sqrt=function(e){if(e.isZero())return e.clone();var t=this.m.andln(3);if(r(t%2==1),3===t){var n=this.m.add(new o(1)).iushrn(2);return this.pow(e,n)}for(var i=this.m.subn(1),s=0;!i.isZero()&&0===i.andln(1);)s++,i.iushrn(1);r(!i.isZero());var a=new o(1).toRed(this),u=a.redNeg(),c=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new o(2*f*f).toRed(this);0!==this.pow(f,c).cmp(u);)f.redIAdd(u);for(var l=this.pow(f,i),d=this.pow(e,i.addn(1).iushrn(1)),h=this.pow(e,i),p=s;0!==h.cmp(a);){for(var v=h,g=0;0!==v.cmp(a);g++)v=v.redSqr();r(g<p);var m=this.pow(l,new o(1).iushln(p-g-1));d=d.redMul(m),l=m.redSqr(),h=h.redMul(l),p=g}return d},A.prototype.invm=function(e){var t=e._invmp(this.m);return 0!==t.negative?(t.negative=0,this.imod(t).redNeg()):this.imod(t)},A.prototype.pow=function(e,t){if(t.isZero())return new o(1).toRed(this);if(0===t.cmpn(1))return e.clone();var n=new Array(16);n[0]=new o(1).toRed(this),n[1]=e;for(var r=2;r<n.length;r++)n[r]=this.mul(n[r-1],e);var i=n[0],s=0,a=0,u=t.bitLength()%26;for(0===u&&(u=26),r=t.length-1;r>=0;r--){for(var c=t.words[r],f=u-1;f>=0;f--){var l=c>>f&1;i!==n[0]&&(i=this.sqr(i)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===r&&0===f)&&(i=this.mul(i,n[s]),a=0,s=0)):a=0}u=26}return i},A.prototype.convertTo=function(e){var t=e.umod(this.m);return t===e?t.clone():t},A.prototype.convertFrom=function(e){var t=e.clone();return t.red=null,t},o.mont=function(e){return new I(e)},i(I,A),I.prototype.convertTo=function(e){return this.imod(e.ushln(this.shift))},I.prototype.convertFrom=function(e){var t=this.imod(e.mul(this.rinv));return t.red=null,t},I.prototype.imul=function(e,t){if(e.isZero()||t.isZero())return e.words[0]=0,e.length=1,e;var n=e.imul(t),r=n.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=n.isub(r).iushrn(this.shift),o=i;return i.cmp(this.m)>=0?o=i.isub(this.m):i.cmpn(0)<0&&(o=i.iadd(this.m)),o._forceRed(this)},I.prototype.mul=function(e,t){if(e.isZero()||t.isZero())return new o(0)._forceRed(this);var n=e.mul(t),r=n.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=n.isub(r).iushrn(this.shift),s=i;return i.cmp(this.m)>=0?s=i.isub(this.m):i.cmpn(0)<0&&(s=i.iadd(this.m)),s._forceRed(this)},I.prototype.invm=function(e){return this.imod(e._invmp(this.m).mul(this.r2))._forceRed(this)}}(e,this)}).call(this,n(57)(e))},function(e,t){},function(e){e.exports=JSON.parse('{"name":"elliptic","version":"6.5.4","description":"EC cryptography","main":"lib/elliptic.js","files":["lib"],"scripts":{"lint":"eslint lib test","lint:fix":"npm run lint -- --fix","unit":"istanbul test _mocha --reporter=spec test/index.js","test":"npm run lint && npm run unit","version":"grunt dist && git add dist/"},"repository":{"type":"git","url":"git@github.com:indutny/elliptic"},"keywords":["EC","Elliptic","curve","Cryptography"],"author":"Fedor Indutny <fedor@indutny.com>","license":"MIT","bugs":{"url":"https://github.com/indutny/elliptic/issues"},"homepage":"https://github.com/indutny/elliptic","devDependencies":{"brfs":"^2.0.2","coveralls":"^3.1.0","eslint":"^7.6.0","grunt":"^1.2.1","grunt-browserify":"^5.3.0","grunt-cli":"^1.3.2","grunt-contrib-connect":"^3.0.0","grunt-contrib-copy":"^1.0.0","grunt-contrib-uglify":"^5.0.0","grunt-mocha-istanbul":"^5.0.2","grunt-saucelabs":"^9.0.1","istanbul":"^0.4.5","mocha":"^8.0.1"},"dependencies":{"bn.js":"^4.11.9","brorand":"^1.1.0","hash.js":"^1.0.0","hmac-drbg":"^1.0.1","inherits":"^2.0.4","minimalistic-assert":"^1.0.1","minimalistic-crypto-utils":"^1.0.1"}}')},function(e,t,n){"use strict";var r=n(47),i=n(29),o=n(7),s=n(95),a=r.assert;function u(e){s.call(this,"short",e),this.a=new i(e.a,16).toRed(this.red),this.b=new i(e.b,16).toRed(this.red),this.tinv=this.two.redInvm(),this.zeroA=0===this.a.fromRed().cmpn(0),this.threeA=0===this.a.fromRed().sub(this.p).cmpn(-3),this.endo=this._getEndomorphism(e),this._endoWnafT1=new Array(4),this._endoWnafT2=new Array(4)}function c(e,t,n,r){s.BasePoint.call(this,e,"affine"),null===t&&null===n?(this.x=null,this.y=null,this.inf=!0):(this.x=new i(t,16),this.y=new i(n,16),r&&(this.x.forceRed(this.curve.red),this.y.forceRed(this.curve.red)),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.inf=!1)}function f(e,t,n,r){s.BasePoint.call(this,e,"jacobian"),null===t&&null===n&&null===r?(this.x=this.curve.one,this.y=this.curve.one,this.z=new i(0)):(this.x=new i(t,16),this.y=new i(n,16),this.z=new i(r,16)),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)),this.zOne=this.z===this.curve.one}o(u,s),e.exports=u,u.prototype._getEndomorphism=function(e){if(this.zeroA&&this.g&&this.n&&1===this.p.modn(3)){var t,n;if(e.beta)t=new i(e.beta,16).toRed(this.red);else{var r=this._getEndoRoots(this.p);t=(t=r[0].cmp(r[1])<0?r[0]:r[1]).toRed(this.red)}if(e.lambda)n=new i(e.lambda,16);else{var o=this._getEndoRoots(this.n);0===this.g.mul(o[0]).x.cmp(this.g.x.redMul(t))?n=o[0]:(n=o[1],a(0===this.g.mul(n).x.cmp(this.g.x.redMul(t))))}return{beta:t,lambda:n,basis:e.basis?e.basis.map((function(e){return{a:new i(e.a,16),b:new i(e.b,16)}})):this._getEndoBasis(n)}}},u.prototype._getEndoRoots=function(e){var t=e===this.p?this.red:i.mont(e),n=new i(2).toRed(t).redInvm(),r=n.redNeg(),o=new i(3).toRed(t).redNeg().redSqrt().redMul(n);return[r.redAdd(o).fromRed(),r.redSub(o).fromRed()]},u.prototype._getEndoBasis=function(e){for(var t,n,r,o,s,a,u,c,f,l=this.n.ushrn(Math.floor(this.n.bitLength()/2)),d=e,h=this.n.clone(),p=new i(1),v=new i(0),g=new i(0),m=new i(1),b=0;0!==d.cmpn(0);){var y=h.div(d);c=h.sub(y.mul(d)),f=g.sub(y.mul(p));var w=m.sub(y.mul(v));if(!r&&c.cmp(l)<0)t=u.neg(),n=p,r=c.neg(),o=f;else if(r&&2==++b)break;u=c,h=d,d=c,g=p,p=f,m=v,v=w}s=c.neg(),a=f;var _=r.sqr().add(o.sqr());return s.sqr().add(a.sqr()).cmp(_)>=0&&(s=t,a=n),r.negative&&(r=r.neg(),o=o.neg()),s.negative&&(s=s.neg(),a=a.neg()),[{a:r,b:o},{a:s,b:a}]},u.prototype._endoSplit=function(e){var t=this.endo.basis,n=t[0],r=t[1],i=r.b.mul(e).divRound(this.n),o=n.b.neg().mul(e).divRound(this.n),s=i.mul(n.a),a=o.mul(r.a),u=i.mul(n.b),c=o.mul(r.b);return{k1:e.sub(s).sub(a),k2:u.add(c).neg()}},u.prototype.pointFromX=function(e,t){(e=new i(e,16)).red||(e=e.toRed(this.red));var n=e.redSqr().redMul(e).redIAdd(e.redMul(this.a)).redIAdd(this.b),r=n.redSqrt();if(0!==r.redSqr().redSub(n).cmp(this.zero))throw new Error("invalid point");var o=r.fromRed().isOdd();return(t&&!o||!t&&o)&&(r=r.redNeg()),this.point(e,r)},u.prototype.validate=function(e){if(e.inf)return!0;var t=e.x,n=e.y,r=this.a.redMul(t),i=t.redSqr().redMul(t).redIAdd(r).redIAdd(this.b);return 0===n.redSqr().redISub(i).cmpn(0)},u.prototype._endoWnafMulAdd=function(e,t,n){for(var r=this._endoWnafT1,i=this._endoWnafT2,o=0;o<e.length;o++){var s=this._endoSplit(t[o]),a=e[o],u=a._getBeta();s.k1.negative&&(s.k1.ineg(),a=a.neg(!0)),s.k2.negative&&(s.k2.ineg(),u=u.neg(!0)),r[2*o]=a,r[2*o+1]=u,i[2*o]=s.k1,i[2*o+1]=s.k2}for(var c=this._wnafMulAdd(1,r,i,2*o,n),f=0;f<2*o;f++)r[f]=null,i[f]=null;return c},o(c,s.BasePoint),u.prototype.point=function(e,t,n){return new c(this,e,t,n)},u.prototype.pointFromJSON=function(e,t){return c.fromJSON(this,e,t)},c.prototype._getBeta=function(){if(this.curve.endo){var e=this.precomputed;if(e&&e.beta)return e.beta;var t=this.curve.point(this.x.redMul(this.curve.endo.beta),this.y);if(e){var n=this.curve,r=function(e){return n.point(e.x.redMul(n.endo.beta),e.y)};e.beta=t,t.precomputed={beta:null,naf:e.naf&&{wnd:e.naf.wnd,points:e.naf.points.map(r)},doubles:e.doubles&&{step:e.doubles.step,points:e.doubles.points.map(r)}}}return t}},c.prototype.toJSON=function(){return this.precomputed?[this.x,this.y,this.precomputed&&{doubles:this.precomputed.doubles&&{step:this.precomputed.doubles.step,points:this.precomputed.doubles.points.slice(1)},naf:this.precomputed.naf&&{wnd:this.precomputed.naf.wnd,points:this.precomputed.naf.points.slice(1)}}]:[this.x,this.y]},c.fromJSON=function(e,t,n){"string"==typeof t&&(t=JSON.parse(t));var r=e.point(t[0],t[1],n);if(!t[2])return r;function i(t){return e.point(t[0],t[1],n)}var o=t[2];return r.precomputed={beta:null,doubles:o.doubles&&{step:o.doubles.step,points:[r].concat(o.doubles.points.map(i))},naf:o.naf&&{wnd:o.naf.wnd,points:[r].concat(o.naf.points.map(i))}},r},c.prototype.inspect=function(){return this.isInfinity()?"<EC Point Infinity>":"<EC Point x: "+this.x.fromRed().toString(16,2)+" y: "+this.y.fromRed().toString(16,2)+">"},c.prototype.isInfinity=function(){return this.inf},c.prototype.add=function(e){if(this.inf)return e;if(e.inf)return this;if(this.eq(e))return this.dbl();if(this.neg().eq(e))return this.curve.point(null,null);if(0===this.x.cmp(e.x))return this.curve.point(null,null);var t=this.y.redSub(e.y);0!==t.cmpn(0)&&(t=t.redMul(this.x.redSub(e.x).redInvm()));var n=t.redSqr().redISub(this.x).redISub(e.x),r=t.redMul(this.x.redSub(n)).redISub(this.y);return this.curve.point(n,r)},c.prototype.dbl=function(){if(this.inf)return this;var e=this.y.redAdd(this.y);if(0===e.cmpn(0))return this.curve.point(null,null);var t=this.curve.a,n=this.x.redSqr(),r=e.redInvm(),i=n.redAdd(n).redIAdd(n).redIAdd(t).redMul(r),o=i.redSqr().redISub(this.x.redAdd(this.x)),s=i.redMul(this.x.redSub(o)).redISub(this.y);return this.curve.point(o,s)},c.prototype.getX=function(){return this.x.fromRed()},c.prototype.getY=function(){return this.y.fromRed()},c.prototype.mul=function(e){return e=new i(e,16),this.isInfinity()?this:this._hasDoubles(e)?this.curve._fixedNafMul(this,e):this.curve.endo?this.curve._endoWnafMulAdd([this],[e]):this.curve._wnafMul(this,e)},c.prototype.mulAdd=function(e,t,n){var r=[this,t],i=[e,n];return this.curve.endo?this.curve._endoWnafMulAdd(r,i):this.curve._wnafMulAdd(1,r,i,2)},c.prototype.jmulAdd=function(e,t,n){var r=[this,t],i=[e,n];return this.curve.endo?this.curve._endoWnafMulAdd(r,i,!0):this.curve._wnafMulAdd(1,r,i,2,!0)},c.prototype.eq=function(e){return this===e||this.inf===e.inf&&(this.inf||0===this.x.cmp(e.x)&&0===this.y.cmp(e.y))},c.prototype.neg=function(e){if(this.inf)return this;var t=this.curve.point(this.x,this.y.redNeg());if(e&&this.precomputed){var n=this.precomputed,r=function(e){return e.neg()};t.precomputed={naf:n.naf&&{wnd:n.naf.wnd,points:n.naf.points.map(r)},doubles:n.doubles&&{step:n.doubles.step,points:n.doubles.points.map(r)}}}return t},c.prototype.toJ=function(){return this.inf?this.curve.jpoint(null,null,null):this.curve.jpoint(this.x,this.y,this.curve.one)},o(f,s.BasePoint),u.prototype.jpoint=function(e,t,n){return new f(this,e,t,n)},f.prototype.toP=function(){if(this.isInfinity())return this.curve.point(null,null);var e=this.z.redInvm(),t=e.redSqr(),n=this.x.redMul(t),r=this.y.redMul(t).redMul(e);return this.curve.point(n,r)},f.prototype.neg=function(){return this.curve.jpoint(this.x,this.y.redNeg(),this.z)},f.prototype.add=function(e){if(this.isInfinity())return e;if(e.isInfinity())return this;var t=e.z.redSqr(),n=this.z.redSqr(),r=this.x.redMul(t),i=e.x.redMul(n),o=this.y.redMul(t.redMul(e.z)),s=e.y.redMul(n.redMul(this.z)),a=r.redSub(i),u=o.redSub(s);if(0===a.cmpn(0))return 0!==u.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var c=a.redSqr(),f=c.redMul(a),l=r.redMul(c),d=u.redSqr().redIAdd(f).redISub(l).redISub(l),h=u.redMul(l.redISub(d)).redISub(o.redMul(f)),p=this.z.redMul(e.z).redMul(a);return this.curve.jpoint(d,h,p)},f.prototype.mixedAdd=function(e){if(this.isInfinity())return e.toJ();if(e.isInfinity())return this;var t=this.z.redSqr(),n=this.x,r=e.x.redMul(t),i=this.y,o=e.y.redMul(t).redMul(this.z),s=n.redSub(r),a=i.redSub(o);if(0===s.cmpn(0))return 0!==a.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var u=s.redSqr(),c=u.redMul(s),f=n.redMul(u),l=a.redSqr().redIAdd(c).redISub(f).redISub(f),d=a.redMul(f.redISub(l)).redISub(i.redMul(c)),h=this.z.redMul(s);return this.curve.jpoint(l,d,h)},f.prototype.dblp=function(e){if(0===e)return this;if(this.isInfinity())return this;if(!e)return this.dbl();var t;if(this.curve.zeroA||this.curve.threeA){var n=this;for(t=0;t<e;t++)n=n.dbl();return n}var r=this.curve.a,i=this.curve.tinv,o=this.x,s=this.y,a=this.z,u=a.redSqr().redSqr(),c=s.redAdd(s);for(t=0;t<e;t++){var f=o.redSqr(),l=c.redSqr(),d=l.redSqr(),h=f.redAdd(f).redIAdd(f).redIAdd(r.redMul(u)),p=o.redMul(l),v=h.redSqr().redISub(p.redAdd(p)),g=p.redISub(v),m=h.redMul(g);m=m.redIAdd(m).redISub(d);var b=c.redMul(a);t+1<e&&(u=u.redMul(d)),o=v,a=b,c=m}return this.curve.jpoint(o,c.redMul(i),a)},f.prototype.dbl=function(){return this.isInfinity()?this:this.curve.zeroA?this._zeroDbl():this.curve.threeA?this._threeDbl():this._dbl()},f.prototype._zeroDbl=function(){var e,t,n;if(this.zOne){var r=this.x.redSqr(),i=this.y.redSqr(),o=i.redSqr(),s=this.x.redAdd(i).redSqr().redISub(r).redISub(o);s=s.redIAdd(s);var a=r.redAdd(r).redIAdd(r),u=a.redSqr().redISub(s).redISub(s),c=o.redIAdd(o);c=(c=c.redIAdd(c)).redIAdd(c),e=u,t=a.redMul(s.redISub(u)).redISub(c),n=this.y.redAdd(this.y)}else{var f=this.x.redSqr(),l=this.y.redSqr(),d=l.redSqr(),h=this.x.redAdd(l).redSqr().redISub(f).redISub(d);h=h.redIAdd(h);var p=f.redAdd(f).redIAdd(f),v=p.redSqr(),g=d.redIAdd(d);g=(g=g.redIAdd(g)).redIAdd(g),e=v.redISub(h).redISub(h),t=p.redMul(h.redISub(e)).redISub(g),n=(n=this.y.redMul(this.z)).redIAdd(n)}return this.curve.jpoint(e,t,n)},f.prototype._threeDbl=function(){var e,t,n;if(this.zOne){var r=this.x.redSqr(),i=this.y.redSqr(),o=i.redSqr(),s=this.x.redAdd(i).redSqr().redISub(r).redISub(o);s=s.redIAdd(s);var a=r.redAdd(r).redIAdd(r).redIAdd(this.curve.a),u=a.redSqr().redISub(s).redISub(s);e=u;var c=o.redIAdd(o);c=(c=c.redIAdd(c)).redIAdd(c),t=a.redMul(s.redISub(u)).redISub(c),n=this.y.redAdd(this.y)}else{var f=this.z.redSqr(),l=this.y.redSqr(),d=this.x.redMul(l),h=this.x.redSub(f).redMul(this.x.redAdd(f));h=h.redAdd(h).redIAdd(h);var p=d.redIAdd(d),v=(p=p.redIAdd(p)).redAdd(p);e=h.redSqr().redISub(v),n=this.y.redAdd(this.z).redSqr().redISub(l).redISub(f);var g=l.redSqr();g=(g=(g=g.redIAdd(g)).redIAdd(g)).redIAdd(g),t=h.redMul(p.redISub(e)).redISub(g)}return this.curve.jpoint(e,t,n)},f.prototype._dbl=function(){var e=this.curve.a,t=this.x,n=this.y,r=this.z,i=r.redSqr().redSqr(),o=t.redSqr(),s=n.redSqr(),a=o.redAdd(o).redIAdd(o).redIAdd(e.redMul(i)),u=t.redAdd(t),c=(u=u.redIAdd(u)).redMul(s),f=a.redSqr().redISub(c.redAdd(c)),l=c.redISub(f),d=s.redSqr();d=(d=(d=d.redIAdd(d)).redIAdd(d)).redIAdd(d);var h=a.redMul(l).redISub(d),p=n.redAdd(n).redMul(r);return this.curve.jpoint(f,h,p)},f.prototype.trpl=function(){if(!this.curve.zeroA)return this.dbl().add(this);var e=this.x.redSqr(),t=this.y.redSqr(),n=this.z.redSqr(),r=t.redSqr(),i=e.redAdd(e).redIAdd(e),o=i.redSqr(),s=this.x.redAdd(t).redSqr().redISub(e).redISub(r),a=(s=(s=(s=s.redIAdd(s)).redAdd(s).redIAdd(s)).redISub(o)).redSqr(),u=r.redIAdd(r);u=(u=(u=u.redIAdd(u)).redIAdd(u)).redIAdd(u);var c=i.redIAdd(s).redSqr().redISub(o).redISub(a).redISub(u),f=t.redMul(c);f=(f=f.redIAdd(f)).redIAdd(f);var l=this.x.redMul(a).redISub(f);l=(l=l.redIAdd(l)).redIAdd(l);var d=this.y.redMul(c.redMul(u.redISub(c)).redISub(s.redMul(a)));d=(d=(d=d.redIAdd(d)).redIAdd(d)).redIAdd(d);var h=this.z.redAdd(s).redSqr().redISub(n).redISub(a);return this.curve.jpoint(l,d,h)},f.prototype.mul=function(e,t){return e=new i(e,t),this.curve._wnafMul(this,e)},f.prototype.eq=function(e){if("affine"===e.type)return this.eq(e.toJ());if(this===e)return!0;var t=this.z.redSqr(),n=e.z.redSqr();if(0!==this.x.redMul(n).redISub(e.x.redMul(t)).cmpn(0))return!1;var r=t.redMul(this.z),i=n.redMul(e.z);return 0===this.y.redMul(i).redISub(e.y.redMul(r)).cmpn(0)},f.prototype.eqXToP=function(e){var t=this.z.redSqr(),n=e.toRed(this.curve.red).redMul(t);if(0===this.x.cmp(n))return!0;for(var r=e.clone(),i=this.curve.redN.redMul(t);;){if(r.iadd(this.curve.n),r.cmp(this.curve.p)>=0)return!1;if(n.redIAdd(i),0===this.x.cmp(n))return!0}},f.prototype.inspect=function(){return this.isInfinity()?"<EC JPoint Infinity>":"<EC JPoint x: "+this.x.toString(16,2)+" y: "+this.y.toString(16,2)+" z: "+this.z.toString(16,2)+">"},f.prototype.isInfinity=function(){return 0===this.z.cmpn(0)}},function(e,t,n){"use strict";var r=n(29),i=n(7),o=n(95),s=n(47);function a(e){o.call(this,"mont",e),this.a=new r(e.a,16).toRed(this.red),this.b=new r(e.b,16).toRed(this.red),this.i4=new r(4).toRed(this.red).redInvm(),this.two=new r(2).toRed(this.red),this.a24=this.i4.redMul(this.a.redAdd(this.two))}function u(e,t,n){o.BasePoint.call(this,e,"projective"),null===t&&null===n?(this.x=this.curve.one,this.z=this.curve.zero):(this.x=new r(t,16),this.z=new r(n,16),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)))}i(a,o),e.exports=a,a.prototype.validate=function(e){var t=e.normalize().x,n=t.redSqr(),r=n.redMul(t).redAdd(n.redMul(this.a)).redAdd(t);return 0===r.redSqrt().redSqr().cmp(r)},i(u,o.BasePoint),a.prototype.decodePoint=function(e,t){return this.point(s.toArray(e,t),1)},a.prototype.point=function(e,t){return new u(this,e,t)},a.prototype.pointFromJSON=function(e){return u.fromJSON(this,e)},u.prototype.precompute=function(){},u.prototype._encode=function(){return this.getX().toArray("be",this.curve.p.byteLength())},u.fromJSON=function(e,t){return new u(e,t[0],t[1]||e.one)},u.prototype.inspect=function(){return this.isInfinity()?"<EC Point Infinity>":"<EC Point x: "+this.x.fromRed().toString(16,2)+" z: "+this.z.fromRed().toString(16,2)+">"},u.prototype.isInfinity=function(){return 0===this.z.cmpn(0)},u.prototype.dbl=function(){var e=this.x.redAdd(this.z).redSqr(),t=this.x.redSub(this.z).redSqr(),n=e.redSub(t),r=e.redMul(t),i=n.redMul(t.redAdd(this.curve.a24.redMul(n)));return this.curve.point(r,i)},u.prototype.add=function(){throw new Error("Not supported on Montgomery curve")},u.prototype.diffAdd=function(e,t){var n=this.x.redAdd(this.z),r=this.x.redSub(this.z),i=e.x.redAdd(e.z),o=e.x.redSub(e.z).redMul(n),s=i.redMul(r),a=t.z.redMul(o.redAdd(s).redSqr()),u=t.x.redMul(o.redISub(s).redSqr());return this.curve.point(a,u)},u.prototype.mul=function(e){for(var t=e.clone(),n=this,r=this.curve.point(null,null),i=[];0!==t.cmpn(0);t.iushrn(1))i.push(t.andln(1));for(var o=i.length-1;o>=0;o--)0===i[o]?(n=n.diffAdd(r,this),r=r.dbl()):(r=n.diffAdd(r,this),n=n.dbl());return r},u.prototype.mulAdd=function(){throw new Error("Not supported on Montgomery curve")},u.prototype.jumlAdd=function(){throw new Error("Not supported on Montgomery curve")},u.prototype.eq=function(e){return 0===this.getX().cmp(e.getX())},u.prototype.normalize=function(){return this.x=this.x.redMul(this.z.redInvm()),this.z=this.curve.one,this},u.prototype.getX=function(){return this.normalize(),this.x.fromRed()}},function(e,t,n){"use strict";var r=n(47),i=n(29),o=n(7),s=n(95),a=r.assert;function u(e){this.twisted=1!=(0|e.a),this.mOneA=this.twisted&&-1==(0|e.a),this.extended=this.mOneA,s.call(this,"edwards",e),this.a=new i(e.a,16).umod(this.red.m),this.a=this.a.toRed(this.red),this.c=new i(e.c,16).toRed(this.red),this.c2=this.c.redSqr(),this.d=new i(e.d,16).toRed(this.red),this.dd=this.d.redAdd(this.d),a(!this.twisted||0===this.c.fromRed().cmpn(1)),this.oneC=1==(0|e.c)}function c(e,t,n,r,o){s.BasePoint.call(this,e,"projective"),null===t&&null===n&&null===r?(this.x=this.curve.zero,this.y=this.curve.one,this.z=this.curve.one,this.t=this.curve.zero,this.zOne=!0):(this.x=new i(t,16),this.y=new i(n,16),this.z=r?new i(r,16):this.curve.one,this.t=o&&new i(o,16),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)),this.t&&!this.t.red&&(this.t=this.t.toRed(this.curve.red)),this.zOne=this.z===this.curve.one,this.curve.extended&&!this.t&&(this.t=this.x.redMul(this.y),this.zOne||(this.t=this.t.redMul(this.z.redInvm()))))}o(u,s),e.exports=u,u.prototype._mulA=function(e){return this.mOneA?e.redNeg():this.a.redMul(e)},u.prototype._mulC=function(e){return this.oneC?e:this.c.redMul(e)},u.prototype.jpoint=function(e,t,n,r){return this.point(e,t,n,r)},u.prototype.pointFromX=function(e,t){(e=new i(e,16)).red||(e=e.toRed(this.red));var n=e.redSqr(),r=this.c2.redSub(this.a.redMul(n)),o=this.one.redSub(this.c2.redMul(this.d).redMul(n)),s=r.redMul(o.redInvm()),a=s.redSqrt();if(0!==a.redSqr().redSub(s).cmp(this.zero))throw new Error("invalid point");var u=a.fromRed().isOdd();return(t&&!u||!t&&u)&&(a=a.redNeg()),this.point(e,a)},u.prototype.pointFromY=function(e,t){(e=new i(e,16)).red||(e=e.toRed(this.red));var n=e.redSqr(),r=n.redSub(this.c2),o=n.redMul(this.d).redMul(this.c2).redSub(this.a),s=r.redMul(o.redInvm());if(0===s.cmp(this.zero)){if(t)throw new Error("invalid point");return this.point(this.zero,e)}var a=s.redSqrt();if(0!==a.redSqr().redSub(s).cmp(this.zero))throw new Error("invalid point");return a.fromRed().isOdd()!==t&&(a=a.redNeg()),this.point(a,e)},u.prototype.validate=function(e){if(e.isInfinity())return!0;e.normalize();var t=e.x.redSqr(),n=e.y.redSqr(),r=t.redMul(this.a).redAdd(n),i=this.c2.redMul(this.one.redAdd(this.d.redMul(t).redMul(n)));return 0===r.cmp(i)},o(c,s.BasePoint),u.prototype.pointFromJSON=function(e){return c.fromJSON(this,e)},u.prototype.point=function(e,t,n,r){return new c(this,e,t,n,r)},c.fromJSON=function(e,t){return new c(e,t[0],t[1],t[2])},c.prototype.inspect=function(){return this.isInfinity()?"<EC Point Infinity>":"<EC Point x: "+this.x.fromRed().toString(16,2)+" y: "+this.y.fromRed().toString(16,2)+" z: "+this.z.fromRed().toString(16,2)+">"},c.prototype.isInfinity=function(){return 0===this.x.cmpn(0)&&(0===this.y.cmp(this.z)||this.zOne&&0===this.y.cmp(this.curve.c))},c.prototype._extDbl=function(){var e=this.x.redSqr(),t=this.y.redSqr(),n=this.z.redSqr();n=n.redIAdd(n);var r=this.curve._mulA(e),i=this.x.redAdd(this.y).redSqr().redISub(e).redISub(t),o=r.redAdd(t),s=o.redSub(n),a=r.redSub(t),u=i.redMul(s),c=o.redMul(a),f=i.redMul(a),l=s.redMul(o);return this.curve.point(u,c,l,f)},c.prototype._projDbl=function(){var e,t,n,r,i,o,s=this.x.redAdd(this.y).redSqr(),a=this.x.redSqr(),u=this.y.redSqr();if(this.curve.twisted){var c=(r=this.curve._mulA(a)).redAdd(u);this.zOne?(e=s.redSub(a).redSub(u).redMul(c.redSub(this.curve.two)),t=c.redMul(r.redSub(u)),n=c.redSqr().redSub(c).redSub(c)):(i=this.z.redSqr(),o=c.redSub(i).redISub(i),e=s.redSub(a).redISub(u).redMul(o),t=c.redMul(r.redSub(u)),n=c.redMul(o))}else r=a.redAdd(u),i=this.curve._mulC(this.z).redSqr(),o=r.redSub(i).redSub(i),e=this.curve._mulC(s.redISub(r)).redMul(o),t=this.curve._mulC(r).redMul(a.redISub(u)),n=r.redMul(o);return this.curve.point(e,t,n)},c.prototype.dbl=function(){return this.isInfinity()?this:this.curve.extended?this._extDbl():this._projDbl()},c.prototype._extAdd=function(e){var t=this.y.redSub(this.x).redMul(e.y.redSub(e.x)),n=this.y.redAdd(this.x).redMul(e.y.redAdd(e.x)),r=this.t.redMul(this.curve.dd).redMul(e.t),i=this.z.redMul(e.z.redAdd(e.z)),o=n.redSub(t),s=i.redSub(r),a=i.redAdd(r),u=n.redAdd(t),c=o.redMul(s),f=a.redMul(u),l=o.redMul(u),d=s.redMul(a);return this.curve.point(c,f,d,l)},c.prototype._projAdd=function(e){var t,n,r=this.z.redMul(e.z),i=r.redSqr(),o=this.x.redMul(e.x),s=this.y.redMul(e.y),a=this.curve.d.redMul(o).redMul(s),u=i.redSub(a),c=i.redAdd(a),f=this.x.redAdd(this.y).redMul(e.x.redAdd(e.y)).redISub(o).redISub(s),l=r.redMul(u).redMul(f);return this.curve.twisted?(t=r.redMul(c).redMul(s.redSub(this.curve._mulA(o))),n=u.redMul(c)):(t=r.redMul(c).redMul(s.redSub(o)),n=this.curve._mulC(u).redMul(c)),this.curve.point(l,t,n)},c.prototype.add=function(e){return this.isInfinity()?e:e.isInfinity()?this:this.curve.extended?this._extAdd(e):this._projAdd(e)},c.prototype.mul=function(e){return this._hasDoubles(e)?this.curve._fixedNafMul(this,e):this.curve._wnafMul(this,e)},c.prototype.mulAdd=function(e,t,n){return this.curve._wnafMulAdd(1,[this,t],[e,n],2,!1)},c.prototype.jmulAdd=function(e,t,n){return this.curve._wnafMulAdd(1,[this,t],[e,n],2,!0)},c.prototype.normalize=function(){if(this.zOne)return this;var e=this.z.redInvm();return this.x=this.x.redMul(e),this.y=this.y.redMul(e),this.t&&(this.t=this.t.redMul(e)),this.z=this.curve.one,this.zOne=!0,this},c.prototype.neg=function(){return this.curve.point(this.x.redNeg(),this.y,this.z,this.t&&this.t.redNeg())},c.prototype.getX=function(){return this.normalize(),this.x.fromRed()},c.prototype.getY=function(){return this.normalize(),this.y.fromRed()},c.prototype.eq=function(e){return this===e||0===this.getX().cmp(e.getX())&&0===this.getY().cmp(e.getY())},c.prototype.eqXToP=function(e){var t=e.toRed(this.curve.red).redMul(this.z);if(0===this.x.cmp(t))return!0;for(var n=e.clone(),r=this.curve.redN.redMul(this.z);;){if(n.iadd(this.curve.n),n.cmp(this.curve.p)>=0)return!1;if(t.redIAdd(r),0===this.x.cmp(t))return!0}},c.prototype.toP=c.prototype.normalize,c.prototype.mixedAdd=c.prototype.add},function(e,t,n){"use strict";t.sha1=n(336),t.sha224=n(337),t.sha256=n(201),t.sha384=n(338),t.sha512=n(202)},function(e,t,n){"use strict";var r=n(51),i=n(82),o=n(200),s=r.rotl32,a=r.sum32,u=r.sum32_5,c=o.ft_1,f=i.BlockHash,l=[1518500249,1859775393,2400959708,3395469782];function d(){if(!(this instanceof d))return new d;f.call(this),this.h=[1732584193,4023233417,2562383102,271733878,3285377520],this.W=new Array(80)}r.inherits(d,f),e.exports=d,d.blockSize=512,d.outSize=160,d.hmacStrength=80,d.padLength=64,d.prototype._update=function(e,t){for(var n=this.W,r=0;r<16;r++)n[r]=e[t+r];for(;r<n.length;r++)n[r]=s(n[r-3]^n[r-8]^n[r-14]^n[r-16],1);var i=this.h[0],o=this.h[1],f=this.h[2],d=this.h[3],h=this.h[4];for(r=0;r<n.length;r++){var p=~~(r/20),v=u(s(i,5),c(p,o,f,d),h,n[r],l[p]);h=d,d=f,f=s(o,30),o=i,i=v}this.h[0]=a(this.h[0],i),this.h[1]=a(this.h[1],o),this.h[2]=a(this.h[2],f),this.h[3]=a(this.h[3],d),this.h[4]=a(this.h[4],h)},d.prototype._digest=function(e){return"hex"===e?r.toHex32(this.h,"big"):r.split32(this.h,"big")}},function(e,t,n){"use strict";var r=n(51),i=n(201);function o(){if(!(this instanceof o))return new o;i.call(this),this.h=[3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428]}r.inherits(o,i),e.exports=o,o.blockSize=512,o.outSize=224,o.hmacStrength=192,o.padLength=64,o.prototype._digest=function(e){return"hex"===e?r.toHex32(this.h.slice(0,7),"big"):r.split32(this.h.slice(0,7),"big")}},function(e,t,n){"use strict";var r=n(51),i=n(202);function o(){if(!(this instanceof o))return new o;i.call(this),this.h=[3418070365,3238371032,1654270250,914150663,2438529370,812702999,355462360,4144912697,1731405415,4290775857,2394180231,1750603025,3675008525,1694076839,1203062813,3204075428]}r.inherits(o,i),e.exports=o,o.blockSize=1024,o.outSize=384,o.hmacStrength=192,o.padLength=128,o.prototype._digest=function(e){return"hex"===e?r.toHex32(this.h.slice(0,12),"big"):r.split32(this.h.slice(0,12),"big")}},function(e,t,n){"use strict";var r=n(51),i=n(82),o=r.rotl32,s=r.sum32,a=r.sum32_3,u=r.sum32_4,c=i.BlockHash;function f(){if(!(this instanceof f))return new f;c.call(this),this.h=[1732584193,4023233417,2562383102,271733878,3285377520],this.endian="little"}function l(e,t,n,r){return e<=15?t^n^r:e<=31?t&n|~t&r:e<=47?(t|~n)^r:e<=63?t&r|n&~r:t^(n|~r)}function d(e){return e<=15?0:e<=31?1518500249:e<=47?1859775393:e<=63?2400959708:2840853838}function h(e){return e<=15?1352829926:e<=31?1548603684:e<=47?1836072691:e<=63?2053994217:0}r.inherits(f,c),t.ripemd160=f,f.blockSize=512,f.outSize=160,f.hmacStrength=192,f.padLength=64,f.prototype._update=function(e,t){for(var n=this.h[0],r=this.h[1],i=this.h[2],c=this.h[3],f=this.h[4],b=n,y=r,w=i,_=c,S=f,E=0;E<80;E++){var M=s(o(u(n,l(E,r,i,c),e[p[E]+t],d(E)),g[E]),f);n=f,f=c,c=o(i,10),i=r,r=M,M=s(o(u(b,l(79-E,y,w,_),e[v[E]+t],h(E)),m[E]),S),b=S,S=_,_=o(w,10),w=y,y=M}M=a(this.h[1],i,_),this.h[1]=a(this.h[2],c,S),this.h[2]=a(this.h[3],f,b),this.h[3]=a(this.h[4],n,y),this.h[4]=a(this.h[0],r,w),this.h[0]=M},f.prototype._digest=function(e){return"hex"===e?r.toHex32(this.h,"little"):r.split32(this.h,"little")};var p=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13],v=[5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11],g=[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6],m=[8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]},function(e,t,n){"use strict";var r=n(51),i=n(46);function o(e,t,n){if(!(this instanceof o))return new o(e,t,n);this.Hash=e,this.blockSize=e.blockSize/8,this.outSize=e.outSize/8,this.inner=null,this.outer=null,this._init(r.toArray(t,n))}e.exports=o,o.prototype._init=function(e){e.length>this.blockSize&&(e=(new this.Hash).update(e).digest()),i(e.length<=this.blockSize);for(var t=e.length;t<this.blockSize;t++)e.push(0);for(t=0;t<e.length;t++)e[t]^=54;for(this.inner=(new this.Hash).update(e),t=0;t<e.length;t++)e[t]^=106;this.outer=(new this.Hash).update(e)},o.prototype.update=function(e,t){return this.inner.update(e,t),this},o.prototype.digest=function(e){return this.outer.update(this.inner.digest()),this.outer.digest(e)}},function(e,t){e.exports={doubles:{step:4,points:[["e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a","f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821"],["8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508","11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf"],["175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739","d3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695"],["363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640","4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9"],["8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c","4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36"],["723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda","96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f"],["eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa","5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999"],["100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0","cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09"],["e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d","9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d"],["feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d","e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088"],["da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1","9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d"],["53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0","5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8"],["8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047","10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a"],["385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862","283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453"],["6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7","7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160"],["3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd","56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0"],["85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83","7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6"],["948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a","53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589"],["6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8","bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17"],["e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d","4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda"],["e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725","7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd"],["213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754","4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2"],["4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c","17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6"],["fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6","6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f"],["76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39","c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01"],["c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891","893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3"],["d895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b","febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f"],["b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03","2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7"],["e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d","eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78"],["a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070","7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1"],["90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4","e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150"],["8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da","662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82"],["e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11","1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc"],["8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e","efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b"],["e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41","2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51"],["b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef","67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45"],["d68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8","db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120"],["324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d","648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84"],["4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96","35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d"],["9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd","ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d"],["6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5","9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8"],["a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266","40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8"],["7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71","34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac"],["928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac","c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f"],["85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751","1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962"],["ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e","493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907"],["827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241","c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec"],["eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3","be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d"],["e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f","4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414"],["1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19","aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd"],["146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be","b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0"],["fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9","6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811"],["da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2","8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1"],["a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13","7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c"],["174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c","ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73"],["959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba","2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd"],["d2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151","e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405"],["64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073","d99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589"],["8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458","38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e"],["13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b","69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27"],["bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366","d3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1"],["8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa","40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482"],["8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0","620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945"],["dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787","7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573"],["f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e","ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82"]]},naf:{wnd:7,points:[["f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9","388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672"],["2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4","d8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6"],["5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc","6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da"],["acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe","cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37"],["774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb","d984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b"],["f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8","ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81"],["d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e","581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58"],["defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34","4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77"],["2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c","85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a"],["352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5","321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c"],["2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f","2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67"],["9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714","73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402"],["daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729","a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55"],["c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db","2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482"],["6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4","e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82"],["1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5","b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396"],["605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479","2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49"],["62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d","80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf"],["80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f","1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a"],["7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb","d0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7"],["d528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9","eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933"],["49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963","758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a"],["77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74","958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6"],["f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530","e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37"],["463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b","5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e"],["f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247","cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6"],["caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1","cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476"],["2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120","4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40"],["7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435","91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61"],["754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18","673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683"],["e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8","59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5"],["186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb","3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b"],["df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f","55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417"],["5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143","efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868"],["290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba","e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a"],["af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45","f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6"],["766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a","744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996"],["59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e","c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e"],["f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8","e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d"],["7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c","30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2"],["948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519","e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e"],["7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab","100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437"],["3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca","ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311"],["d3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf","8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4"],["1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610","68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575"],["733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4","f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d"],["15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c","d56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d"],["a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940","edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629"],["e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980","a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06"],["311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3","66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374"],["34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf","9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee"],["f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63","4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1"],["d7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448","fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b"],["32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf","5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661"],["7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5","8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6"],["ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6","8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e"],["16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5","5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d"],["eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99","f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc"],["78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51","f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4"],["494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5","42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c"],["a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5","204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b"],["c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997","4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913"],["841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881","73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154"],["5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5","39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865"],["36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66","d2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc"],["336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726","ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224"],["8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede","6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e"],["1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94","60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6"],["85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31","3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511"],["29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51","b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b"],["a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252","ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2"],["4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5","cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c"],["d24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b","6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3"],["ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4","322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d"],["af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f","6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700"],["e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889","2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4"],["591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246","b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196"],["11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984","998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4"],["3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a","b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257"],["cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030","bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13"],["c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197","6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096"],["c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593","c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38"],["a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef","21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f"],["347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38","60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448"],["da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a","49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a"],["c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111","5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4"],["4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502","7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437"],["3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea","be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7"],["cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26","8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d"],["b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986","39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a"],["d4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e","62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54"],["48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4","25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77"],["dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda","ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517"],["6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859","cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10"],["e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f","f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125"],["eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c","6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e"],["13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942","fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1"],["ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a","1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2"],["b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80","5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423"],["ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d","438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8"],["8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1","cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758"],["52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63","c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375"],["e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352","6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d"],["7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193","ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec"],["5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00","9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0"],["32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58","ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c"],["e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7","d3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4"],["8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8","c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f"],["4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e","67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649"],["3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d","cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826"],["674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b","299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5"],["d32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f","f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87"],["30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6","462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b"],["be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297","62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc"],["93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a","7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c"],["b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c","ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f"],["d5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52","4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a"],["d3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb","bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46"],["463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065","bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f"],["7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917","603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03"],["74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9","cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08"],["30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3","553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8"],["9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57","712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373"],["176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66","ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3"],["75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8","9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8"],["809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721","9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1"],["1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180","4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9"]]}}},function(e,t,n){"use strict";var r=n(29),i=n(343),o=n(47),s=n(128),a=n(124),u=o.assert,c=n(344),f=n(345);function l(e){if(!(this instanceof l))return new l(e);"string"==typeof e&&(u(Object.prototype.hasOwnProperty.call(s,e),"Unknown curve "+e),e=s[e]),e instanceof s.PresetCurve&&(e={curve:e}),this.curve=e.curve.curve,this.n=this.curve.n,this.nh=this.n.ushrn(1),this.g=this.curve.g,this.g=e.curve.g,this.g.precompute(e.curve.n.bitLength()+1),this.hash=e.hash||e.curve.hash}e.exports=l,l.prototype.keyPair=function(e){return new c(this,e)},l.prototype.keyFromPrivate=function(e,t){return c.fromPrivate(this,e,t)},l.prototype.keyFromPublic=function(e,t){return c.fromPublic(this,e,t)},l.prototype.genKeyPair=function(e){e||(e={});for(var t=new i({hash:this.hash,pers:e.pers,persEnc:e.persEnc||"utf8",entropy:e.entropy||a(this.hash.hmacStrength),entropyEnc:e.entropy&&e.entropyEnc||"utf8",nonce:this.n.toArray()}),n=this.n.byteLength(),o=this.n.sub(new r(2));;){var s=new r(t.generate(n));if(!(s.cmp(o)>0))return s.iaddn(1),this.keyFromPrivate(s)}},l.prototype._truncateToN=function(e,t){var n=8*e.byteLength()-this.n.bitLength();return n>0&&(e=e.ushrn(n)),!t&&e.cmp(this.n)>=0?e.sub(this.n):e},l.prototype.sign=function(e,t,n,o){"object"==typeof n&&(o=n,n=null),o||(o={}),t=this.keyFromPrivate(t,n),e=this._truncateToN(new r(e,16));for(var s=this.n.byteLength(),a=t.getPrivate().toArray("be",s),u=e.toArray("be",s),c=new i({hash:this.hash,entropy:a,nonce:u,pers:o.pers,persEnc:o.persEnc||"utf8"}),l=this.n.sub(new r(1)),d=0;;d++){var h=o.k?o.k(d):new r(c.generate(this.n.byteLength()));if(!((h=this._truncateToN(h,!0)).cmpn(1)<=0||h.cmp(l)>=0)){var p=this.g.mul(h);if(!p.isInfinity()){var v=p.getX(),g=v.umod(this.n);if(0!==g.cmpn(0)){var m=h.invm(this.n).mul(g.mul(t.getPrivate()).iadd(e));if(0!==(m=m.umod(this.n)).cmpn(0)){var b=(p.getY().isOdd()?1:0)|(0!==v.cmp(g)?2:0);return o.canonical&&m.cmp(this.nh)>0&&(m=this.n.sub(m),b^=1),new f({r:g,s:m,recoveryParam:b})}}}}}},l.prototype.verify=function(e,t,n,i){e=this._truncateToN(new r(e,16)),n=this.keyFromPublic(n,i);var o=(t=new f(t,"hex")).r,s=t.s;if(o.cmpn(1)<0||o.cmp(this.n)>=0)return!1;if(s.cmpn(1)<0||s.cmp(this.n)>=0)return!1;var a,u=s.invm(this.n),c=u.mul(e).umod(this.n),l=u.mul(o).umod(this.n);return this.curve._maxwellTrick?!(a=this.g.jmulAdd(c,n.getPublic(),l)).isInfinity()&&a.eqXToP(o):!(a=this.g.mulAdd(c,n.getPublic(),l)).isInfinity()&&0===a.getX().umod(this.n).cmp(o)},l.prototype.recoverPubKey=function(e,t,n,i){u((3&n)===n,"The recovery param is more than two bits"),t=new f(t,i);var o=this.n,s=new r(e),a=t.r,c=t.s,l=1&n,d=n>>1;if(a.cmp(this.curve.p.umod(this.curve.n))>=0&&d)throw new Error("Unable to find sencond key candinate");a=d?this.curve.pointFromX(a.add(this.curve.n),l):this.curve.pointFromX(a,l);var h=t.r.invm(o),p=o.sub(s).mul(h).umod(o),v=c.mul(h).umod(o);return this.g.mulAdd(p,a,v)},l.prototype.getKeyRecoveryParam=function(e,t,n,r){if(null!==(t=new f(t,r)).recoveryParam)return t.recoveryParam;for(var i=0;i<4;i++){var o;try{o=this.recoverPubKey(e,t,i)}catch(e){continue}if(o.eq(n))return i}throw new Error("Unable to find valid recovery factor")}},function(e,t,n){"use strict";var r=n(129),i=n(198),o=n(46);function s(e){if(!(this instanceof s))return new s(e);this.hash=e.hash,this.predResist=!!e.predResist,this.outLen=this.hash.outSize,this.minEntropy=e.minEntropy||this.hash.hmacStrength,this._reseed=null,this.reseedInterval=null,this.K=null,this.V=null;var t=i.toArray(e.entropy,e.entropyEnc||"hex"),n=i.toArray(e.nonce,e.nonceEnc||"hex"),r=i.toArray(e.pers,e.persEnc||"hex");o(t.length>=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._init(t,n,r)}e.exports=s,s.prototype._init=function(e,t,n){var r=e.concat(t).concat(n);this.K=new Array(this.outLen/8),this.V=new Array(this.outLen/8);for(var i=0;i<this.V.length;i++)this.K[i]=0,this.V[i]=1;this._update(r),this._reseed=1,this.reseedInterval=281474976710656},s.prototype._hmac=function(){return new r.hmac(this.hash,this.K)},s.prototype._update=function(e){var t=this._hmac().update(this.V).update([0]);e&&(t=t.update(e)),this.K=t.digest(),this.V=this._hmac().update(this.V).digest(),e&&(this.K=this._hmac().update(this.V).update([1]).update(e).digest(),this.V=this._hmac().update(this.V).digest())},s.prototype.reseed=function(e,t,n,r){"string"!=typeof t&&(r=n,n=t,t=null),e=i.toArray(e,t),n=i.toArray(n,r),o(e.length>=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._update(e.concat(n||[])),this._reseed=1},s.prototype.generate=function(e,t,n,r){if(this._reseed>this.reseedInterval)throw new Error("Reseed is required");"string"!=typeof t&&(r=n,n=t,t=null),n&&(n=i.toArray(n,r||"hex"),this._update(n));for(var o=[];o.length<e;)this.V=this._hmac().update(this.V).digest(),o=o.concat(this.V);var s=o.slice(0,e);return this._update(n),this._reseed++,i.encode(s,t)}},function(e,t,n){"use strict";var r=n(29),i=n(47).assert;function o(e,t){this.ec=e,this.priv=null,this.pub=null,t.priv&&this._importPrivate(t.priv,t.privEnc),t.pub&&this._importPublic(t.pub,t.pubEnc)}e.exports=o,o.fromPublic=function(e,t,n){return t instanceof o?t:new o(e,{pub:t,pubEnc:n})},o.fromPrivate=function(e,t,n){return t instanceof o?t:new o(e,{priv:t,privEnc:n})},o.prototype.validate=function(){var e=this.getPublic();return e.isInfinity()?{result:!1,reason:"Invalid public key"}:e.validate()?e.mul(this.ec.curve.n).isInfinity()?{result:!0,reason:null}:{result:!1,reason:"Public key * N != O"}:{result:!1,reason:"Public key is not a point"}},o.prototype.getPublic=function(e,t){return"string"==typeof e&&(t=e,e=null),this.pub||(this.pub=this.ec.g.mul(this.priv)),t?this.pub.encode(t,e):this.pub},o.prototype.getPrivate=function(e){return"hex"===e?this.priv.toString(16,2):this.priv},o.prototype._importPrivate=function(e,t){this.priv=new r(e,t||16),this.priv=this.priv.umod(this.ec.curve.n)},o.prototype._importPublic=function(e,t){if(e.x||e.y)return"mont"===this.ec.curve.type?i(e.x,"Need x coordinate"):"short"!==this.ec.curve.type&&"edwards"!==this.ec.curve.type||i(e.x&&e.y,"Need both x and y coordinate"),void(this.pub=this.ec.curve.point(e.x,e.y));this.pub=this.ec.curve.decodePoint(e,t)},o.prototype.derive=function(e){return e.validate()||i(e.validate(),"public point not validated"),e.mul(this.priv).getX()},o.prototype.sign=function(e,t,n){return this.ec.sign(e,this,t,n)},o.prototype.verify=function(e,t){return this.ec.verify(e,t,this)},o.prototype.inspect=function(){return"<Key priv: "+(this.priv&&this.priv.toString(16,2))+" pub: "+(this.pub&&this.pub.inspect())+" >"}},function(e,t,n){"use strict";var r=n(29),i=n(47),o=i.assert;function s(e,t){if(e instanceof s)return e;this._importDER(e,t)||(o(e.r&&e.s,"Signature without r or s"),this.r=new r(e.r,16),this.s=new r(e.s,16),void 0===e.recoveryParam?this.recoveryParam=null:this.recoveryParam=e.recoveryParam)}function a(){this.place=0}function u(e,t){var n=e[t.place++];if(!(128&n))return n;var r=15&n;if(0===r||r>4)return!1;for(var i=0,o=0,s=t.place;o<r;o++,s++)i<<=8,i|=e[s],i>>>=0;return!(i<=127)&&(t.place=s,i)}function c(e){for(var t=0,n=e.length-1;!e[t]&&!(128&e[t+1])&&t<n;)t++;return 0===t?e:e.slice(t)}function f(e,t){if(t<128)e.push(t);else{var n=1+(Math.log(t)/Math.LN2>>>3);for(e.push(128|n);--n;)e.push(t>>>(n<<3)&255);e.push(t)}}e.exports=s,s.prototype._importDER=function(e,t){e=i.toArray(e,t);var n=new a;if(48!==e[n.place++])return!1;var o=u(e,n);if(!1===o)return!1;if(o+n.place!==e.length)return!1;if(2!==e[n.place++])return!1;var s=u(e,n);if(!1===s)return!1;var c=e.slice(n.place,s+n.place);if(n.place+=s,2!==e[n.place++])return!1;var f=u(e,n);if(!1===f)return!1;if(e.length!==f+n.place)return!1;var l=e.slice(n.place,f+n.place);if(0===c[0]){if(!(128&c[1]))return!1;c=c.slice(1)}if(0===l[0]){if(!(128&l[1]))return!1;l=l.slice(1)}return this.r=new r(c),this.s=new r(l),this.recoveryParam=null,!0},s.prototype.toDER=function(e){var t=this.r.toArray(),n=this.s.toArray();for(128&t[0]&&(t=[0].concat(t)),128&n[0]&&(n=[0].concat(n)),t=c(t),n=c(n);!(n[0]||128&n[1]);)n=n.slice(1);var r=[2];f(r,t.length),(r=r.concat(t)).push(2),f(r,n.length);var o=r.concat(n),s=[48];return f(s,o.length),s=s.concat(o),i.encode(s,e)}},function(e,t,n){"use strict";var r=n(129),i=n(128),o=n(47),s=o.assert,a=o.parseBytes,u=n(347),c=n(348);function f(e){if(s("ed25519"===e,"only tested with ed25519 so far"),!(this instanceof f))return new f(e);e=i[e].curve,this.curve=e,this.g=e.g,this.g.precompute(e.n.bitLength()+1),this.pointClass=e.point().constructor,this.encodingLength=Math.ceil(e.n.bitLength()/8),this.hash=r.sha512}e.exports=f,f.prototype.sign=function(e,t){e=a(e);var n=this.keyFromSecret(t),r=this.hashInt(n.messagePrefix(),e),i=this.g.mul(r),o=this.encodePoint(i),s=this.hashInt(o,n.pubBytes(),e).mul(n.priv()),u=r.add(s).umod(this.curve.n);return this.makeSignature({R:i,S:u,Rencoded:o})},f.prototype.verify=function(e,t,n){e=a(e),t=this.makeSignature(t);var r=this.keyFromPublic(n),i=this.hashInt(t.Rencoded(),r.pubBytes(),e),o=this.g.mul(t.S());return t.R().add(r.pub().mul(i)).eq(o)},f.prototype.hashInt=function(){for(var e=this.hash(),t=0;t<arguments.length;t++)e.update(arguments[t]);return o.intFromLE(e.digest()).umod(this.curve.n)},f.prototype.keyFromPublic=function(e){return u.fromPublic(this,e)},f.prototype.keyFromSecret=function(e){return u.fromSecret(this,e)},f.prototype.makeSignature=function(e){return e instanceof c?e:new c(this,e)},f.prototype.encodePoint=function(e){var t=e.getY().toArray("le",this.encodingLength);return t[this.encodingLength-1]|=e.getX().isOdd()?128:0,t},f.prototype.decodePoint=function(e){var t=(e=o.parseBytes(e)).length-1,n=e.slice(0,t).concat(-129&e[t]),r=0!=(128&e[t]),i=o.intFromLE(n);return this.curve.pointFromY(i,r)},f.prototype.encodeInt=function(e){return e.toArray("le",this.encodingLength)},f.prototype.decodeInt=function(e){return o.intFromLE(e)},f.prototype.isPoint=function(e){return e instanceof this.pointClass}},function(e,t,n){"use strict";var r=n(47),i=r.assert,o=r.parseBytes,s=r.cachedProperty;function a(e,t){this.eddsa=e,this._secret=o(t.secret),e.isPoint(t.pub)?this._pub=t.pub:this._pubBytes=o(t.pub)}a.fromPublic=function(e,t){return t instanceof a?t:new a(e,{pub:t})},a.fromSecret=function(e,t){return t instanceof a?t:new a(e,{secret:t})},a.prototype.secret=function(){return this._secret},s(a,"pubBytes",(function(){return this.eddsa.encodePoint(this.pub())})),s(a,"pub",(function(){return this._pubBytes?this.eddsa.decodePoint(this._pubBytes):this.eddsa.g.mul(this.priv())})),s(a,"privBytes",(function(){var e=this.eddsa,t=this.hash(),n=e.encodingLength-1,r=t.slice(0,e.encodingLength);return r[0]&=248,r[n]&=127,r[n]|=64,r})),s(a,"priv",(function(){return this.eddsa.decodeInt(this.privBytes())})),s(a,"hash",(function(){return this.eddsa.hash().update(this.secret()).digest()})),s(a,"messagePrefix",(function(){return this.hash().slice(this.eddsa.encodingLength)})),a.prototype.sign=function(e){return i(this._secret,"KeyPair can only verify"),this.eddsa.sign(e,this)},a.prototype.verify=function(e,t){return this.eddsa.verify(e,t,this)},a.prototype.getSecret=function(e){return i(this._secret,"KeyPair is public only"),r.encode(this.secret(),e)},a.prototype.getPublic=function(e){return r.encode(this.pubBytes(),e)},e.exports=a},function(e,t,n){"use strict";var r=n(29),i=n(47),o=i.assert,s=i.cachedProperty,a=i.parseBytes;function u(e,t){this.eddsa=e,"object"!=typeof t&&(t=a(t)),Array.isArray(t)&&(t={R:t.slice(0,e.encodingLength),S:t.slice(e.encodingLength)}),o(t.R&&t.S,"Signature without R or S"),e.isPoint(t.R)&&(this._R=t.R),t.S instanceof r&&(this._S=t.S),this._Rencoded=Array.isArray(t.R)?t.R:t.Rencoded,this._Sencoded=Array.isArray(t.S)?t.S:t.Sencoded}s(u,"S",(function(){return this.eddsa.decodeInt(this.Sencoded())})),s(u,"R",(function(){return this.eddsa.decodePoint(this.Rencoded())})),s(u,"Rencoded",(function(){return this.eddsa.encodePoint(this.R())})),s(u,"Sencoded",(function(){return this.eddsa.encodeInt(this.S())})),u.prototype.toBytes=function(){return this.Rencoded().concat(this.Sencoded())},u.prototype.toHex=function(){return i.encode(this.toBytes(),"hex").toUpperCase()},e.exports=u},function(e,t){},function(e,t,n){"use strict";var r=n(204);t.certificate=n(356);var i=r.define("RSAPrivateKey",(function(){this.seq().obj(this.key("version").int(),this.key("modulus").int(),this.key("publicExponent").int(),this.key("privateExponent").int(),this.key("prime1").int(),this.key("prime2").int(),this.key("exponent1").int(),this.key("exponent2").int(),this.key("coefficient").int())}));t.RSAPrivateKey=i;var o=r.define("RSAPublicKey",(function(){this.seq().obj(this.key("modulus").int(),this.key("publicExponent").int())}));t.RSAPublicKey=o;var s=r.define("SubjectPublicKeyInfo",(function(){this.seq().obj(this.key("algorithm").use(a),this.key("subjectPublicKey").bitstr())}));t.PublicKey=s;var a=r.define("AlgorithmIdentifier",(function(){this.seq().obj(this.key("algorithm").objid(),this.key("none").null_().optional(),this.key("curve").objid().optional(),this.key("params").seq().obj(this.key("p").int(),this.key("q").int(),this.key("g").int()).optional())})),u=r.define("PrivateKeyInfo",(function(){this.seq().obj(this.key("version").int(),this.key("algorithm").use(a),this.key("subjectPrivateKey").octstr())}));t.PrivateKey=u;var c=r.define("EncryptedPrivateKeyInfo",(function(){this.seq().obj(this.key("algorithm").seq().obj(this.key("id").objid(),this.key("decrypt").seq().obj(this.key("kde").seq().obj(this.key("id").objid(),this.key("kdeparams").seq().obj(this.key("salt").octstr(),this.key("iters").int())),this.key("cipher").seq().obj(this.key("algo").objid(),this.key("iv").octstr()))),this.key("subjectPrivateKey").octstr())}));t.EncryptedPrivateKey=c;var f=r.define("DSAPrivateKey",(function(){this.seq().obj(this.key("version").int(),this.key("p").int(),this.key("q").int(),this.key("g").int(),this.key("pub_key").int(),this.key("priv_key").int())}));t.DSAPrivateKey=f,t.DSAparam=r.define("DSAparam",(function(){this.int()}));var l=r.define("ECPrivateKey",(function(){this.seq().obj(this.key("version").int(),this.key("privateKey").octstr(),this.key("parameters").optional().explicit(0).use(d),this.key("publicKey").optional().explicit(1).bitstr())}));t.ECPrivateKey=l;var d=r.define("ECParameters",(function(){this.choice({namedCurve:this.objid()})}));t.signature=r.define("signature",(function(){this.seq().obj(this.key("r").int(),this.key("s").int())}))},function(e,t,n){"use strict";const r=n(205),i=n(207),o=n(7);function s(e,t){this.name=e,this.body=t,this.decoders={},this.encoders={}}t.define=function(e,t){return new s(e,t)},s.prototype._createNamed=function(e){const t=this.name;function n(e){this._initNamed(e,t)}return o(n,e),n.prototype._initNamed=function(t,n){e.call(this,t,n)},new n(this)},s.prototype._getDecoder=function(e){return e=e||"der",this.decoders.hasOwnProperty(e)||(this.decoders[e]=this._createNamed(i[e])),this.decoders[e]},s.prototype.decode=function(e,t,n){return this._getDecoder(t).decode(e,n)},s.prototype._getEncoder=function(e){return e=e||"der",this.encoders.hasOwnProperty(e)||(this.encoders[e]=this._createNamed(r[e])),this.encoders[e]},s.prototype.encode=function(e,t,n){return this._getEncoder(t).encode(e,n)}},function(e,t,n){"use strict";const r=n(7),i=n(206);function o(e){i.call(this,e),this.enc="pem"}r(o,i),e.exports=o,o.prototype.encode=function(e,t){const n=i.prototype.encode.call(this,e).toString("base64"),r=["-----BEGIN "+t.label+"-----"];for(let e=0;e<n.length;e+=64)r.push(n.slice(e,e+64));return r.push("-----END "+t.label+"-----"),r.join("\n")}},function(e,t,n){"use strict";const r=n(7),i=n(130).Buffer,o=n(208);function s(e){o.call(this,e),this.enc="pem"}r(s,o),e.exports=s,s.prototype.decode=function(e,t){const n=e.toString().split(/[\r\n]+/g),r=t.label.toUpperCase(),s=/^-----(BEGIN|END) ([^-]+)-----$/;let a=-1,u=-1;for(let e=0;e<n.length;e++){const t=n[e].match(s);if(null!==t&&t[2]===r){if(-1!==a){if("END"!==t[1])break;u=e;break}if("BEGIN"!==t[1])break;a=e}}if(-1===a||-1===u)throw new Error("PEM section not found for: "+r);const c=n.slice(a+1,u).join("");c.replace(/[^a-z0-9+/=]+/gi,"");const f=i.from(c,"base64");return o.prototype.decode.call(this,f,t)}},function(e,t,n){"use strict";const r=t;r.Reporter=n(132).Reporter,r.DecoderBuffer=n(83).DecoderBuffer,r.EncoderBuffer=n(83).EncoderBuffer,r.Node=n(131)},function(e,t,n){"use strict";const r=t;r._reverse=function(e){const t={};return Object.keys(e).forEach((function(n){(0|n)==n&&(n|=0);const r=e[n];t[r]=n})),t},r.der=n(133)},function(e,t,n){"use strict";var r=n(204),i=r.define("Time",(function(){this.choice({utcTime:this.utctime(),generalTime:this.gentime()})})),o=r.define("AttributeTypeValue",(function(){this.seq().obj(this.key("type").objid(),this.key("value").any())})),s=r.define("AlgorithmIdentifier",(function(){this.seq().obj(this.key("algorithm").objid(),this.key("parameters").optional(),this.key("curve").objid().optional())})),a=r.define("SubjectPublicKeyInfo",(function(){this.seq().obj(this.key("algorithm").use(s),this.key("subjectPublicKey").bitstr())})),u=r.define("RelativeDistinguishedName",(function(){this.setof(o)})),c=r.define("RDNSequence",(function(){this.seqof(u)})),f=r.define("Name",(function(){this.choice({rdnSequence:this.use(c)})})),l=r.define("Validity",(function(){this.seq().obj(this.key("notBefore").use(i),this.key("notAfter").use(i))})),d=r.define("Extension",(function(){this.seq().obj(this.key("extnID").objid(),this.key("critical").bool().def(!1),this.key("extnValue").octstr())})),h=r.define("TBSCertificate",(function(){this.seq().obj(this.key("version").explicit(0).int().optional(),this.key("serialNumber").int(),this.key("signature").use(s),this.key("issuer").use(f),this.key("validity").use(l),this.key("subject").use(f),this.key("subjectPublicKeyInfo").use(a),this.key("issuerUniqueID").implicit(1).bitstr().optional(),this.key("subjectUniqueID").implicit(2).bitstr().optional(),this.key("extensions").explicit(3).seqof(d).optional())})),p=r.define("X509Certificate",(function(){this.seq().obj(this.key("tbsCertificate").use(h),this.key("signatureAlgorithm").use(s),this.key("signatureValue").bitstr())}));e.exports=p},function(e){e.exports=JSON.parse('{"2.16.840.1.101.3.4.1.1":"aes-128-ecb","2.16.840.1.101.3.4.1.2":"aes-128-cbc","2.16.840.1.101.3.4.1.3":"aes-128-ofb","2.16.840.1.101.3.4.1.4":"aes-128-cfb","2.16.840.1.101.3.4.1.21":"aes-192-ecb","2.16.840.1.101.3.4.1.22":"aes-192-cbc","2.16.840.1.101.3.4.1.23":"aes-192-ofb","2.16.840.1.101.3.4.1.24":"aes-192-cfb","2.16.840.1.101.3.4.1.41":"aes-256-ecb","2.16.840.1.101.3.4.1.42":"aes-256-cbc","2.16.840.1.101.3.4.1.43":"aes-256-ofb","2.16.840.1.101.3.4.1.44":"aes-256-cfb"}')},function(e,t,n){var r=/Proc-Type: 4,ENCRYPTED[\n\r]+DEK-Info: AES-((?:128)|(?:192)|(?:256))-CBC,([0-9A-H]+)[\n\r]+([0-9A-z\n\r+/=]+)[\n\r]+/m,i=/^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----/m,o=/^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----([0-9A-z\n\r+/=]+)-----END \1-----$/m,s=n(94),a=n(122),u=n(8).Buffer;e.exports=function(e,t){var n,c=e.toString(),f=c.match(r);if(f){var l="aes"+f[1],d=u.from(f[2],"hex"),h=u.from(f[3].replace(/[\r\n]/g,""),"base64"),p=s(t,d.slice(0,8),parseInt(f[1],10)).key,v=[],g=a.createDecipheriv(l,p,d);v.push(g.update(h)),v.push(g.final()),n=u.concat(v)}else{var m=c.match(o);n=u.from(m[2].replace(/[\r\n]/g,""),"base64")}return{tag:c.match(i)[1],data:n}}},function(e,t,n){var r=n(8).Buffer,i=n(203),o=n(127).ec,s=n(96),a=n(209);function u(e,t){if(e.cmpn(0)<=0)throw new Error("invalid sig");if(e.cmp(t)>=t)throw new Error("invalid sig")}e.exports=function(e,t,n,c,f){var l=s(n);if("ec"===l.type){if("ecdsa"!==c&&"ecdsa/rsa"!==c)throw new Error("wrong public key type");return function(e,t,n){var r=a[n.data.algorithm.curve.join(".")];if(!r)throw new Error("unknown curve "+n.data.algorithm.curve.join("."));var i=new o(r),s=n.data.subjectPrivateKey.data;return i.verify(t,e,s)}(e,t,l)}if("dsa"===l.type){if("dsa"!==c)throw new Error("wrong public key type");return function(e,t,n){var r=n.data.p,o=n.data.q,a=n.data.g,c=n.data.pub_key,f=s.signature.decode(e,"der"),l=f.s,d=f.r;u(l,o),u(d,o);var h=i.mont(r),p=l.invm(o);return 0===a.toRed(h).redPow(new i(t).mul(p).mod(o)).fromRed().mul(c.toRed(h).redPow(d.mul(p).mod(o)).fromRed()).mod(r).mod(o).cmp(d)}(e,t,l)}if("rsa"!==c&&"ecdsa/rsa"!==c)throw new Error("wrong public key type");t=r.concat([f,t]);for(var d=l.modulus.byteLength(),h=[1],p=0;t.length+h.length+2<d;)h.push(255),p++;h.push(0);for(var v=-1;++v<t.length;)h.push(t[v]);h=r.from(h);var g=i.mont(l.modulus);e=(e=new i(e).toRed(g)).redPow(new i(l.publicExponent)),e=r.from(e.fromRed().toArray());var m=p<8?1:0;for(d=Math.min(e.length,h.length),e.length!==h.length&&(m=1),v=-1;++v<d;)m|=e[v]^h[v];return 0===m}},function(e,t,n){(function(t){var r=n(127),i=n(29);e.exports=function(e){return new s(e)};var o={secp256k1:{name:"secp256k1",byteLength:32},secp224r1:{name:"p224",byteLength:28},prime256v1:{name:"p256",byteLength:32},prime192v1:{name:"p192",byteLength:24},ed25519:{name:"ed25519",byteLength:32},secp384r1:{name:"p384",byteLength:48},secp521r1:{name:"p521",byteLength:66}};function s(e){this.curveType=o[e],this.curveType||(this.curveType={name:e}),this.curve=new r.ec(this.curveType.name),this.keys=void 0}function a(e,n,r){Array.isArray(e)||(e=e.toArray());var i=new t(e);if(r&&i.length<r){var o=new t(r-i.length);o.fill(0),i=t.concat([o,i])}return n?i.toString(n):i}o.p224=o.secp224r1,o.p256=o.secp256r1=o.prime256v1,o.p192=o.secp192r1=o.prime192v1,o.p384=o.secp384r1,o.p521=o.secp521r1,s.prototype.generateKeys=function(e,t){return this.keys=this.curve.genKeyPair(),this.getPublicKey(e,t)},s.prototype.computeSecret=function(e,n,r){return n=n||"utf8",t.isBuffer(e)||(e=new t(e,n)),a(this.curve.keyFromPublic(e).getPublic().mul(this.keys.getPrivate()).getX(),r,this.curveType.byteLength)},s.prototype.getPublicKey=function(e,t){var n=this.keys.getPublic("compressed"===t,!0);return"hybrid"===t&&(n[n.length-1]%2?n[0]=7:n[0]=6),a(n,e)},s.prototype.getPrivateKey=function(e){return a(this.keys.getPrivate(),e)},s.prototype.setPublicKey=function(e,n){return n=n||"utf8",t.isBuffer(e)||(e=new t(e,n)),this.keys._importPublic(e),this},s.prototype.setPrivateKey=function(e,n){n=n||"utf8",t.isBuffer(e)||(e=new t(e,n));var r=new i(e);return r=r.toString(16),this.keys=this.curve.genKeyPair(),this.keys._importPrivate(r),this}}).call(this,n(6).Buffer)},function(e,t,n){t.publicEncrypt=n(362),t.privateDecrypt=n(363),t.privateEncrypt=function(e,n){return t.publicEncrypt(e,n,!0)},t.publicDecrypt=function(e,n){return t.privateDecrypt(e,n,!0)}},function(e,t,n){var r=n(96),i=n(66),o=n(79),s=n(210),a=n(211),u=n(29),c=n(212),f=n(126),l=n(8).Buffer;e.exports=function(e,t,n){var d;d=e.padding?e.padding:n?1:4;var h,p=r(e);if(4===d)h=function(e,t){var n=e.modulus.byteLength(),r=t.length,c=o("sha1").update(l.alloc(0)).digest(),f=c.length,d=2*f;if(r>n-d-2)throw new Error("message too long");var h=l.alloc(n-r-d-2),p=n-f-1,v=i(f),g=a(l.concat([c,h,l.alloc(1,1),t],p),s(v,p)),m=a(v,s(g,f));return new u(l.concat([l.alloc(1),m,g],n))}(p,t);else if(1===d)h=function(e,t,n){var r,o=t.length,s=e.modulus.byteLength();if(o>s-11)throw new Error("message too long");r=n?l.alloc(s-o-3,255):function(e){var t,n=l.allocUnsafe(e),r=0,o=i(2*e),s=0;for(;r<e;)s===o.length&&(o=i(2*e),s=0),(t=o[s++])&&(n[r++]=t);return n}(s-o-3);return new u(l.concat([l.from([0,n?1:2]),r,l.alloc(1),t],s))}(p,t,n);else{if(3!==d)throw new Error("unknown padding");if((h=new u(t)).cmp(p.modulus)>=0)throw new Error("data too long for modulus")}return n?f(h,p):c(h,p)}},function(e,t,n){var r=n(96),i=n(210),o=n(211),s=n(29),a=n(126),u=n(79),c=n(212),f=n(8).Buffer;e.exports=function(e,t,n){var l;l=e.padding?e.padding:n?1:4;var d,h=r(e),p=h.modulus.byteLength();if(t.length>p||new s(t).cmp(h.modulus)>=0)throw new Error("decryption error");d=n?c(new s(t),h):a(t,h);var v=f.alloc(p-d.length);if(d=f.concat([v,d],p),4===l)return function(e,t){var n=e.modulus.byteLength(),r=u("sha1").update(f.alloc(0)).digest(),s=r.length;if(0!==t[0])throw new Error("decryption error");var a=t.slice(1,s+1),c=t.slice(s+1),l=o(a,i(c,s)),d=o(c,i(l,n-s-1));if(function(e,t){e=f.from(e),t=f.from(t);var n=0,r=e.length;e.length!==t.length&&(n++,r=Math.min(e.length,t.length));var i=-1;for(;++i<r;)n+=e[i]^t[i];return n}(r,d.slice(0,s)))throw new Error("decryption error");var h=s;for(;0===d[h];)h++;if(1!==d[h++])throw new Error("decryption error");return d.slice(h)}(h,d);if(1===l)return function(e,t,n){var r=t.slice(0,2),i=2,o=0;for(;0!==t[i++];)if(i>=t.length){o++;break}var s=t.slice(2,i-1);("0002"!==r.toString("hex")&&!n||"0001"!==r.toString("hex")&&n)&&o++;s.length<8&&o++;if(o)throw new Error("decryption error");return t.slice(i)}(0,d,n);if(3===l)return d;throw new Error("unknown padding")}},function(e,t,n){"use strict";(function(e,r){function i(){throw new Error("secure random number generation not supported by this browser\nuse chrome, FireFox or Internet Explorer 11")}var o=n(8),s=n(66),a=o.Buffer,u=o.kMaxLength,c=e.crypto||e.msCrypto,f=Math.pow(2,32)-1;function l(e,t){if("number"!=typeof e||e!=e)throw new TypeError("offset must be a number");if(e>f||e<0)throw new TypeError("offset must be a uint32");if(e>u||e>t)throw new RangeError("offset out of range")}function d(e,t,n){if("number"!=typeof e||e!=e)throw new TypeError("size must be a number");if(e>f||e<0)throw new TypeError("size must be a uint32");if(e+t>n||e>u)throw new RangeError("buffer too small")}function h(e,t,n,i){if(r.browser){var o=e.buffer,a=new Uint8Array(o,t,n);return c.getRandomValues(a),i?void r.nextTick((function(){i(null,e)})):e}if(!i)return s(n).copy(e,t),e;s(n,(function(n,r){if(n)return i(n);r.copy(e,t),i(null,e)}))}c&&c.getRandomValues||!r.browser?(t.randomFill=function(t,n,r,i){if(!(a.isBuffer(t)||t instanceof e.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');if("function"==typeof n)i=n,n=0,r=t.length;else if("function"==typeof r)i=r,r=t.length-n;else if("function"!=typeof i)throw new TypeError('"cb" argument must be a function');return l(n,t.length),d(r,n,t.length),h(t,n,r,i)},t.randomFillSync=function(t,n,r){void 0===n&&(n=0);if(!(a.isBuffer(t)||t instanceof e.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');l(n,t.length),void 0===r&&(r=t.length-n);return d(r,n,t.length),h(t,n,r)}):(t.randomFill=i,t.randomFillSync=i)}).call(this,n(31),n(20))},function(e,t,n){e.exports=self.fetch||(self.fetch=n(213).default||n(213))},function(e,t,n){(function(e,r){var i;/*! https://mths.be/punycode v1.4.1 by @mathias */!function(o){t&&t.nodeType,e&&e.nodeType;var s="object"==typeof r&&r;s.global!==s&&s.window!==s&&s.self;var a,u=2147483647,c=/^xn--/,f=/[^\x20-\x7E]/,l=/[\x2E\u3002\uFF0E\uFF61]/g,d={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},h=Math.floor,p=String.fromCharCode;function v(e){throw new RangeError(d[e])}function g(e,t){for(var n=e.length,r=[];n--;)r[n]=t(e[n]);return r}function m(e,t){var n=e.split("@"),r="";return n.length>1&&(r=n[0]+"@",e=n[1]),r+g((e=e.replace(l,".")).split("."),t).join(".")}function b(e){for(var t,n,r=[],i=0,o=e.length;i<o;)(t=e.charCodeAt(i++))>=55296&&t<=56319&&i<o?56320==(64512&(n=e.charCodeAt(i++)))?r.push(((1023&t)<<10)+(1023&n)+65536):(r.push(t),i--):r.push(t);return r}function y(e){return g(e,(function(e){var t="";return e>65535&&(t+=p((e-=65536)>>>10&1023|55296),e=56320|1023&e),t+=p(e)})).join("")}function w(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function _(e,t,n){var r=0;for(e=n?h(e/700):e>>1,e+=h(e/t);e>455;r+=36)e=h(e/35);return h(r+36*e/(e+38))}function S(e){var t,n,r,i,o,s,a,c,f,l,d,p=[],g=e.length,m=0,b=128,w=72;for((n=e.lastIndexOf("-"))<0&&(n=0),r=0;r<n;++r)e.charCodeAt(r)>=128&&v("not-basic"),p.push(e.charCodeAt(r));for(i=n>0?n+1:0;i<g;){for(o=m,s=1,a=36;i>=g&&v("invalid-input"),((c=(d=e.charCodeAt(i++))-48<10?d-22:d-65<26?d-65:d-97<26?d-97:36)>=36||c>h((u-m)/s))&&v("overflow"),m+=c*s,!(c<(f=a<=w?1:a>=w+26?26:a-w));a+=36)s>h(u/(l=36-f))&&v("overflow"),s*=l;w=_(m-o,t=p.length+1,0==o),h(m/t)>u-b&&v("overflow"),b+=h(m/t),m%=t,p.splice(m++,0,b)}return y(p)}function E(e){var t,n,r,i,o,s,a,c,f,l,d,g,m,y,S,E=[];for(g=(e=b(e)).length,t=128,n=0,o=72,s=0;s<g;++s)(d=e[s])<128&&E.push(p(d));for(r=i=E.length,i&&E.push("-");r<g;){for(a=u,s=0;s<g;++s)(d=e[s])>=t&&d<a&&(a=d);for(a-t>h((u-n)/(m=r+1))&&v("overflow"),n+=(a-t)*m,t=a,s=0;s<g;++s)if((d=e[s])<t&&++n>u&&v("overflow"),d==t){for(c=n,f=36;!(c<(l=f<=o?1:f>=o+26?26:f-o));f+=36)S=c-l,y=36-l,E.push(p(w(l+S%y,0))),c=h(S/y);E.push(p(w(c,0))),o=_(n,m,r==i),n=0,++r}++n,++t}return E.join("")}a={version:"1.4.1",ucs2:{decode:b,encode:y},decode:S,encode:E,toASCII:function(e){return m(e,(function(e){return f.test(e)?"xn--"+E(e):e}))},toUnicode:function(e){return m(e,(function(e){return c.test(e)?S(e.slice(4).toLowerCase()):e}))}},void 0===(i=function(){return a}.call(t,n,t,e))||(e.exports=i)}()}).call(this,n(57)(e),n(31))},function(e,t,n){"use strict";e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},function(e,t,n){"use strict";t.decode=t.parse=n(369),t.encode=t.stringify=n(370)},function(e,t,n){"use strict";function r(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,o){t=t||"&",n=n||"=";var s={};if("string"!=typeof e||0===e.length)return s;var a=/\+/g;e=e.split(t);var u=1e3;o&&"number"==typeof o.maxKeys&&(u=o.maxKeys);var c=e.length;u>0&&c>u&&(c=u);for(var f=0;f<c;++f){var l,d,h,p,v=e[f].replace(a,"%20"),g=v.indexOf(n);g>=0?(l=v.substr(0,g),d=v.substr(g+1)):(l=v,d=""),h=decodeURIComponent(l),p=decodeURIComponent(d),r(s,h)?i(s[h])?s[h].push(p):s[h]=[s[h],p]:s[h]=p}return s};var i=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},function(e,t,n){"use strict";var r=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,a){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?o(s(e),(function(s){var a=encodeURIComponent(r(s))+n;return i(e[s])?o(e[s],(function(e){return a+encodeURIComponent(r(e))})).join(t):a+encodeURIComponent(r(e[s]))})).join(t):a?encodeURIComponent(r(a))+n+encodeURIComponent(r(e)):""};var i=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};function o(e,t){if(e.map)return e.map(t);for(var n=[],r=0;r<e.length;r++)n.push(t(e[r],r));return n}var s=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},function(e,t,n){var r,i,o=n(214),s=n(215),a=0,u=0;e.exports=function(e,t,n){var c=t&&n||0,f=t||[],l=(e=e||{}).node||r,d=void 0!==e.clockseq?e.clockseq:i;if(null==l||null==d){var h=o();null==l&&(l=r=[1|h[0],h[1],h[2],h[3],h[4],h[5]]),null==d&&(d=i=16383&(h[6]<<8|h[7]))}var p=void 0!==e.msecs?e.msecs:(new Date).getTime(),v=void 0!==e.nsecs?e.nsecs:u+1,g=p-a+(v-u)/1e4;if(g<0&&void 0===e.clockseq&&(d=d+1&16383),(g<0||p>a)&&void 0===e.nsecs&&(v=0),v>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");a=p,u=v,i=d;var m=(1e4*(268435455&(p+=122192928e5))+v)%4294967296;f[c++]=m>>>24&255,f[c++]=m>>>16&255,f[c++]=m>>>8&255,f[c++]=255&m;var b=p/4294967296*1e4&268435455;f[c++]=b>>>8&255,f[c++]=255&b,f[c++]=b>>>24&15|16,f[c++]=b>>>16&255,f[c++]=d>>>8|128,f[c++]=255&d;for(var y=0;y<6;++y)f[c+y]=l[y];return t||s(f)}},function(e,t,n){var r=n(214),i=n(215);e.exports=function(e,t,n){var o=t&&n||0;"string"==typeof e&&(t="binary"===e?new Array(16):null,e=null);var s=(e=e||{}).random||(e.rng||r)();if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,t)for(var a=0;a<16;++a)t[o+a]=s[a];return t||i(s)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Sha256=void 0;var r=n(216),i=n(219),o=n(374),s=n(377),a=n(379),u=n(134),c=function(){function e(e){s.supportsWebCrypto(u.locateWindow())?this.hash=new i.Sha256(e):a.isMsWindow(u.locateWindow())?this.hash=new r.Sha256(e):this.hash=new o.Sha256(e)}return e.prototype.update=function(e,t){this.hash.update(e,t)},e.prototype.digest=function(){return this.hash.digest()},e}();t.Sha256=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(1).__exportStar(n(375),t)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Sha256=void 0;var r=n(1),i=n(220),o=n(376),s=n(497),a=function(){function e(e){if(this.hash=new o.RawSha256,e){this.outer=new o.RawSha256;var t=function(e){var t=u(e);if(t.byteLength>i.BLOCK_SIZE){var n=new o.RawSha256;n.update(t),t=n.digest()}var r=new Uint8Array(i.BLOCK_SIZE);return r.set(t),r}(e),n=new Uint8Array(i.BLOCK_SIZE);n.set(t);for(var r=0;r<i.BLOCK_SIZE;r++)t[r]^=54,n[r]^=92;this.hash.update(t),this.outer.update(n);for(r=0;r<t.byteLength;r++)t[r]=0}}return e.prototype.update=function(e){if(!function(e){if("string"==typeof e)return 0===e.length;return 0===e.byteLength}(e)&&!this.error)try{this.hash.update(u(e))}catch(e){this.error=e}},e.prototype.digestSync=function(){if(this.error)throw this.error;return this.outer?(this.outer.finished||this.outer.update(this.hash.digest()),this.outer.digest()):this.hash.digest()},e.prototype.digest=function(){return r.__awaiter(this,void 0,void 0,(function(){return r.__generator(this,(function(e){return[2,this.digestSync()]}))}))},e}();function u(e){return"string"==typeof e?s.fromUtf8(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}t.Sha256=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RawSha256=void 0;var r=n(220),i=function(){function e(){this.state=Int32Array.from(r.INIT),this.temp=new Int32Array(64),this.buffer=new Uint8Array(64),this.bufferLength=0,this.bytesHashed=0,this.finished=!1}return e.prototype.update=function(e){if(this.finished)throw new Error("Attempted to update an already finished hash.");var t=0,n=e.byteLength;if(this.bytesHashed+=n,8*this.bytesHashed>r.MAX_HASHABLE_LENGTH)throw new Error("Cannot hash more than 2^53 - 1 bits");for(;n>0;)this.buffer[this.bufferLength++]=e[t++],n--,this.bufferLength===r.BLOCK_SIZE&&(this.hashBuffer(),this.bufferLength=0)},e.prototype.digest=function(){if(!this.finished){var e=8*this.bytesHashed,t=new DataView(this.buffer.buffer,this.buffer.byteOffset,this.buffer.byteLength),n=this.bufferLength;if(t.setUint8(this.bufferLength++,128),n%r.BLOCK_SIZE>=r.BLOCK_SIZE-8){for(var i=this.bufferLength;i<r.BLOCK_SIZE;i++)t.setUint8(i,0);this.hashBuffer(),this.bufferLength=0}for(i=this.bufferLength;i<r.BLOCK_SIZE-8;i++)t.setUint8(i,0);t.setUint32(r.BLOCK_SIZE-8,Math.floor(e/4294967296),!0),t.setUint32(r.BLOCK_SIZE-4,e),this.hashBuffer(),this.finished=!0}var o=new Uint8Array(r.DIGEST_LENGTH);for(i=0;i<8;i++)o[4*i]=this.state[i]>>>24&255,o[4*i+1]=this.state[i]>>>16&255,o[4*i+2]=this.state[i]>>>8&255,o[4*i+3]=this.state[i]>>>0&255;return o},e.prototype.hashBuffer=function(){for(var e=this.buffer,t=this.state,n=t[0],i=t[1],o=t[2],s=t[3],a=t[4],u=t[5],c=t[6],f=t[7],l=0;l<r.BLOCK_SIZE;l++){if(l<16)this.temp[l]=(255&e[4*l])<<24|(255&e[4*l+1])<<16|(255&e[4*l+2])<<8|255&e[4*l+3];else{var d=this.temp[l-2],h=(d>>>17|d<<15)^(d>>>19|d<<13)^d>>>10,p=((d=this.temp[l-15])>>>7|d<<25)^(d>>>18|d<<14)^d>>>3;this.temp[l]=(h+this.temp[l-7]|0)+(p+this.temp[l-16]|0)}var v=(((a>>>6|a<<26)^(a>>>11|a<<21)^(a>>>25|a<<7))+(a&u^~a&c)|0)+(f+(r.KEY[l]+this.temp[l]|0)|0)|0,g=((n>>>2|n<<30)^(n>>>13|n<<19)^(n>>>22|n<<10))+(n&i^n&o^i&o)|0;f=c,c=u,u=a,a=s+v|0,s=o,o=i,i=n,n=v+g|0}t[0]+=n,t[1]+=i,t[2]+=o,t[3]+=s,t[4]+=a,t[5]+=u,t[6]+=c,t[7]+=f},e}();t.RawSha256=i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(1).__exportStar(n(378),t)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.supportsZeroByteGCM=t.supportsSubtleCrypto=t.supportsSecureRandom=t.supportsWebCrypto=void 0;var r=n(1),i=["decrypt","digest","encrypt","exportKey","generateKey","importKey","sign","verify"];function o(e){return"object"==typeof e&&"object"==typeof e.crypto&&"function"==typeof e.crypto.getRandomValues}function s(e){return e&&i.every((function(t){return"function"==typeof e[t]}))}t.supportsWebCrypto=function(e){return!(!o(e)||"object"!=typeof e.crypto.subtle)&&s(e.crypto.subtle)},t.supportsSecureRandom=o,t.supportsSubtleCrypto=s,t.supportsZeroByteGCM=function(e){return r.__awaiter(this,void 0,void 0,(function(){var t;return r.__generator(this,(function(n){switch(n.label){case 0:if(!s(e))return[2,!1];n.label=1;case 1:return n.trys.push([1,4,,5]),[4,e.generateKey({name:"AES-GCM",length:128},!1,["encrypt"])];case 2:return t=n.sent(),[4,e.encrypt({name:"AES-GCM",iv:new Uint8Array(Array(12)),additionalData:new Uint8Array(Array(16)),tagLength:128},t,new Uint8Array(0))];case 3:return[2,16===n.sent().byteLength];case 4:return n.sent(),[2,!1];case 5:return[2]}}))}))}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(1);r.__exportStar(n(380),t),r.__exportStar(n(381),t),r.__exportStar(n(382),t),r.__exportStar(n(383),t),r.__exportStar(n(384),t)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isMsWindow=void 0;var r=["decrypt","digest","encrypt","exportKey","generateKey","importKey","sign","verify"];t.isMsWindow=function(e){if(function(e){return"MSInputMethodContext"in e&&"msCrypto"in e}(e)&&void 0!==e.msCrypto.subtle){var t=e.msCrypto,n=t.getRandomValues,i=t.subtle;return r.map((function(e){return i[e]})).concat(n).every((function(e){return"function"==typeof e}))}return!1}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(1),i=n(222),o=n(386),s=n(494),a=function(){function e(e){if(this.hash=new o.RawSha256,e){this.outer=new o.RawSha256;var t=function(e){var t=u(e);if(t.byteLength>i.BLOCK_SIZE){var n=new o.RawSha256;n.update(t),t=n.digest()}var r=new Uint8Array(i.BLOCK_SIZE);return r.set(t),r}(e),n=new Uint8Array(i.BLOCK_SIZE);n.set(t);for(var r=0;r<i.BLOCK_SIZE;r++)t[r]^=54,n[r]^=92;this.hash.update(t),this.outer.update(n);for(r=0;r<t.byteLength;r++)t[r]=0}}return e.prototype.update=function(e){if(!function(e){if("string"==typeof e)return 0===e.length;return 0===e.byteLength}(e)&&!this.error)try{this.hash.update(u(e))}catch(e){this.error=e}},e.prototype.digestSync=function(){if(this.error)throw this.error;return this.outer?(this.outer.finished||this.outer.update(this.hash.digest()),this.outer.digest()):this.hash.digest()},e.prototype.digest=function(){return r.__awaiter(this,void 0,void 0,(function(){return r.__generator(this,(function(e){return[2,this.digestSync()]}))}))},e}();function u(e){return"string"==typeof e?s.fromUtf8(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}t.Sha256=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(222),i=function(){function e(){this.state=Int32Array.from(r.INIT),this.temp=new Int32Array(64),this.buffer=new Uint8Array(64),this.bufferLength=0,this.bytesHashed=0,this.finished=!1}return e.prototype.update=function(e){if(this.finished)throw new Error("Attempted to update an already finished hash.");var t=0,n=e.byteLength;if(this.bytesHashed+=n,8*this.bytesHashed>r.MAX_HASHABLE_LENGTH)throw new Error("Cannot hash more than 2^53 - 1 bits");for(;n>0;)this.buffer[this.bufferLength++]=e[t++],n--,this.bufferLength===r.BLOCK_SIZE&&(this.hashBuffer(),this.bufferLength=0)},e.prototype.digest=function(){if(!this.finished){var e=8*this.bytesHashed,t=new DataView(this.buffer.buffer,this.buffer.byteOffset,this.buffer.byteLength),n=this.bufferLength;if(t.setUint8(this.bufferLength++,128),n%r.BLOCK_SIZE>=r.BLOCK_SIZE-8){for(var i=this.bufferLength;i<r.BLOCK_SIZE;i++)t.setUint8(i,0);this.hashBuffer(),this.bufferLength=0}for(i=this.bufferLength;i<r.BLOCK_SIZE-8;i++)t.setUint8(i,0);t.setUint32(r.BLOCK_SIZE-8,Math.floor(e/4294967296),!0),t.setUint32(r.BLOCK_SIZE-4,e),this.hashBuffer(),this.finished=!0}var o=new Uint8Array(r.DIGEST_LENGTH);for(i=0;i<8;i++)o[4*i]=this.state[i]>>>24&255,o[4*i+1]=this.state[i]>>>16&255,o[4*i+2]=this.state[i]>>>8&255,o[4*i+3]=this.state[i]>>>0&255;return o},e.prototype.hashBuffer=function(){for(var e=this.buffer,t=this.state,n=t[0],i=t[1],o=t[2],s=t[3],a=t[4],u=t[5],c=t[6],f=t[7],l=0;l<r.BLOCK_SIZE;l++){if(l<16)this.temp[l]=(255&e[4*l])<<24|(255&e[4*l+1])<<16|(255&e[4*l+2])<<8|255&e[4*l+3];else{var d=this.temp[l-2],h=(d>>>17|d<<15)^(d>>>19|d<<13)^d>>>10,p=((d=this.temp[l-15])>>>7|d<<25)^(d>>>18|d<<14)^d>>>3;this.temp[l]=(h+this.temp[l-7]|0)+(p+this.temp[l-16]|0)}var v=(((a>>>6|a<<26)^(a>>>11|a<<21)^(a>>>25|a<<7))+(a&u^~a&c)|0)+(f+(r.KEY[l]+this.temp[l]|0)|0)|0,g=((n>>>2|n<<30)^(n>>>13|n<<19)^(n>>>22|n<<10))+(n&i^n&o^i&o)|0;f=c,c=u,u=a,a=s+v|0,s=o,o=i,i=n,n=v+g|0}t[0]+=n,t[1]+=i,t[2]+=o,t[3]+=s,t[4]+=a,t[5]+=u,t[6]+=c,t[7]+=f},e}();t.RawSha256=i},function(e,t,n){var r=n(388),i=n(419);e.exports=function(e,t){for(var n=0,o=(t=r(t,e)).length;null!=e&&n<o;)e=e[i(t[n++])];return n&&n==o?e:void 0}},function(e,t,n){var r=n(61),i=n(389),o=n(392),s=n(416);e.exports=function(e,t){return r(e)?e:i(e,t)?[e]:o(s(e))}},function(e,t,n){var r=n(61),i=n(135),o=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,s=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!i(e))||(s.test(e)||!o.test(e)||null!=t&&e in Object(t))}},function(e,t,n){var r=n(97),i=Object.prototype,o=i.hasOwnProperty,s=i.toString,a=r?r.toStringTag:void 0;e.exports=function(e){var t=o.call(e,a),n=e[a];try{e[a]=void 0;var r=!0}catch(e){}var i=s.call(e);return r&&(t?e[a]=n:delete e[a]),i}},function(e,t){var n=Object.prototype.toString;e.exports=function(e){return n.call(e)}},function(e,t,n){var r=n(393),i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,o=/\\(\\)?/g,s=r((function(e){var t=[];return 46===e.charCodeAt(0)&&t.push(""),e.replace(i,(function(e,n,r,i){t.push(r?i.replace(o,"$1"):n||e)})),t}));e.exports=s},function(e,t,n){var r=n(394);e.exports=function(e){var t=r(e,(function(e){return 500===n.size&&n.clear(),e})),n=t.cache;return t}},function(e,t,n){var r=n(136);function i(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new TypeError("Expected a function");var n=function(){var r=arguments,i=t?t.apply(this,r):r[0],o=n.cache;if(o.has(i))return o.get(i);var s=e.apply(this,r);return n.cache=o.set(i,s)||o,s};return n.cache=new(i.Cache||r),n}i.Cache=r,e.exports=i},function(e,t,n){var r=n(396),i=n(99),o=n(137);e.exports=function(){this.size=0,this.__data__={hash:new r,map:new(o||i),string:new r}}},function(e,t,n){var r=n(397),i=n(402),o=n(403),s=n(404),a=n(405);function u(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}u.prototype.clear=r,u.prototype.delete=i,u.prototype.get=o,u.prototype.has=s,u.prototype.set=a,e.exports=u},function(e,t,n){var r=n(98);e.exports=function(){this.__data__=r?r(null):{},this.size=0}},function(e,t,n){var r=n(224),i=n(399),o=n(225),s=n(226),a=/^\[object .+?Constructor\]$/,u=Function.prototype,c=Object.prototype,f=u.toString,l=c.hasOwnProperty,d=RegExp("^"+f.call(l).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");e.exports=function(e){return!(!o(e)||i(e))&&(r(e)?d:a).test(s(e))}},function(e,t,n){var r,i=n(400),o=(r=/[^.]+$/.exec(i&&i.keys&&i.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";e.exports=function(e){return!!o&&o in e}},function(e,t,n){var r=n(53)["__core-js_shared__"];e.exports=r},function(e,t){e.exports=function(e,t){return null==e?void 0:e[t]}},function(e,t){e.exports=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t}},function(e,t,n){var r=n(98),i=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;if(r){var n=t[e];return"__lodash_hash_undefined__"===n?void 0:n}return i.call(t,e)?t[e]:void 0}},function(e,t,n){var r=n(98),i=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;return r?void 0!==t[e]:i.call(t,e)}},function(e,t,n){var r=n(98);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}},function(e,t){e.exports=function(){this.__data__=[],this.size=0}},function(e,t,n){var r=n(100),i=Array.prototype.splice;e.exports=function(e){var t=this.__data__,n=r(t,e);return!(n<0)&&(n==t.length-1?t.pop():i.call(t,n,1),--this.size,!0)}},function(e,t,n){var r=n(100);e.exports=function(e){var t=this.__data__,n=r(t,e);return n<0?void 0:t[n][1]}},function(e,t,n){var r=n(100);e.exports=function(e){return r(this.__data__,e)>-1}},function(e,t,n){var r=n(100);e.exports=function(e,t){var n=this.__data__,i=r(n,e);return i<0?(++this.size,n.push([e,t])):n[i][1]=t,this}},function(e,t,n){var r=n(101);e.exports=function(e){var t=r(this,e).delete(e);return this.size-=t?1:0,t}},function(e,t){e.exports=function(e){var t=typeof e;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==e:null===e}},function(e,t,n){var r=n(101);e.exports=function(e){return r(this,e).get(e)}},function(e,t,n){var r=n(101);e.exports=function(e){return r(this,e).has(e)}},function(e,t,n){var r=n(101);e.exports=function(e,t){var n=r(this,e),i=n.size;return n.set(e,t),this.size+=n.size==i?0:1,this}},function(e,t,n){var r=n(417);e.exports=function(e){return null==e?"":r(e)}},function(e,t,n){var r=n(97),i=n(418),o=n(61),s=n(135),a=r?r.prototype:void 0,u=a?a.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(o(t))return i(t,e)+"";if(s(t))return u?u.call(t):"";var n=t+"";return"0"==n&&1/t==-1/0?"-0":n}},function(e,t){e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,i=Array(r);++n<r;)i[n]=t(e[n],n,e);return i}},function(e,t,n){var r=n(135);e.exports=function(e){if("string"==typeof e||r(e))return e;var t=e+"";return"0"==t&&1/e==-1/0?"-0":t}},function(e,t,n){var r=n(421)(Object.keys,Object);e.exports=r},function(e,t){e.exports=function(e,t){return function(n){return e(t(n))}}},function(e,t,n){var r=n(72)(n(53),"DataView");e.exports=r},function(e,t,n){var r=n(72)(n(53),"Promise");e.exports=r},function(e,t,n){var r=n(72)(n(53),"Set");e.exports=r},function(e,t,n){var r=n(72)(n(53),"WeakMap");e.exports=r},function(e,t,n){var r=n(84),i=n(85);e.exports=function(e){return i(e)&&"[object Arguments]"==r(e)}},function(e,t){e.exports=function(){return!1}},function(e,t,n){var r=n(84),i=n(233),o=n(85),s={};s["[object Float32Array]"]=s["[object Float64Array]"]=s["[object Int8Array]"]=s["[object Int16Array]"]=s["[object Int32Array]"]=s["[object Uint8Array]"]=s["[object Uint8ClampedArray]"]=s["[object Uint16Array]"]=s["[object Uint32Array]"]=!0,s["[object Arguments]"]=s["[object Array]"]=s["[object ArrayBuffer]"]=s["[object Boolean]"]=s["[object DataView]"]=s["[object Date]"]=s["[object Error]"]=s["[object Function]"]=s["[object Map]"]=s["[object Number]"]=s["[object Object]"]=s["[object RegExp]"]=s["[object Set]"]=s["[object String]"]=s["[object WeakMap]"]=!1,e.exports=function(e){return o(e)&&i(e.length)&&!!s[r(e)]}},function(e,t){e.exports=function(e){return function(t){return e(t)}}},function(e,t,n){(function(e){var r=n(223),i=t&&!t.nodeType&&t,o=i&&"object"==typeof e&&e&&!e.nodeType&&e,s=o&&o.exports===i&&r.process,a=function(){try{var e=o&&o.require&&o.require("util").types;return e||s&&s.binding&&s.binding("util")}catch(e){}}();e.exports=a}).call(this,n(57)(e))},function(e,t,n){var r=n(432),i=n(85);e.exports=function e(t,n,o,s,a){return t===n||(null==t||null==n||!i(t)&&!i(n)?t!=t&&n!=n:r(t,n,o,s,e,a))}},function(e,t,n){var r=n(433),i=n(234),o=n(444),s=n(448),a=n(230),u=n(61),c=n(138),f=n(139),l="[object Object]",d=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,h,p,v){var g=u(e),m=u(t),b=g?"[object Array]":a(e),y=m?"[object Array]":a(t),w=(b="[object Arguments]"==b?l:b)==l,_=(y="[object Arguments]"==y?l:y)==l,S=b==y;if(S&&c(e)){if(!c(t))return!1;g=!0,w=!1}if(S&&!w)return v||(v=new r),g||f(e)?i(e,t,n,h,p,v):o(e,t,b,n,h,p,v);if(!(1&n)){var E=w&&d.call(e,"__wrapped__"),M=_&&d.call(t,"__wrapped__");if(E||M){var A=E?e.value():e,I=M?t.value():t;return v||(v=new r),p(A,I,n,h,v)}}return!!S&&(v||(v=new r),s(e,t,n,h,p,v))}},function(e,t,n){var r=n(99),i=n(434),o=n(435),s=n(436),a=n(437),u=n(438);function c(e){var t=this.__data__=new r(e);this.size=t.size}c.prototype.clear=i,c.prototype.delete=o,c.prototype.get=s,c.prototype.has=a,c.prototype.set=u,e.exports=c},function(e,t,n){var r=n(99);e.exports=function(){this.__data__=new r,this.size=0}},function(e,t){e.exports=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}},function(e,t){e.exports=function(e){return this.__data__.get(e)}},function(e,t){e.exports=function(e){return this.__data__.has(e)}},function(e,t,n){var r=n(99),i=n(137),o=n(136);e.exports=function(e,t){var n=this.__data__;if(n instanceof r){var s=n.__data__;if(!i||s.length<199)return s.push([e,t]),this.size=++n.size,this;n=this.__data__=new o(s)}return n.set(e,t),this.size=n.size,this}},function(e,t,n){var r=n(136),i=n(440),o=n(441);function s(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new r;++t<n;)this.add(e[t])}s.prototype.add=s.prototype.push=i,s.prototype.has=o,e.exports=s},function(e,t){e.exports=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this}},function(e,t){e.exports=function(e){return this.__data__.has(e)}},function(e,t){e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n<r;)if(t(e[n],n,e))return!0;return!1}},function(e,t){e.exports=function(e,t){return e.has(t)}},function(e,t,n){var r=n(97),i=n(445),o=n(227),s=n(234),a=n(446),u=n(447),c=r?r.prototype:void 0,f=c?c.valueOf:void 0;e.exports=function(e,t,n,r,c,l,d){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||!l(new i(e),new i(t)));case"[object Boolean]":case"[object Date]":case"[object Number]":return o(+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 h=a;case"[object Set]":var p=1&r;if(h||(h=u),e.size!=t.size&&!p)return!1;var v=d.get(e);if(v)return v==t;r|=2,d.set(e,t);var g=s(h(e),h(t),r,c,l,d);return d.delete(e),g;case"[object Symbol]":if(f)return f.call(e)==f.call(t)}return!1}},function(e,t,n){var r=n(53).Uint8Array;e.exports=r},function(e,t){e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}},function(e,t){e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e){n[++t]=e})),n}},function(e,t,n){var r=n(449),i=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,o,s,a){var u=1&n,c=r(e),f=c.length;if(f!=r(t).length&&!u)return!1;for(var l=f;l--;){var d=c[l];if(!(u?d in t:i.call(t,d)))return!1}var h=a.get(e),p=a.get(t);if(h&&p)return h==t&&p==e;var v=!0;a.set(e,t),a.set(t,e);for(var g=u;++l<f;){var m=e[d=c[l]],b=t[d];if(o)var y=u?o(b,m,d,t,e,a):o(m,b,d,e,t,a);if(!(void 0===y?m===b||s(m,b,n,o,a):y)){v=!1;break}g||(g="constructor"==d)}if(v&&!g){var w=e.constructor,_=t.constructor;w==_||!("constructor"in e)||!("constructor"in t)||"function"==typeof w&&w instanceof w&&"function"==typeof _&&_ instanceof _||(v=!1)}return a.delete(e),a.delete(t),v}},function(e,t,n){var r=n(450),i=n(452),o=n(455);e.exports=function(e){return r(e,o,i)}},function(e,t,n){var r=n(451),i=n(61);e.exports=function(e,t,n){var o=t(e);return i(e)?o:r(o,n(e))}},function(e,t){e.exports=function(e,t){for(var n=-1,r=t.length,i=e.length;++n<r;)e[i+n]=t[n];return e}},function(e,t,n){var r=n(453),i=n(454),o=Object.prototype.propertyIsEnumerable,s=Object.getOwnPropertySymbols,a=s?function(e){return null==e?[]:(e=Object(e),r(s(e),(function(t){return o.call(e,t)})))}:i;e.exports=a},function(e,t){e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,i=0,o=[];++n<r;){var s=e[n];t(s,n,e)&&(o[i++]=s)}return o}},function(e,t){e.exports=function(){return[]}},function(e,t,n){var r=n(456),i=n(228),o=n(232);e.exports=function(e){return o(e)?r(e):i(e)}},function(e,t,n){var r=n(457),i=n(231),o=n(61),s=n(138),a=n(458),u=n(139),c=Object.prototype.hasOwnProperty;e.exports=function(e,t){var n=o(e),f=!n&&i(e),l=!n&&!f&&s(e),d=!n&&!f&&!l&&u(e),h=n||f||l||d,p=h?r(e.length,String):[],v=p.length;for(var g in e)!t&&!c.call(e,g)||h&&("length"==g||l&&("offset"==g||"parent"==g)||d&&("buffer"==g||"byteLength"==g||"byteOffset"==g)||a(g,v))||p.push(g);return p}},function(e,t){e.exports=function(e,t){for(var n=-1,r=Array(e);++n<e;)r[n]=t(n);return r}},function(e,t){var n=/^(?:0|[1-9]\d*)$/;e.exports=function(e,t){var r=typeof e;return!!(t=null==t?9007199254740991:t)&&("number"==r||"symbol"!=r&&n.test(e))&&e>-1&&e%1==0&&e<t}},function(e,t,n){"use strict";const r=n(54),i=function(e,t,n){const o={};if((!e.child||r.isEmptyObject(e.child))&&(!e.attrsMap||r.isEmptyObject(e.attrsMap)))return r.isExist(e.val)?e.val:"";if(r.isExist(e.val)&&("string"!=typeof e.val||""!==e.val&&e.val!==t.cdataPositionChar)){const i=r.isTagNameInArrayMode(e.tagname,t.arrayMode,n);o[t.textNodeName]=i?[e.val]:e.val}r.merge(o,e.attrsMap,t.arrayMode);const s=Object.keys(e.child);for(let a=0;a<s.length;a++){const u=s[a];if(e.child[u]&&e.child[u].length>1){o[u]=[];for(let n in e.child[u])e.child[u].hasOwnProperty(n)&&o[u].push(i(e.child[u][n],t,u))}else{const s=i(e.child[u][0],t,u),a=!0===t.arrayMode&&"object"==typeof s||r.isTagNameInArrayMode(u,t.arrayMode,n);o[u]=a?[s]:s}}return o};t.convertToJson=i},function(e,t,n){"use strict";e.exports=function(e,t,n){this.tagname=e,this.parent=t,this.child={},this.attrsMap={},this.val=n,this.addChild=function(e){Array.isArray(this.child[e.tagname])?this.child[e.tagname].push(e):this.child[e.tagname]=[e]}}},function(e,t,n){"use strict";const r=n(54),i={allowBooleanAttributes:!1},o=["allowBooleanAttributes"];function s(e,t){for(var n=t;t<e.length;t++)if("?"!=e[t]&&" "!=e[t]);else{var r=e.substr(n,t-n);if(t>5&&"xml"===r)return d("InvalidXml","XML declaration allowed only at the start of the document.",p(e,t));if("?"==e[t]&&">"==e[t+1]){t++;break}}return t}function a(e,t){if(e.length>t+5&&"-"===e[t+1]&&"-"===e[t+2]){for(t+=3;t<e.length;t++)if("-"===e[t]&&"-"===e[t+1]&&">"===e[t+2]){t+=2;break}}else if(e.length>t+8&&"D"===e[t+1]&&"O"===e[t+2]&&"C"===e[t+3]&&"T"===e[t+4]&&"Y"===e[t+5]&&"P"===e[t+6]&&"E"===e[t+7]){let n=1;for(t+=8;t<e.length;t++)if("<"===e[t])n++;else if(">"===e[t]&&(n--,0===n))break}else if(e.length>t+9&&"["===e[t+1]&&"C"===e[t+2]&&"D"===e[t+3]&&"A"===e[t+4]&&"T"===e[t+5]&&"A"===e[t+6]&&"["===e[t+7])for(t+=8;t<e.length;t++)if("]"===e[t]&&"]"===e[t+1]&&">"===e[t+2]){t+=2;break}return t}t.validate=function(e,t){t=r.buildOptions(t,i,o);const n=[];let c=!1,h=!1;"\ufeff"===e[0]&&(e=e.substr(1));for(let i=0;i<e.length;i++)if("<"===e[i]&&"?"===e[i+1]){if(i+=2,i=s(e,i),i.err)return i}else{if("<"!==e[i]){if(" "===e[i]||"\t"===e[i]||"\n"===e[i]||"\r"===e[i])continue;return d("InvalidChar","char '"+e[i]+"' is not expected.",p(e,i))}if(i++,"!"===e[i]){i=a(e,i);continue}{let o=!1;"/"===e[i]&&(o=!0,i++);let g="";for(;i<e.length&&">"!==e[i]&&" "!==e[i]&&"\t"!==e[i]&&"\n"!==e[i]&&"\r"!==e[i];i++)g+=e[i];if(g=g.trim(),"/"===g[g.length-1]&&(g=g.substring(0,g.length-1),i--),v=g,!r.isName(v)){let t;return t=0===g.trim().length?"There is an unnecessary space between tag name and backward slash '</ ..'.":"Tag '"+g+"' is an invalid name.",d("InvalidTag",t,p(e,i))}const m=u(e,i);if(!1===m)return d("InvalidAttr","Attributes for '"+g+"' have open quote.",p(e,i));let b=m.value;if(i=m.index,"/"===b[b.length-1]){b=b.substring(0,b.length-1);const n=f(b,t);if(!0!==n)return d(n.err.code,n.err.msg,p(e,i-b.length+n.err.line));c=!0}else if(o){if(!m.tagClosed)return d("InvalidTag","Closing tag '"+g+"' doesn't have proper closing.",p(e,i));if(b.trim().length>0)return d("InvalidTag","Closing tag '"+g+"' can't have attributes or invalid starting.",p(e,i));{const t=n.pop();if(g!==t)return d("InvalidTag","Closing tag '"+t+"' is expected inplace of '"+g+"'.",p(e,i));0==n.length&&(h=!0)}}else{const r=f(b,t);if(!0!==r)return d(r.err.code,r.err.msg,p(e,i-b.length+r.err.line));if(!0===h)return d("InvalidXml","Multiple possible root nodes found.",p(e,i));n.push(g),c=!0}for(i++;i<e.length;i++)if("<"===e[i]){if("!"===e[i+1]){i++,i=a(e,i);continue}if("?"!==e[i+1])break;if(i=s(e,++i),i.err)return i}else if("&"===e[i]){const t=l(e,i);if(-1==t)return d("InvalidChar","char '&' is not expected.",p(e,i));i=t}"<"===e[i]&&i--}}var v;return c?!(n.length>0)||d("InvalidXml","Invalid '"+JSON.stringify(n,null,4).replace(/\r?\n/g,"")+"' found.",1):d("InvalidXml","Start tag expected.",1)};function u(e,t){let n="",r="",i=!1;for(;t<e.length;t++){if('"'===e[t]||"'"===e[t])if(""===r)r=e[t];else{if(r!==e[t])continue;r=""}else if(">"===e[t]&&""===r){i=!0;break}n+=e[t]}return""===r&&{value:n,index:t,tagClosed:i}}const c=new RegExp("(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['\"])(([\\s\\S])*?)\\5)?","g");function f(e,t){const n=r.getAllMatches(e,c),i={};for(let r=0;r<n.length;r++){if(0===n[r][1].length)return d("InvalidAttr","Attribute '"+n[r][2]+"' has no space in starting.",v(e,n[r][0]));if(void 0===n[r][3]&&!t.allowBooleanAttributes)return d("InvalidAttr","boolean attribute '"+n[r][2]+"' is not allowed.",v(e,n[r][0]));const o=n[r][2];if(!h(o))return d("InvalidAttr","Attribute '"+o+"' is an invalid name.",v(e,n[r][0]));if(i.hasOwnProperty(o))return d("InvalidAttr","Attribute '"+o+"' is repeated.",v(e,n[r][0]));i[o]=1}return!0}function l(e,t){if(";"===e[++t])return-1;if("#"===e[t])return function(e,t){let n=/\d/;for("x"===e[t]&&(t++,n=/[\da-fA-F]/);t<e.length;t++){if(";"===e[t])return t;if(!e[t].match(n))break}return-1}(e,++t);let n=0;for(;t<e.length;t++,n++)if(!(e[t].match(/\w/)&&n<20)){if(";"===e[t])break;return-1}return t}function d(e,t,n){return{err:{code:e,msg:t,line:n}}}function h(e){return r.isName(e)}function p(e,t){return e.substring(0,t).split(/\r?\n/).length}function v(e,t){return e.indexOf(t)+t.length}},function(e,t,n){"use strict";const r=function(e){return String.fromCharCode(e)},i={nilChar:r(176),missingChar:r(201),nilPremitive:r(175),missingPremitive:r(200),emptyChar:r(178),emptyValue:r(177),boundryChar:r(179),objStart:r(198),arrStart:r(204),arrayEnd:r(185)},o=[i.nilChar,i.nilPremitive,i.missingChar,i.missingPremitive,i.boundryChar,i.emptyChar,i.emptyValue,i.arrayEnd,i.objStart,i.arrStart],s=function(e,t,n){if("string"==typeof t)return e&&e[0]&&void 0!==e[0].val?a(e[0].val,t):a(e,t);{const o=void 0===(r=e)?i.missingChar:null===r?i.nilChar:!(r.child&&0===Object.keys(r.child).length&&(!r.attrsMap||0===Object.keys(r.attrsMap).length))||i.emptyChar;if(!0===o){let r="";if(Array.isArray(t)){r+=i.arrStart;const o=t[0],c=e.length;if("string"==typeof o)for(let t=0;t<c;t++){const n=a(e[t].val,o);r=u(r,n)}else for(let t=0;t<c;t++){const i=s(e[t],o,n);r=u(r,i)}r+=i.arrayEnd}else{r+=i.objStart;const o=Object.keys(t);Array.isArray(e)&&(e=e[0]);for(let i in o){const a=o[i];let c;c=!n.ignoreAttributes&&e.attrsMap&&e.attrsMap[a]?s(e.attrsMap[a],t[a],n):a===n.textNodeName?s(e.val,t[a],n):s(e.child[a],t[a],n),r=u(r,c)}}return r}return o}var r},a=function(e){switch(e){case void 0:return i.missingPremitive;case null:return i.nilPremitive;case"":return i.emptyValue;default:return e}},u=function(e,t){return c(t[0])||c(e[e.length-1])||(e+=i.boundryChar),e+t},c=function(e){return-1!==o.indexOf(e)};const f=n(102),l=n(54).buildOptions;t.convert2nimn=function(e,t,n){return n=l(n,f.defaultOptions,f.props),s(e,t,n)}},function(e,t,n){"use strict";const r=n(54),i=n(54).buildOptions,o=n(102),s=function(e,t,n){let i="{";const o=Object.keys(e.child);for(let n=0;n<o.length;n++){var a=o[n];if(e.child[a]&&e.child[a].length>1){for(var u in i+='"'+a+'" : [ ',e.child[a])i+=s(e.child[a][u],t)+" , ";i=i.substr(0,i.length-1)+" ] "}else i+='"'+a+'" : '+s(e.child[a][0],t)+" ,"}return r.merge(i,e.attrsMap),r.isEmptyObject(i)?r.isExist(e.val)?e.val:"":(r.isExist(e.val)&&("string"!=typeof e.val||""!==e.val&&e.val!==t.cdataPositionChar)&&(i+='"'+t.textNodeName+'" : '+(!0!==(c=e.val)&&!1!==c&&isNaN(c)?'"'+c+'"':c)),","===i[i.length-1]&&(i=i.substr(0,i.length-2)),i+"}");var c};t.convertToJsonString=function(e,t){return(t=i(t,o.defaultOptions,o.props)).indentBy=t.indentBy||"",s(e,t,0)}},function(e,t,n){"use strict";const r=n(54).buildOptions,i={attributeNamePrefix:"@_",attrNodeName:!1,textNodeName:"#text",ignoreAttributes:!0,cdataTagName:!1,cdataPositionChar:"\\c",format:!1,indentBy:" ",supressEmptyNode:!1,tagValueProcessor:function(e){return e},attrValueProcessor:function(e){return e}},o=["attributeNamePrefix","attrNodeName","textNodeName","ignoreAttributes","cdataTagName","cdataPositionChar","format","indentBy","supressEmptyNode","tagValueProcessor","attrValueProcessor"];function s(e){this.options=r(e,i,o),this.options.ignoreAttributes||this.options.attrNodeName?this.isAttribute=function(){return!1}:(this.attrPrefixLen=this.options.attributeNamePrefix.length,this.isAttribute=p),this.options.cdataTagName?this.isCDATA=v:this.isCDATA=function(){return!1},this.replaceCDATAstr=a,this.replaceCDATAarr=u,this.options.format?(this.indentate=h,this.tagEndChar=">\n",this.newLine="\n"):(this.indentate=function(){return""},this.tagEndChar=">",this.newLine=""),this.options.supressEmptyNode?(this.buildTextNode=d,this.buildObjNode=f):(this.buildTextNode=l,this.buildObjNode=c),this.buildTextValNode=l,this.buildObjectNode=c}function a(e,t){return e=this.options.tagValueProcessor(""+e),""===this.options.cdataPositionChar||""===e?e+"<![CDATA["+t+"]]"+this.tagEndChar:e.replace(this.options.cdataPositionChar,"<![CDATA["+t+"]]"+this.tagEndChar)}function u(e,t){if(e=this.options.tagValueProcessor(""+e),""===this.options.cdataPositionChar||""===e)return e+"<![CDATA["+t.join("]]><![CDATA[")+"]]"+this.tagEndChar;for(let n in t)e=e.replace(this.options.cdataPositionChar,"<![CDATA["+t[n]+"]]>");return e+this.newLine}function c(e,t,n,r){return n&&!e.includes("<")?this.indentate(r)+"<"+t+n+">"+e+"</"+t+this.tagEndChar:this.indentate(r)+"<"+t+n+this.tagEndChar+e+this.indentate(r)+"</"+t+this.tagEndChar}function f(e,t,n,r){return""!==e?this.buildObjectNode(e,t,n,r):this.indentate(r)+"<"+t+n+"/"+this.tagEndChar}function l(e,t,n,r){return this.indentate(r)+"<"+t+n+">"+this.options.tagValueProcessor(e)+"</"+t+this.tagEndChar}function d(e,t,n,r){return""!==e?this.buildTextValNode(e,t,n,r):this.indentate(r)+"<"+t+n+"/"+this.tagEndChar}function h(e){return this.options.indentBy.repeat(e)}function p(e){return!!e.startsWith(this.options.attributeNamePrefix)&&e.substr(this.attrPrefixLen)}function v(e){return e===this.options.cdataTagName}s.prototype.parse=function(e){return this.j2x(e,0).val},s.prototype.j2x=function(e,t){let n="",r="";const i=Object.keys(e),o=i.length;for(let s=0;s<o;s++){const o=i[s];if(void 0===e[o]);else if(null===e[o])r+=this.indentate(t)+"<"+o+"/"+this.tagEndChar;else if(e[o]instanceof Date)r+=this.buildTextNode(e[o],o,"",t);else if("object"!=typeof e[o]){const i=this.isAttribute(o);i?n+=" "+i+'="'+this.options.attrValueProcessor(""+e[o])+'"':this.isCDATA(o)?e[this.options.textNodeName]?r+=this.replaceCDATAstr(e[this.options.textNodeName],e[o]):r+=this.replaceCDATAstr("",e[o]):o===this.options.textNodeName?e[this.options.cdataTagName]||(r+=this.options.tagValueProcessor(""+e[o])):r+=this.buildTextNode(e[o],o,"",t)}else if(Array.isArray(e[o]))if(this.isCDATA(o))r+=this.indentate(t),e[this.options.textNodeName]?r+=this.replaceCDATAarr(e[this.options.textNodeName],e[o]):r+=this.replaceCDATAarr("",e[o]);else{const n=e[o].length;for(let i=0;i<n;i++){const n=e[o][i];if(void 0===n);else if(null===n)r+=this.indentate(t)+"<"+o+"/"+this.tagEndChar;else if("object"==typeof n){const e=this.j2x(n,t+1);r+=this.buildObjNode(e.val,o,e.attrStr,t)}else r+=this.buildTextNode(n,o,"",t)}}else if(this.options.attrNodeName&&o===this.options.attrNodeName){const t=Object.keys(e[o]),r=t.length;for(let i=0;i<r;i++)n+=" "+t[i]+'="'+this.options.attrValueProcessor(""+e[o][t[i]])+'"'}else{const n=this.j2x(e[o],t+1);r+=this.buildObjNode(n.val,o,n.attrStr,t)}}return{attrStr:n,val:r}},e.exports=s},function(e,t,n){"use strict";var r=n(45),i=n(235),o=n(466),s=n(241);function a(e){var t=new o(e),n=i(o.prototype.request,t);return r.extend(n,o.prototype,t),r.extend(n,t),n}var u=a(n(238));u.Axios=o,u.create=function(e){return a(s(u.defaults,e))},u.Cancel=n(242),u.CancelToken=n(479),u.isCancel=n(237),u.all=function(e){return Promise.all(e)},u.spread=n(480),u.isAxiosError=n(481),e.exports=u,e.exports.default=u},function(e,t,n){"use strict";var r=n(45),i=n(236),o=n(467),s=n(468),a=n(241);function u(e){this.defaults=e,this.interceptors={request:new o,response:new o}}u.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{}).url=arguments[0]:e=e||{},(e=a(this.defaults,e)).method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[s,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach((function(e){t.unshift(e.fulfilled,e.rejected)})),this.interceptors.response.forEach((function(e){t.push(e.fulfilled,e.rejected)}));t.length;)n=n.then(t.shift(),t.shift());return n},u.prototype.getUri=function(e){return e=a(this.defaults,e),i(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},r.forEach(["delete","get","head","options"],(function(e){u.prototype[e]=function(t,n){return this.request(a(n||{},{method:e,url:t,data:(n||{}).data}))}})),r.forEach(["post","put","patch"],(function(e){u.prototype[e]=function(t,n,r){return this.request(a(r||{},{method:e,url:t,data:n}))}})),e.exports=u},function(e,t,n){"use strict";var r=n(45);function i(){this.handlers=[]}i.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},i.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},i.prototype.forEach=function(e){r.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=i},function(e,t,n){"use strict";var r=n(45),i=n(469),o=n(237),s=n(238);function a(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){return a(e),e.headers=e.headers||{},e.data=i(e.data,e.headers,e.transformRequest),e.headers=r.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||s.adapter)(e).then((function(t){return a(e),t.data=i(t.data,t.headers,e.transformResponse),t}),(function(t){return o(t)||(a(e),t&&t.response&&(t.response.data=i(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},function(e,t,n){"use strict";var r=n(45);e.exports=function(e,t,n){return r.forEach(n,(function(n){e=n(e,t)})),e}},function(e,t,n){"use strict";var r=n(45);e.exports=function(e,t){r.forEach(e,(function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])}))}},function(e,t,n){"use strict";var r=n(240);e.exports=function(e,t,n){var i=n.config.validateStatus;n.status&&i&&!i(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},function(e,t,n){"use strict";e.exports=function(e,t,n,r,i){return e.config=t,n&&(e.code=n),e.request=r,e.response=i,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(45);e.exports=r.isStandardBrowserEnv()?{write:function(e,t,n,i,o,s){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(i)&&a.push("path="+i),r.isString(o)&&a.push("domain="+o),!0===s&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,n){"use strict";var r=n(475),i=n(476);e.exports=function(e,t){return e&&!r(t)?i(e,t):t}},function(e,t,n){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,n){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(45),i=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,o,s={};return e?(r.forEach(e.split("\n"),(function(e){if(o=e.indexOf(":"),t=r.trim(e.substr(0,o)).toLowerCase(),n=r.trim(e.substr(o+1)),t){if(s[t]&&i.indexOf(t)>=0)return;s[t]="set-cookie"===t?(s[t]?s[t]:[]).concat([n]):s[t]?s[t]+", "+n:n}})),s):s}},function(e,t,n){"use strict";var r=n(45);e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function i(e){var r=e;return t&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return e=i(window.location.href),function(t){var n=r.isString(t)?i(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return!0}},function(e,t,n){"use strict";var r=n(242);function i(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var n=this;e((function(e){n.reason||(n.reason=new r(e),t(n.reason))}))}i.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},i.source=function(){var e;return{token:new i((function(t){e=t})),cancel:e}},e.exports=i},function(e,t,n){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,n){"use strict";e.exports=function(e){return"object"==typeof e&&!0===e.isAxiosError}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function o(e,t,n){return t&&i(e.prototype,t),n&&i(e,n),e}Object.defineProperty(t,"__esModule",{value:!0}),t.Observable=void 0;var s=function(){return"function"==typeof Symbol},a=function(e){return s()&&Boolean(Symbol[e])},u=function(e){return a(e)?Symbol[e]:"@@"+e};s()&&!a("observable")&&(Symbol.observable=Symbol("observable"));var c=u("iterator"),f=u("observable"),l=u("species");function d(e,t){var n=e[t];if(null!=n){if("function"!=typeof n)throw new TypeError(n+" is not a function");return n}}function h(e){var t=e.constructor;return void 0!==t&&null===(t=t[l])&&(t=void 0),void 0!==t?t:E}function p(e){return e instanceof E}function v(e){v.log?v.log(e):setTimeout((function(){throw e}))}function g(e){Promise.resolve().then((function(){try{e()}catch(e){v(e)}}))}function m(e){var t=e._cleanup;if(void 0!==t&&(e._cleanup=void 0,t))try{if("function"==typeof t)t();else{var n=d(t,"unsubscribe");n&&n.call(t)}}catch(e){v(e)}}function b(e){e._observer=void 0,e._queue=void 0,e._state="closed"}function y(e,t,n){e._state="running";var r=e._observer;try{var i=d(r,t);switch(t){case"next":i&&i.call(r,n);break;case"error":if(b(e),!i)throw n;i.call(r,n);break;case"complete":b(e),i&&i.call(r)}}catch(e){v(e)}"closed"===e._state?m(e):"running"===e._state&&(e._state="ready")}function w(e,t,n){if("closed"!==e._state){if("buffering"!==e._state)return"ready"!==e._state?(e._state="buffering",e._queue=[{type:t,value:n}],void g((function(){return function(e){var t=e._queue;if(t){e._queue=void 0,e._state="ready";for(var n=0;n<t.length&&(y(e,t[n].type,t[n].value),"closed"!==e._state);++n);}}(e)}))):void y(e,t,n);e._queue.push({type:t,value:n})}}var _=function(){function e(t,n){r(this,e),this._cleanup=void 0,this._observer=t,this._queue=void 0,this._state="initializing";var i=new S(this);try{this._cleanup=n.call(void 0,i)}catch(e){i.error(e)}"initializing"===this._state&&(this._state="ready")}return o(e,[{key:"unsubscribe",value:function(){"closed"!==this._state&&(b(this),m(this))}},{key:"closed",get:function(){return"closed"===this._state}}]),e}(),S=function(){function e(t){r(this,e),this._subscription=t}return o(e,[{key:"next",value:function(e){w(this._subscription,"next",e)}},{key:"error",value:function(e){w(this._subscription,"error",e)}},{key:"complete",value:function(){w(this._subscription,"complete")}},{key:"closed",get:function(){return"closed"===this._subscription._state}}]),e}(),E=function(){function e(t){if(r(this,e),!(this instanceof e))throw new TypeError("Observable cannot be called as a function");if("function"!=typeof t)throw new TypeError("Observable initializer must be a function");this._subscriber=t}return o(e,[{key:"subscribe",value:function(e){return"object"==typeof e&&null!==e||(e={next:e,error:arguments[1],complete:arguments[2]}),new _(e,this._subscriber)}},{key:"forEach",value:function(e){var t=this;return new Promise((function(n,r){if("function"==typeof e)var i=t.subscribe({next:function(t){try{e(t,o)}catch(e){r(e),i.unsubscribe()}},error:r,complete:n});else r(new TypeError(e+" is not a function"));function o(){i.unsubscribe(),n()}}))}},{key:"map",value:function(e){var t=this;if("function"!=typeof e)throw new TypeError(e+" is not a function");return new(h(this))((function(n){return t.subscribe({next:function(t){try{t=e(t)}catch(e){return n.error(e)}n.next(t)},error:function(e){n.error(e)},complete:function(){n.complete()}})}))}},{key:"filter",value:function(e){var t=this;if("function"!=typeof e)throw new TypeError(e+" is not a function");return new(h(this))((function(n){return t.subscribe({next:function(t){try{if(!e(t))return}catch(e){return n.error(e)}n.next(t)},error:function(e){n.error(e)},complete:function(){n.complete()}})}))}},{key:"reduce",value:function(e){var t=this;if("function"!=typeof e)throw new TypeError(e+" is not a function");var n=h(this),r=arguments.length>1,i=!1,o=arguments[1],s=o;return new n((function(n){return t.subscribe({next:function(t){var o=!i;if(i=!0,!o||r)try{s=e(s,t)}catch(e){return n.error(e)}else s=t},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(new TypeError("Cannot reduce an empty sequence"));n.next(s),n.complete()}})}))}},{key:"concat",value:function(){for(var e=this,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];var i=h(this);return new i((function(t){var r,o=0;return function e(s){r=s.subscribe({next:function(e){t.next(e)},error:function(e){t.error(e)},complete:function(){o===n.length?(r=void 0,t.complete()):e(i.from(n[o++]))}})}(e),function(){r&&(r.unsubscribe(),r=void 0)}}))}},{key:"flatMap",value:function(e){var t=this;if("function"!=typeof e)throw new TypeError(e+" is not a function");var n=h(this);return new n((function(r){var i=[],o=t.subscribe({next:function(t){if(e)try{t=e(t)}catch(e){return r.error(e)}var o=n.from(t).subscribe({next:function(e){r.next(e)},error:function(e){r.error(e)},complete:function(){var e=i.indexOf(o);e>=0&&i.splice(e,1),s()}});i.push(o)},error:function(e){r.error(e)},complete:function(){s()}});function s(){o.closed&&0===i.length&&r.complete()}return function(){i.forEach((function(e){return e.unsubscribe()})),o.unsubscribe()}}))}},{key:f,value:function(){return this}}],[{key:"from",value:function(t){var n="function"==typeof this?this:e;if(null==t)throw new TypeError(t+" is not an object");var r=d(t,f);if(r){var i=r.call(t);if(Object(i)!==i)throw new TypeError(i+" is not an object");return p(i)&&i.constructor===n?i:new n((function(e){return i.subscribe(e)}))}if(a("iterator")&&(r=d(t,c)))return new n((function(e){g((function(){if(!e.closed){var n=!0,i=!1,o=void 0;try{for(var s,a=r.call(t)[Symbol.iterator]();!(n=(s=a.next()).done);n=!0){var u=s.value;if(e.next(u),e.closed)return}}catch(e){i=!0,o=e}finally{try{n||null==a.return||a.return()}finally{if(i)throw o}}e.complete()}}))}));if(Array.isArray(t))return new n((function(e){g((function(){if(!e.closed){for(var n=0;n<t.length;++n)if(e.next(t[n]),e.closed)return;e.complete()}}))}));throw new TypeError(t+" is not observable")}},{key:"of",value:function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];var i="function"==typeof this?this:e;return new i((function(e){g((function(){if(!e.closed){for(var t=0;t<n.length;++t)if(e.next(n[t]),e.closed)return;e.complete()}}))}))}},{key:l,get:function(){return this}}]),e}();t.Observable=E,s()&&Object.defineProperty(E,Symbol("extensions"),{value:{symbol:f,hostReportError:v},configurable:!0})},function(e,t,n){var r,i,o=n(243),s=n(244),a=0,u=0;e.exports=function(e,t,n){var c=t&&n||0,f=t||[],l=(e=e||{}).node||r,d=void 0!==e.clockseq?e.clockseq:i;if(null==l||null==d){var h=o();null==l&&(l=r=[1|h[0],h[1],h[2],h[3],h[4],h[5]]),null==d&&(d=i=16383&(h[6]<<8|h[7]))}var p=void 0!==e.msecs?e.msecs:(new Date).getTime(),v=void 0!==e.nsecs?e.nsecs:u+1,g=p-a+(v-u)/1e4;if(g<0&&void 0===e.clockseq&&(d=d+1&16383),(g<0||p>a)&&void 0===e.nsecs&&(v=0),v>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");a=p,u=v,i=d;var m=(1e4*(268435455&(p+=122192928e5))+v)%4294967296;f[c++]=m>>>24&255,f[c++]=m>>>16&255,f[c++]=m>>>8&255,f[c++]=255&m;var b=p/4294967296*1e4&268435455;f[c++]=b>>>8&255,f[c++]=255&b,f[c++]=b>>>24&15|16,f[c++]=b>>>16&255,f[c++]=d>>>8|128,f[c++]=255&d;for(var y=0;y<6;++y)f[c+y]=l[y];return t||s(f)}},function(e,t,n){var r=n(243),i=n(244);e.exports=function(e,t,n){var o=t&&n||0;"string"==typeof e&&(t="binary"===e?new Array(16):null,e=null);var s=(e=e||{}).random||(e.rng||r)();if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,t)for(var a=0;a<16;++a)t[o+a]=s[a];return t||i(s)}},function(e,t){},function(e,t,n){e.exports=n(487).Observable},function(e,t,n){"use strict";(function(e){!function(e,t){function n(e){return"function"==typeof Symbol&&Boolean(Symbol[e])}function r(e){return n(e)?Symbol[e]:"@@"+e}function i(e){setTimeout((function(){throw e}))}function o(e,t){var n=e[t];if(null!=n){if("function"!=typeof n)throw new TypeError(n+" is not a function");return n}}function s(e){var t=e.constructor;return void 0!==t&&null===(t=t[r("species")])&&(t=void 0),void 0!==t?t:d}function a(e,t){Object.keys(t).forEach((function(n){var r=Object.getOwnPropertyDescriptor(t,n);r.enumerable=!1,Object.defineProperty(e,n,r)}))}function u(e){var t=e._cleanup;if(t){e._cleanup=void 0;try{t()}catch(e){i(e)}}}function c(e){return void 0===e._observer}function f(e,t){if(Object(e)!==e)throw new TypeError("Observer must be an object");this._cleanup=void 0,this._observer=e;try{var n=o(e,"start");n&&n.call(e,this)}catch(e){i(e)}if(!c(this)){e=new l(this);try{var r=t.call(void 0,e);if(null!=r){if("function"==typeof r.unsubscribe)s=r,r=function(){s.unsubscribe()};else if("function"!=typeof r)throw new TypeError(r+" is not a function");this._cleanup=r}}catch(t){return void e.error(t)}var s;c(this)&&u(this)}}function l(e){this._subscription=e}function d(e){if(!(this instanceof d))throw new TypeError("Observable cannot be called as a function");if("function"!=typeof e)throw new TypeError("Observable initializer must be a function");this._subscriber=e}"function"!=typeof Symbol||Symbol.observable||(Symbol.observable=Symbol("observable")),a(f.prototype={},{get closed(){return c(this)},unsubscribe:function(){var e;c(e=this)||(e._observer=void 0,u(e))}}),a(l.prototype={},{get closed(){return c(this._subscription)},next:function(e){var t=this._subscription;if(!c(t)){var n=t._observer;try{var r=o(n,"next");r&&r.call(n,e)}catch(e){i(e)}}},error:function(e){var t=this._subscription;if(c(t))i(e);else{var n=t._observer;t._observer=void 0;try{var r=o(n,"error");if(!r)throw e;r.call(n,e)}catch(e){i(e)}u(t)}},complete:function(){var e=this._subscription;if(!c(e)){var t=e._observer;e._observer=void 0;try{var n=o(t,"complete");n&&n.call(t)}catch(e){i(e)}u(e)}}}),a(d.prototype,{subscribe:function(e){for(var t=[],n=1;n<arguments.length;++n)t.push(arguments[n]);return"function"==typeof e?e={next:e,error:t[0],complete:t[1]}:"object"==typeof e&&null!==e||(e={}),new f(e,this._subscriber)},forEach:function(e){var t=this;return new Promise((function(n,r){if("function"!=typeof e)return Promise.reject(new TypeError(e+" is not a function"));t.subscribe({_subscription:null,start:function(e){if(Object(e)!==e)throw new TypeError(e+" is not an object");this._subscription=e},next:function(t){var n=this._subscription;if(!n.closed)try{e(t)}catch(e){r(e),n.unsubscribe()}},error:r,complete:n})}))},map:function(e){var t=this;if("function"!=typeof e)throw new TypeError(e+" is not a function");return new(s(this))((function(n){return t.subscribe({next:function(t){if(!n.closed){try{t=e(t)}catch(e){return n.error(e)}n.next(t)}},error:function(e){n.error(e)},complete:function(){n.complete()}})}))},filter:function(e){var t=this;if("function"!=typeof e)throw new TypeError(e+" is not a function");return new(s(this))((function(n){return t.subscribe({next:function(t){if(!n.closed){try{if(!e(t))return}catch(e){return n.error(e)}n.next(t)}},error:function(e){n.error(e)},complete:function(){n.complete()}})}))},reduce:function(e){var t=this;if("function"!=typeof e)throw new TypeError(e+" is not a function");var n=s(this),r=arguments.length>1,i=!1,o=arguments[1],a=o;return new n((function(n){return t.subscribe({next:function(t){if(!n.closed){var o=!i;if(i=!0,!o||r)try{a=e(a,t)}catch(e){return n.error(e)}else a=t}},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(new TypeError("Cannot reduce an empty sequence"));n.next(a),n.complete()}})}))}}),Object.defineProperty(d.prototype,r("observable"),{value:function(){return this},writable:!0,configurable:!0}),a(d,{from:function(e){var t="function"==typeof this?this:d;if(null==e)throw new TypeError(e+" is not an object");var i=o(e,r("observable"));if(i){var s=i.call(e);if(Object(s)!==s)throw new TypeError(s+" is not an object");return s.constructor===t?s:new t((function(e){return s.subscribe(e)}))}if(n("iterator")&&(i=o(e,r("iterator"))))return new t((function(t){for(var n,r=i.call(e)[Symbol.iterator]();!(n=r.next()).done;){var o=n.value;if(t.next(o),t.closed)return}t.complete()}));if(Array.isArray(e))return new t((function(t){for(var n=0;n<e.length;++n)if(t.next(e[n]),t.closed)return;t.complete()}));throw new TypeError(e+" is not observable")},of:function(){for(var e=[],t=0;t<arguments.length;++t)e.push(arguments[t]);var n="function"==typeof this?this:d;return new n((function(t){for(var n=0;n<e.length;++n)if(t.next(e[n]),t.closed)return;t.complete()}))}}),Object.defineProperty(d,r("species"),{get:function(){return this},configurable:!0}),Object.defineProperty(d,"extensions",{value:{observableSymbol:r("observable"),setHostReportError:function(e){i=e}}}),e.Observable=d}(t)}).call(this,n(57)(e))},function(e,t,n){"use strict";n.d(t,"a",(function(){return c}));var r=n(44),i=n(19),o=function(){return(o=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},s=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},a=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(s(arguments[t]));return e},u=new r.a("Predictions"),c=new(function(){function e(e){this._options=e,this._convertPluggables=[],this._identifyPluggables=[],this._interpretPluggables=[]}return e.prototype.getModuleName=function(){return"Predictions"},e.prototype.addPluggable=function(e){if(this.getPluggable(e.getProviderName()))throw new Error("Pluggable with name "+e.getProviderName()+" has already been added.");var t=!1;this.implementsConvertPluggable(e)&&(this._convertPluggables.push(e),t=!0),this.implementsIdentifyPluggable(e)&&(this._identifyPluggables.push(e),t=!0),this.implementsInterpretPluggable(e)&&(this._interpretPluggables.push(e),t=!0),t&&this.configurePluggable(e)},e.prototype.getPluggable=function(e){var t=this.getAllProviders().find((function(t){return t.getProviderName()===e}));return void 0===t?(u.debug("No plugin found with providerName=>",e),null):t},e.prototype.removePluggable=function(e){this._convertPluggables=this._convertPluggables.filter((function(t){return t.getProviderName()!==e})),this._identifyPluggables=this._identifyPluggables.filter((function(t){return t.getProviderName()!==e})),this._interpretPluggables=this._interpretPluggables.filter((function(t){return t.getProviderName()!==e}))},e.prototype.configure=function(e){var t=this,n=e?e.predictions||e:{};n=o(o({},n),e),this._options=Object.assign({},this._options,n),u.debug("configure Predictions",this._options),this.getAllProviders().forEach((function(e){return t.configurePluggable(e)}))},e.prototype.interpret=function(e,t){return this.getPluggableToExecute(this._interpretPluggables,t).interpret(e)},e.prototype.convert=function(e,t){return this.getPluggableToExecute(this._convertPluggables,t).convert(e)},e.prototype.identify=function(e,t){return this.getPluggableToExecute(this._identifyPluggables,t).identify(e)},e.prototype.getPluggableToExecute=function(e,t){if(t&&t.providerName)return a(e).find((function(e){return e.getProviderName()===t.providerName}));if(1===e.length)return e[0];throw new Error("More than one or no providers are configured, Either specify a provider name or configure exactly one provider")},e.prototype.getAllProviders=function(){return a(this._convertPluggables,this._identifyPluggables,this._interpretPluggables)},e.prototype.configurePluggable=function(e){var t=Object.assign({},this._options.predictions,this._options[e.getCategory().toLowerCase()]);e.configure(t)},e.prototype.implementsConvertPluggable=function(e){return e&&"function"==typeof e.convert},e.prototype.implementsIdentifyPluggable=function(e){return e&&"function"==typeof e.identify},e.prototype.implementsInterpretPluggable=function(e){return e&&"function"==typeof e.interpret},e}())({});i.a.register(c)},function(e,t,n){"use strict";n.d(t,"a",(function(){return ft}));var r=n(44),i=n(19),o=function(){return(o=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},s=new r.a("AbstractInteractionsProvider"),a=function(){function e(e){void 0===e&&(e={}),this._config=e}return e.prototype.configure=function(e){return void 0===e&&(e={}),this._config=o(o({},this._config),e),s.debug("configure "+this.getProviderName(),this._config),this.options},e.prototype.getCategory=function(){return"Interactions"},Object.defineProperty(e.prototype,"options",{get:function(){return o({},this._config)},enumerable:!0,configurable:!0}),e}(),u=function(e,t){return(u=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function c(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}u(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var f=function(){return(f=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function l(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function d(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}Object.create;function h(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s}Object.create;var p,v,g,m,b,y,w,_,S,E,M,A,I,k,O,x,C,T,P,N,R,L,j,D,U,B,F,z,q,K,H,V,G,W,$,Y=n(155),J=n(38),Z=n(18),X=n(24),Q=n(11),ee=n(39),te=n(17),ne=n(40),re=n(41),ie=n(15),oe=new Set(["ap-east-1","ap-northeast-1","ap-northeast-2","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-north-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-west-1","us-west-2"]),se=new Set(["cn-north-1","cn-northwest-1"]),ae=new Set(["us-iso-east-1"]),ue=new Set(["us-isob-east-1"]),ce=new Set(["us-gov-east-1","us-gov-west-1"]),fe=f(f({},{apiVersion:"2016-11-28",disableHostPrefix:!1,logger:{},regionInfoProvider:function(e,t){var n=void 0;switch(e){case"eu-west-1":n={hostname:"runtime.lex.eu-west-1.amazonaws.com",partition:"aws",signingService:"lex"};break;case"us-east-1":n={hostname:"runtime.lex.us-east-1.amazonaws.com",partition:"aws",signingService:"lex"};break;case"us-west-2":n={hostname:"runtime.lex.us-west-2.amazonaws.com",partition:"aws",signingService:"lex"};break;default:oe.has(e)&&(n={hostname:"runtime.lex.{region}.amazonaws.com".replace("{region}",e),partition:"aws",signingService:"lex"}),se.has(e)&&(n={hostname:"runtime.lex.{region}.amazonaws.com.cn".replace("{region}",e),partition:"aws-cn"}),ae.has(e)&&(n={hostname:"runtime.lex.{region}.c2s.ic.gov".replace("{region}",e),partition:"aws-iso"}),ue.has(e)&&(n={hostname:"runtime.lex.{region}.sc2s.sgov.gov".replace("{region}",e),partition:"aws-iso-b"}),ce.has(e)&&(n={hostname:"runtime.lex.{region}.amazonaws.com".replace("{region}",e),partition:"aws-us-gov"}),void 0===n&&(n={hostname:"runtime.lex.{region}.amazonaws.com".replace("{region}",e),partition:"aws",signingService:"lex"})}return Promise.resolve(n)},signingName:"lex"}),{runtime:"browser",base64Decoder:te.a,base64Encoder:te.b,bodyLengthChecker:ne.a,credentialDefaultProvider:Object(X.a)("Credential is missing"),defaultUserAgent:Object(re.a)(Y.name,Y.version),maxAttempts:Q.a,region:Object(X.a)("Region is missing"),requestHandler:new Z.a,sha256:J.Sha256,streamCollector:Z.b,urlParser:ee.a,utf8Decoder:ie.a,utf8Encoder:ie.b}),le=n(22),de=n(37),he=n(21),pe=n(43),ve=n(25),ge=n(23),me=n(0),be=function(e){function t(t){var n=this,r=f(f({},fe),t),i=Object(le.b)(r),o=Object(le.a)(i),s=Object(ve.b)(o),a=Object(Q.c)(s),u=Object(ge.b)(a),c=Object(he.b)(u);return(n=e.call(this,c)||this).config=c,n.middlewareStack.use(Object(ve.a)(n.config)),n.middlewareStack.use(Object(Q.b)(n.config)),n.middlewareStack.use(Object(ge.a)(n.config)),n.middlewareStack.use(Object(de.a)(n.config)),n.middlewareStack.use(Object(he.a)(n.config)),n.middlewareStack.use(Object(pe.a)(n.config)),n}return c(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this)},t}(me.a);(p||(p={})).filterSensitiveLog=function(e){return f({},e)},(v||(v={})).filterSensitiveLog=function(e){return f({},e)},(g||(g={})).filterSensitiveLog=function(e){return f({},e)},(m||(m={})).filterSensitiveLog=function(e){return f({},e)},(b||(b={})).filterSensitiveLog=function(e){return f({},e)},(y||(y={})).filterSensitiveLog=function(e){return f({},e)},(w||(w={})).filterSensitiveLog=function(e){return f({},e)},(_||(_={})).filterSensitiveLog=function(e){return f({},e)},function(e){e.FAILED="Failed",e.FULFILLED="Fulfilled",e.READY_FOR_FULFILLMENT="ReadyForFulfillment"}(S||(S={})),function(e){e.COMPOSITE="Composite",e.CUSTOM_PAYLOAD="CustomPayload",e.PLAIN_TEXT="PlainText",e.SSML="SSML"}(E||(E={})),function(e){e.CLOSE="Close",e.CONFIRM_INTENT="ConfirmIntent",e.DELEGATE="Delegate",e.ELICIT_INTENT="ElicitIntent",e.ELICIT_SLOT="ElicitSlot"}(M||(M={})),(A||(A={})).filterSensitiveLog=function(e){return f(f(f({},e),e.slots&&{slots:me.d}),e.message&&{message:me.d})},function(e){e.CONFIRMED="Confirmed",e.DENIED="Denied",e.NONE="None"}(I||(I={})),(k||(k={})).filterSensitiveLog=function(e){return f(f({},e),e.slots&&{slots:me.d})},(O||(O={})).filterSensitiveLog=function(e){return f(f(f(f({},e),e.dialogAction&&{dialogAction:A.filterSensitiveLog(e.dialogAction)}),e.recentIntentSummaryView&&{recentIntentSummaryView:e.recentIntentSummaryView.map((function(e){return k.filterSensitiveLog(e)}))}),e.sessionAttributes&&{sessionAttributes:me.d})},(x||(x={})).filterSensitiveLog=function(e){return f({},e)},(C||(C={})).filterSensitiveLog=function(e){return f({},e)},(T||(T={})).filterSensitiveLog=function(e){return f({},e)},(P||(P={})).filterSensitiveLog=function(e){return f({},e)},(N||(N={})).filterSensitiveLog=function(e){return f(f(f({},e),e.requestAttributes&&{requestAttributes:me.d}),e.sessionAttributes&&{sessionAttributes:me.d})},function(e){e.CONFIRM_INTENT="ConfirmIntent",e.ELICIT_INTENT="ElicitIntent",e.ELICIT_SLOT="ElicitSlot",e.FAILED="Failed",e.FULFILLED="Fulfilled",e.READY_FOR_FULFILLMENT="ReadyForFulfillment"}(R||(R={})),(L||(L={})).filterSensitiveLog=function(e){return f(f({},e),e.message&&{message:me.d})},(j||(j={})).filterSensitiveLog=function(e){return f({},e)},(D||(D={})).filterSensitiveLog=function(e){return f({},e)},(U||(U={})).filterSensitiveLog=function(e){return f(f(f(f({},e),e.requestAttributes&&{requestAttributes:me.d}),e.inputText&&{inputText:me.d}),e.sessionAttributes&&{sessionAttributes:me.d})},(B||(B={})).filterSensitiveLog=function(e){return f({},e)},(F||(F={})).filterSensitiveLog=function(e){return f(f({},e),e.slots&&{slots:me.d})},function(e){e.GENERIC="application/vnd.amazonaws.card.generic"}(z||(z={})),(q||(q={})).filterSensitiveLog=function(e){return f({},e)},(K||(K={})).filterSensitiveLog=function(e){return f({},e)},(H||(H={})).filterSensitiveLog=function(e){return f({},e)},(V||(V={})).filterSensitiveLog=function(e){return f({},e)},(G||(G={})).filterSensitiveLog=function(e){return f(f(f(f(f({},e),e.alternativeIntents&&{alternativeIntents:e.alternativeIntents.map((function(e){return F.filterSensitiveLog(e)}))}),e.message&&{message:me.d}),e.sessionAttributes&&{sessionAttributes:me.d}),e.slots&&{slots:me.d})},(W||(W={})).filterSensitiveLog=function(e){return f(f(f(f({},e),e.dialogAction&&{dialogAction:A.filterSensitiveLog(e.dialogAction)}),e.recentIntentSummaryView&&{recentIntentSummaryView:e.recentIntentSummaryView.map((function(e){return k.filterSensitiveLog(e)}))}),e.sessionAttributes&&{sessionAttributes:me.d})},($||($={})).filterSensitiveLog=function(e){return f(f({},e),e.message&&{message:me.d})};var ye,we=n(2),_e=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l,h,p,v,g,m,b,y,w,_;return d(this,(function(d){switch(d.label){case 0:return r=[f({},e)],_={},[4,Ge(e.body,t)];case 1:switch(n=f.apply(void 0,r.concat([(_.body=d.sent(),_)])),o="UnknownError",o=We(e,n.body),o){case"BadGatewayException":case"com.amazonaws.lexruntimeservice#BadGatewayException":return[3,2];case"BadRequestException":case"com.amazonaws.lexruntimeservice#BadRequestException":return[3,4];case"ConflictException":case"com.amazonaws.lexruntimeservice#ConflictException":return[3,6];case"DependencyFailedException":case"com.amazonaws.lexruntimeservice#DependencyFailedException":return[3,8];case"InternalFailureException":case"com.amazonaws.lexruntimeservice#InternalFailureException":return[3,10];case"LimitExceededException":case"com.amazonaws.lexruntimeservice#LimitExceededException":return[3,12];case"LoopDetectedException":case"com.amazonaws.lexruntimeservice#LoopDetectedException":return[3,14];case"NotAcceptableException":case"com.amazonaws.lexruntimeservice#NotAcceptableException":return[3,16];case"NotFoundException":case"com.amazonaws.lexruntimeservice#NotFoundException":return[3,18];case"RequestTimeoutException":case"com.amazonaws.lexruntimeservice#RequestTimeoutException":return[3,20];case"UnsupportedMediaTypeException":case"com.amazonaws.lexruntimeservice#UnsupportedMediaTypeException":return[3,22]}return[3,24];case 2:return s=[{}],[4,Ee(n,t)];case 3:return i=f.apply(void 0,[f.apply(void 0,s.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 4:return a=[{}],[4,Me(n,t)];case 5:return i=f.apply(void 0,[f.apply(void 0,a.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 6:return u=[{}],[4,Ae(n,t)];case 7:return i=f.apply(void 0,[f.apply(void 0,u.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 8:return c=[{}],[4,Ie(n,t)];case 9:return i=f.apply(void 0,[f.apply(void 0,c.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 10:return l=[{}],[4,ke(n,t)];case 11:return i=f.apply(void 0,[f.apply(void 0,l.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 12:return h=[{}],[4,Oe(n,t)];case 13:return i=f.apply(void 0,[f.apply(void 0,h.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 14:return p=[{}],[4,xe(n,t)];case 15:return i=f.apply(void 0,[f.apply(void 0,p.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 16:return v=[{}],[4,Ce(n,t)];case 17:return i=f.apply(void 0,[f.apply(void 0,v.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 18:return g=[{}],[4,Te(n,t)];case 19:return i=f.apply(void 0,[f.apply(void 0,g.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 20:return m=[{}],[4,Pe(n,t)];case 21:return i=f.apply(void 0,[f.apply(void 0,m.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 22:return b=[{}],[4,Ne(n,t)];case 23:return i=f.apply(void 0,[f.apply(void 0,b.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,25];case 24:y=n.body,o=y.code||y.Code||o,i=f(f({},y),{name:""+o,message:y.message||y.Message||o,$fault:"client",$metadata:Ke(e)}),d.label=25;case 25:return w=i.message||i.Message||o,i.message=w,delete i.Message,[2,Promise.reject(Object.assign(new Error(w),i))]}}))}))},Se=function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l,h,p,v,g,m,b;return d(this,(function(d){switch(d.label){case 0:return r=[f({},e)],b={},[4,Ge(e.body,t)];case 1:switch(n=f.apply(void 0,r.concat([(b.body=d.sent(),b)])),o="UnknownError",o=We(e,n.body),o){case"BadGatewayException":case"com.amazonaws.lexruntimeservice#BadGatewayException":return[3,2];case"BadRequestException":case"com.amazonaws.lexruntimeservice#BadRequestException":return[3,4];case"ConflictException":case"com.amazonaws.lexruntimeservice#ConflictException":return[3,6];case"DependencyFailedException":case"com.amazonaws.lexruntimeservice#DependencyFailedException":return[3,8];case"InternalFailureException":case"com.amazonaws.lexruntimeservice#InternalFailureException":return[3,10];case"LimitExceededException":case"com.amazonaws.lexruntimeservice#LimitExceededException":return[3,12];case"LoopDetectedException":case"com.amazonaws.lexruntimeservice#LoopDetectedException":return[3,14];case"NotFoundException":case"com.amazonaws.lexruntimeservice#NotFoundException":return[3,16]}return[3,18];case 2:return s=[{}],[4,Ee(n,t)];case 3:return i=f.apply(void 0,[f.apply(void 0,s.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 4:return a=[{}],[4,Me(n,t)];case 5:return i=f.apply(void 0,[f.apply(void 0,a.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 6:return u=[{}],[4,Ae(n,t)];case 7:return i=f.apply(void 0,[f.apply(void 0,u.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 8:return c=[{}],[4,Ie(n,t)];case 9:return i=f.apply(void 0,[f.apply(void 0,c.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 10:return l=[{}],[4,ke(n,t)];case 11:return i=f.apply(void 0,[f.apply(void 0,l.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 12:return h=[{}],[4,Oe(n,t)];case 13:return i=f.apply(void 0,[f.apply(void 0,h.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 14:return p=[{}],[4,xe(n,t)];case 15:return i=f.apply(void 0,[f.apply(void 0,p.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 16:return v=[{}],[4,Te(n,t)];case 17:return i=f.apply(void 0,[f.apply(void 0,v.concat([d.sent()])),{name:o,$metadata:Ke(e)}]),[3,19];case 18:g=n.body,o=g.code||g.Code||o,i=f(f({},g),{name:""+o,message:g.message||g.Message||o,$fault:"client",$metadata:Ke(e)}),d.label=19;case 19:return m=i.message||i.Message||o,i.message=m,delete i.Message,[2,Promise.reject(Object.assign(new Error(m),i))]}}))}))},Ee=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"BadGatewayException",$fault:"server",$metadata:Ke(e),Message:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),[2,t]}))}))},Me=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"BadRequestException",$fault:"client",$metadata:Ke(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},Ae=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"ConflictException",$fault:"client",$metadata:Ke(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},Ie=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"DependencyFailedException",$fault:"client",$metadata:Ke(e),Message:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),[2,t]}))}))},ke=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"InternalFailureException",$fault:"server",$metadata:Ke(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},Oe=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"LimitExceededException",$fault:"client",$metadata:Ke(e),message:void 0,retryAfterSeconds:void 0},void 0!==e.headers["retry-after"]&&(t.retryAfterSeconds=e.headers["retry-after"]),void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},xe=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"LoopDetectedException",$fault:"server",$metadata:Ke(e),Message:void 0},void 0!==(n=e.body).Message&&null!==n.Message&&(t.Message=n.Message),[2,t]}))}))},Ce=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"NotAcceptableException",$fault:"client",$metadata:Ke(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},Te=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"NotFoundException",$fault:"client",$metadata:Ke(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},Pe=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"RequestTimeoutException",$fault:"client",$metadata:Ke(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},Ne=function(e,t){return l(void 0,void 0,void 0,(function(){var t,n;return d(this,(function(r){return t={name:"UnsupportedMediaTypeException",$fault:"client",$metadata:Ke(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},Re=function(e,t){return Object.entries(e).reduce((function(e,t){var n,r=h(t,2),i=r[0],o=r[1];return f(f({},e),((n={})[i]=o,n))}),{})},Le=function(e,t){return(e||[]).map((function(e){return function(e,t){return{attachmentLinkUrl:void 0!==e.attachmentLinkUrl&&null!==e.attachmentLinkUrl?e.attachmentLinkUrl:void 0,buttons:void 0!==e.buttons&&null!==e.buttons?Ue(e.buttons,t):void 0,imageUrl:void 0!==e.imageUrl&&null!==e.imageUrl?e.imageUrl:void 0,subTitle:void 0!==e.subTitle&&null!==e.subTitle?e.subTitle:void 0,title:void 0!==e.title&&null!==e.title?e.title:void 0}}(e,t)}))},je=function(e,t){return{score:void 0!==e.score&&null!==e.score?e.score:void 0}},De=function(e,t){return(e||[]).map((function(e){return Be(e,t)}))},Ue=function(e,t){return(e||[]).map((function(e){return function(e,t){return{text:void 0!==e.text&&null!==e.text?e.text:void 0,value:void 0!==e.value&&null!==e.value?e.value:void 0}}(e)}))},Be=function(e,t){return{intentName:void 0!==e.intentName&&null!==e.intentName?e.intentName:void 0,nluIntentConfidence:void 0!==e.nluIntentConfidence&&null!==e.nluIntentConfidence?je(e.nluIntentConfidence,t):void 0,slots:void 0!==e.slots&&null!==e.slots?qe(e.slots,t):void 0}},Fe=function(e,t){return{contentType:void 0!==e.contentType&&null!==e.contentType?e.contentType:void 0,genericAttachments:void 0!==e.genericAttachments&&null!==e.genericAttachments?Le(e.genericAttachments,t):void 0,version:void 0!==e.version&&null!==e.version?e.version:void 0}},ze=function(e,t){return{sentimentLabel:void 0!==e.sentimentLabel&&null!==e.sentimentLabel?e.sentimentLabel:void 0,sentimentScore:void 0!==e.sentimentScore&&null!==e.sentimentScore?e.sentimentScore:void 0}},qe=function(e,t){return Object.entries(e).reduce((function(e,t){var n,r=h(t,2),i=r[0],o=r[1];return f(f({},e),((n={})[i]=o,n))}),{})},Ke=function(e){return{httpStatusCode:e.statusCode,httpHeaders:e.headers,requestId:e.headers["x-amzn-requestid"]}},He=function(e,t){return function(e,t){return void 0===e&&(e=new Uint8Array),e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array)}(e,t).then((function(e){return t.utf8Encoder(e)}))},Ve=function(e){return!(void 0===e||""===e||Object.getOwnPropertyNames(e).includes("length")&&0==e.length||Object.getOwnPropertyNames(e).includes("size")&&0==e.size)},Ge=function(e,t){return He(e,t).then((function(e){return e.length?JSON.parse(e):{}}))},We=function(e,t){var n,r,i=function(e){var t=e;return t.indexOf(":")>=0&&(t=t.split(":")[0]),t.indexOf("#")>=0&&(t=t.split("#")[1]),t},o=(n=e.headers,r="x-amzn-errortype",Object.keys(n).find((function(e){return e.toLowerCase()===r.toLowerCase()})));return void 0!==o?i(e.headers[o]):void 0!==t.code?i(t.code):void 0!==t.__type?i(t.__type):""},$e=n(10),Ye=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object($e.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"LexRuntimeServiceClient",commandName:"PostTextCommand",inputFilterSensitiveLog:U.filterSensitiveLog,outputFilterSensitiveLog:G.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"LexRuntimeServiceClient",commandName:"PostTextCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n={"Content-Type":"application/json"},r="/bot/{botName}/alias/{botAlias}/user/{userId}/text",void 0===e.userId)throw new Error("No value provided for input HTTP label: userId.");if((i=e.userId).length<=0)throw new Error("Empty value provided for input HTTP label: userId.");if(r=r.replace("{userId}",Object(me.f)(i)),void 0===e.botAlias)throw new Error("No value provided for input HTTP label: botAlias.");if((i=e.botAlias).length<=0)throw new Error("Empty value provided for input HTTP label: botAlias.");if(r=r.replace("{botAlias}",Object(me.f)(i)),void 0===e.botName)throw new Error("No value provided for input HTTP label: botName.");if((i=e.botName).length<=0)throw new Error("Empty value provided for input HTTP label: botName.");return r=r.replace("{botName}",Object(me.f)(i)),o=JSON.stringify(f(f(f({},void 0!==e.inputText&&{inputText:e.inputText}),void 0!==e.requestAttributes&&{requestAttributes:Re(e.requestAttributes,t)}),void 0!==e.sessionAttributes&&{sessionAttributes:Re(e.sessionAttributes,t)})),[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new we.a({protocol:c,hostname:a,port:l,method:"POST",headers:n,path:r,body:o})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r;return d(this,(function(i){switch(i.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,Se(e,t)]:(n={$metadata:Ke(e),alternativeIntents:void 0,botVersion:void 0,dialogState:void 0,intentName:void 0,message:void 0,messageFormat:void 0,nluIntentConfidence:void 0,responseCard:void 0,sentimentResponse:void 0,sessionAttributes:void 0,sessionId:void 0,slotToElicit:void 0,slots:void 0},[4,Ge(e.body,t)]);case 1:return void 0!==(r=i.sent()).alternativeIntents&&null!==r.alternativeIntents&&(n.alternativeIntents=De(r.alternativeIntents,t)),void 0!==r.botVersion&&null!==r.botVersion&&(n.botVersion=r.botVersion),void 0!==r.dialogState&&null!==r.dialogState&&(n.dialogState=r.dialogState),void 0!==r.intentName&&null!==r.intentName&&(n.intentName=r.intentName),void 0!==r.message&&null!==r.message&&(n.message=r.message),void 0!==r.messageFormat&&null!==r.messageFormat&&(n.messageFormat=r.messageFormat),void 0!==r.nluIntentConfidence&&null!==r.nluIntentConfidence&&(n.nluIntentConfidence=je(r.nluIntentConfidence,t)),void 0!==r.responseCard&&null!==r.responseCard&&(n.responseCard=Fe(r.responseCard,t)),void 0!==r.sentimentResponse&&null!==r.sentimentResponse&&(n.sentimentResponse=ze(r.sentimentResponse,t)),void 0!==r.sessionAttributes&&null!==r.sessionAttributes&&(n.sessionAttributes=qe(r.sessionAttributes,t)),void 0!==r.sessionId&&null!==r.sessionId&&(n.sessionId=r.sessionId),void 0!==r.slotToElicit&&null!==r.slotToElicit&&(n.slotToElicit=r.slotToElicit),void 0!==r.slots&&null!==r.slots&&(n.slots=qe(r.slots,t)),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(me.b),Je=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return c(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object($e.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"LexRuntimeServiceClient",commandName:"PostContentCommand",inputFilterSensitiveLog:N.filterSensitiveLog,outputFilterSensitiveLog:L.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"LexRuntimeServiceClient",commandName:"PostContentCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,l;return d(this,(function(d){switch(d.label){case 0:if(n=f(f(f(f({"Content-Type":"application/octet-stream","x-amz-content-sha256":"UNSIGNED-PAYLOAD"},Ve(e.requestAttributes)&&{"x-amz-lex-request-attributes":me.c.fromObject(e.requestAttributes)}),Ve(e.sessionAttributes)&&{"x-amz-lex-session-attributes":me.c.fromObject(e.sessionAttributes)}),Ve(e.contentType)&&{"Content-Type":e.contentType}),Ve(e.accept)&&{Accept:e.accept}),r="/bot/{botName}/alias/{botAlias}/user/{userId}/content",void 0===e.botAlias)throw new Error("No value provided for input HTTP label: botAlias.");if((i=e.botAlias).length<=0)throw new Error("Empty value provided for input HTTP label: botAlias.");if(r=r.replace("{botAlias}",Object(me.f)(i)),void 0===e.botName)throw new Error("No value provided for input HTTP label: botName.");if((i=e.botName).length<=0)throw new Error("Empty value provided for input HTTP label: botName.");if(r=r.replace("{botName}",Object(me.f)(i)),void 0===e.userId)throw new Error("No value provided for input HTTP label: userId.");if((i=e.userId).length<=0)throw new Error("Empty value provided for input HTTP label: userId.");return r=r.replace("{userId}",Object(me.f)(i)),void 0!==e.inputStream&&(o=e.inputStream),[4,t.endpoint()];case 1:return s=d.sent(),a=s.hostname,u=s.protocol,c=void 0===u?"https":u,l=s.port,[2,new we.a({protocol:c,hostname:a,port:l,method:"POST",headers:n,path:r,body:o})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return l(void 0,void 0,void 0,(function(){var n,r;return d(this,(function(i){return 200!==e.statusCode&&e.statusCode>=300?[2,_e(e,t)]:(n={$metadata:Ke(e),alternativeIntents:void 0,audioStream:void 0,botVersion:void 0,contentType:void 0,dialogState:void 0,inputTranscript:void 0,intentName:void 0,message:void 0,messageFormat:void 0,nluIntentConfidence:void 0,sentimentResponse:void 0,sessionAttributes:void 0,sessionId:void 0,slotToElicit:void 0,slots:void 0},void 0!==e.headers["x-amz-lex-alternative-intents"]&&(n.alternativeIntents=new me.c(e.headers["x-amz-lex-alternative-intents"])),void 0!==e.headers["x-amz-lex-message-format"]&&(n.messageFormat=e.headers["x-amz-lex-message-format"]),void 0!==e.headers["content-type"]&&(n.contentType=e.headers["content-type"]),void 0!==e.headers["x-amz-lex-message"]&&(n.message=e.headers["x-amz-lex-message"]),void 0!==e.headers["x-amz-lex-bot-version"]&&(n.botVersion=e.headers["x-amz-lex-bot-version"]),void 0!==e.headers["x-amz-lex-sentiment"]&&(n.sentimentResponse=e.headers["x-amz-lex-sentiment"]),void 0!==e.headers["x-amz-lex-slots"]&&(n.slots=new me.c(e.headers["x-amz-lex-slots"])),void 0!==e.headers["x-amz-lex-input-transcript"]&&(n.inputTranscript=e.headers["x-amz-lex-input-transcript"]),void 0!==e.headers["x-amz-lex-slot-to-elicit"]&&(n.slotToElicit=e.headers["x-amz-lex-slot-to-elicit"]),void 0!==e.headers["x-amz-lex-session-attributes"]&&(n.sessionAttributes=new me.c(e.headers["x-amz-lex-session-attributes"])),void 0!==e.headers["x-amz-lex-session-id"]&&(n.sessionId=e.headers["x-amz-lex-session-id"]),void 0!==e.headers["x-amz-lex-dialog-state"]&&(n.dialogState=e.headers["x-amz-lex-dialog-state"]),void 0!==e.headers["x-amz-lex-intent-name"]&&(n.intentName=e.headers["x-amz-lex-intent-name"]),void 0!==e.headers["x-amz-lex-nlu-intent-confidence"]&&(n.nluIntentConfidence=new me.c(e.headers["x-amz-lex-nlu-intent-confidence"])),r=e.body,n.audioStream=r,[2,Promise.resolve(n)])}))}))}(e,t)},t}(me.b),Ze=n(89),Xe=n(50),Qe=function(e){if(e instanceof Blob||e instanceof ReadableStream)return new Response(e).arrayBuffer().then((function(e){return new Uint8Array(e)}));throw new Error("Readable is not supported.")},et=(ye=function(e,t){return(ye=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}ye(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),tt=function(){return(tt=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},nt=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},rt=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},it=new r.a("AWSLexProvider"),ot=function(e){function t(t){void 0===t&&(t={});var n=e.call(this,t)||this;return n._botsCompleteCallback={},n}return et(t,e),t.prototype.getProviderName=function(){return"AWSLexProvider"},t.prototype.reportBotStatus=function(e,t){var n=this;it.debug("postContent state",e.dialogState),"ReadyForFulfillment"!==e.dialogState&&"Fulfilled"!==e.dialogState||("function"==typeof this._botsCompleteCallback[t]&&setTimeout((function(){return n._botsCompleteCallback[t](null,{slots:e.slots})}),0),this._config&&"function"==typeof this._config[t].onComplete&&setTimeout((function(){return n._config[t].onComplete(null,{slots:e.slots})}),0)),"Failed"===e.dialogState&&("function"==typeof this._botsCompleteCallback[t]&&setTimeout((function(){return n._botsCompleteCallback[t]("Bot conversation failed")}),0),this._config&&"function"==typeof this._config[t].onComplete&&setTimeout((function(){return n._config[t].onComplete("Bot conversation failed")}),0))},t.prototype.sendMessage=function(e,t){return nt(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l;return rt(this,(function(d){switch(d.label){case 0:return this._config[e]?[4,Ze.a.get()]:[2,Promise.reject("Bot "+e+" does not exist")];case 1:if(!(n=d.sent()))return[2,Promise.reject("No credentials")];if(this.lexRuntimeServiceClient=new be({region:this._config[e].region,credentials:n,customUserAgent:Object(Xe.b)()}),"string"!=typeof t)return[3,6];r={botAlias:this._config[e].alias,botName:e,inputText:t,userId:n.identityId},it.debug("postText to lex",t),d.label=2;case 2:return d.trys.push([2,4,,5]),i=new Ye(r),[4,this.lexRuntimeServiceClient.send(i)];case 3:return c=d.sent(),this.reportBotStatus(c,e),[2,c];case 4:return o=d.sent(),[2,Promise.reject(o)];case 5:return[3,11];case 6:s=t.content,a=t.options.messageType,r="voice"===a?{botAlias:this._config[e].alias,botName:e,contentType:"audio/x-l16; sample-rate=16000",inputStream:s,userId:n.identityId,accept:"audio/mpeg"}:{botAlias:this._config[e].alias,botName:e,contentType:"text/plain; charset=utf-8",inputStream:s,userId:n.identityId,accept:"audio/mpeg"},it.debug("postContent to lex",t),d.label=7;case 7:return d.trys.push([7,10,,11]),u=new Je(r),[4,this.lexRuntimeServiceClient.send(u)];case 8:return c=d.sent(),[4,Qe(c.audioStream)];case 9:return f=d.sent(),this.reportBotStatus(c,e),[2,tt(tt({},c),{audioStream:f})];case 10:return l=d.sent(),[2,Promise.reject(l)];case 11:return[2]}}))}))},t.prototype.onComplete=function(e,t){if(!this._config[e])throw new ErrorEvent("Bot "+e+" does not exist");this._botsCompleteCallback[e]=t},t}(a),st=function(){return(st=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},at=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},ut=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},ct=new r.a("Interactions"),ft=new(function(){function e(e){this._options=e,ct.debug("Interactions Options",this._options),this._pluggables={}}return e.prototype.getModuleName=function(){return"Interactions"},e.prototype.configure=function(e){var t=this,n=e?e.Interactions||e:{};ct.debug("configure Interactions",{opt:n}),this._options=st(st({bots:{}},n),n.Interactions);var r=this._options.aws_bots_config,i=this._options.bots;return!Object.keys(i).length&&r&&Array.isArray(r)&&r.forEach((function(e){t._options.bots[e.name]=e})),!this._pluggables.AWSLexProvider&&i&&Object.keys(i).map((function(e){return i[e]})).find((function(e){return!e.providerName||"AWSLexProvider"===e.providerName}))&&(this._pluggables.AWSLexProvider=new ot),Object.keys(this._pluggables).map((function(e){t._pluggables[e].configure(t._options.bots)})),this._options},e.prototype.addPluggable=function(e){if(e&&"Interactions"===e.getCategory()){if(this._pluggables[e.getProviderName()])throw new Error("Bot "+e.getProviderName()+" already plugged");return e.configure(this._options.bots),void(this._pluggables[e.getProviderName()]=e)}},e.prototype.send=function(e,t){return at(this,void 0,void 0,(function(){var n;return ut(this,(function(r){switch(r.label){case 0:if(!this._options.bots||!this._options.bots[e])throw new Error("Bot "+e+" does not exist");if(n=this._options.bots[e].providerName||"AWSLexProvider",!this._pluggables[n])throw new Error("Bot "+n+" does not have valid pluggin did you try addPluggable first?");return[4,this._pluggables[n].sendMessage(e,t)];case 1:return[2,r.sent()]}}))}))},e.prototype.onComplete=function(e,t){if(!this._options.bots||!this._options.bots[e])throw new Error("Bot "+e+" does not exist");var n=this._options.bots[e].providerName||"AWSLexProvider";if(!this._pluggables[n])throw new Error("Bot "+n+" does not have valid pluggin did you try addPluggable first?");this._pluggables[n].onComplete(e,t)},e}())(null);i.a.register(ft)},function(e,t,n){"use strict";n.d(t,"a",(function(){return be}));var r=n(44),i=n(33),o=n(50),s=n(89),a=function(e,t){return(a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}a(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var c=function(){return(c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function f(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function l(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}Object.create;var d,h,p,v,g,m,b,y;Object.create;(d||(d={})).filterSensitiveLog=function(e){return c({},e)},(h||(h={})).filterSensitiveLog=function(e){return c({},e)},(p||(p={})).filterSensitiveLog=function(e){return c({},e)},(v||(v={})).filterSensitiveLog=function(e){return c({},e)},(g||(g={})).filterSensitiveLog=function(e){return c({},e)},(m||(m={})).filterSensitiveLog=function(e){return c({},e)},(b||(b={})).filterSensitiveLog=function(e){return c({},e)},(y||(y={})).filterSensitiveLog=function(e){return c({},e)};var w,_,S,E=n(2),M=n(0),A=function(e,t){return f(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,f;return l(this,(function(l){switch(l.label){case 0:return r=[c({},e)],f={},[4,T(e.body,t)];case 1:switch(n=c.apply(void 0,r.concat([(f.body=l.sent(),f)])),o="UnknownError",o=P(e,n.body),o){case"InvalidInputException":case"com.amazonaws.personalizeevents#InvalidInputException":return[3,2]}return[3,4];case 2:return s=[{}],[4,I(n,t)];case 3:return i=c.apply(void 0,[c.apply(void 0,s.concat([l.sent()])),{name:o,$metadata:x(e)}]),[3,5];case 4:a=n.body,o=a.code||a.Code||o,i=c(c({},a),{name:""+o,message:a.message||a.Message||o,$fault:"client",$metadata:x(e)}),l.label=5;case 5:return u=i.message||i.Message||o,i.message=u,delete i.Message,[2,Promise.reject(Object.assign(new Error(u),i))]}}))}))},I=function(e,t){return f(void 0,void 0,void 0,(function(){var t,n;return l(this,(function(r){return t={name:"InvalidInputException",$fault:"client",$metadata:x(e),message:void 0},void 0!==(n=e.body).message&&null!==n.message&&(t.message=n.message),[2,t]}))}))},k=function(e,t){return e.map((function(e){return function(e,t){return c(c(c(c(c(c(c(c({},void 0!==e.eventId&&{eventId:e.eventId}),void 0!==e.eventType&&{eventType:e.eventType}),void 0!==e.eventValue&&{eventValue:e.eventValue}),void 0!==e.impression&&{impression:O(e.impression,t)}),void 0!==e.itemId&&{itemId:e.itemId}),void 0!==e.properties&&{properties:M.c.fromObject(e.properties)}),void 0!==e.recommendationId&&{recommendationId:e.recommendationId}),void 0!==e.sentAt&&{sentAt:Math.round(e.sentAt.getTime()/1e3)})}(e,t)}))},O=function(e,t){return e.map((function(e){return e}))},x=function(e){return{httpStatusCode:e.statusCode,httpHeaders:e.headers,requestId:e.headers["x-amzn-requestid"]}},C=function(e,t){return void 0===e&&(e=new Uint8Array),e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array)},T=function(e,t){return function(e,t){return C(e,t).then((function(e){return t.utf8Encoder(e)}))}(e,t).then((function(e){return e.length?JSON.parse(e):{}}))},P=function(e,t){var n,r,i=function(e){var t=e;return t.indexOf(":")>=0&&(t=t.split(":")[0]),t.indexOf("#")>=0&&(t=t.split("#")[1]),t},o=(n=e.headers,r="x-amzn-errortype",Object.keys(n).find((function(e){return e.toLowerCase()===r.toLowerCase()})));return void 0!==o?i(e.headers[o]):void 0!==t.code?i(t.code):void 0!==t.__type?i(t.__type):""},N=n(10),R=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return u(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(N.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"PersonalizeEventsClient",commandName:"PutEventsCommand",inputFilterSensitiveLog:p.filterSensitiveLog,outputFilterSensitiveLog:function(e){return e}};"function"==typeof i.info&&i.info({clientName:"PersonalizeEventsClient",commandName:"PutEventsCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return f(void 0,void 0,void 0,(function(){var n,r,i,o,s,a,u,f;return l(this,(function(l){switch(l.label){case 0:return n={"Content-Type":"application/json"},r="/events",i=JSON.stringify(c(c(c(c({},void 0!==e.eventList&&{eventList:k(e.eventList,t)}),void 0!==e.sessionId&&{sessionId:e.sessionId}),void 0!==e.trackingId&&{trackingId:e.trackingId}),void 0!==e.userId&&{userId:e.userId})),[4,t.endpoint()];case 1:return o=l.sent(),s=o.hostname,a=o.protocol,u=void 0===a?"https":a,f=o.port,[2,new E.a({protocol:u,hostname:s,port:f,method:"POST",headers:n,path:r,body:i})]}}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return f(void 0,void 0,void 0,(function(){var n;return l(this,(function(r){switch(r.label){case 0:return 200!==e.statusCode&&e.statusCode>=300?[2,A(e,t)]:(n={$metadata:x(e)},[4,C(e.body,t)]);case 1:return r.sent(),[2,Promise.resolve(n)]}}))}))}(e,t)},t}(M.b),L=n(152),j=n(38),D=n(18),U=n(24),B=n(11),F=n(39),z=n(17),q=n(40),K=n(41),H=n(15),V="personalize-events.{region}.amazonaws.com",G=new Set(["ap-east-1","ap-northeast-1","ap-northeast-2","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-north-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-west-1","us-west-2"]),W=new Set(["cn-north-1","cn-northwest-1"]),$=new Set(["us-iso-east-1"]),Y=new Set(["us-isob-east-1"]),J=new Set(["us-gov-east-1","us-gov-west-1"]),Z=c(c({},{apiVersion:"2018-03-22",disableHostPrefix:!1,logger:{},regionInfoProvider:function(e,t){var n=void 0;return G.has(e)&&(n={hostname:V.replace("{region}",e),partition:"aws"}),W.has(e)&&(n={hostname:"personalize-events.{region}.amazonaws.com.cn".replace("{region}",e),partition:"aws-cn"}),$.has(e)&&(n={hostname:"personalize-events.{region}.c2s.ic.gov".replace("{region}",e),partition:"aws-iso"}),Y.has(e)&&(n={hostname:"personalize-events.{region}.sc2s.sgov.gov".replace("{region}",e),partition:"aws-iso-b"}),J.has(e)&&(n={hostname:"personalize-events.{region}.amazonaws.com".replace("{region}",e),partition:"aws-us-gov"}),void 0===n&&(n={hostname:V.replace("{region}",e),partition:"aws"}),Promise.resolve(n)},signingName:"personalize"}),{runtime:"browser",base64Decoder:z.a,base64Encoder:z.b,bodyLengthChecker:q.a,credentialDefaultProvider:Object(U.a)("Credential is missing"),defaultUserAgent:Object(K.a)(L.name,L.version),maxAttempts:B.a,region:Object(U.a)("Region is missing"),requestHandler:new D.a,sha256:j.Sha256,streamCollector:D.b,urlParser:F.a,utf8Decoder:H.a,utf8Encoder:H.b}),X=n(22),Q=n(37),ee=n(21),te=n(43),ne=n(25),re=n(23),ie=function(e){function t(t){var n=this,r=c(c({},Z),t),i=Object(X.b)(r),o=Object(X.a)(i),s=Object(ne.b)(o),a=Object(B.c)(s),u=Object(re.b)(a),f=Object(ee.b)(u);return(n=e.call(this,f)||this).config=f,n.middlewareStack.use(Object(ne.a)(n.config)),n.middlewareStack.use(Object(B.b)(n.config)),n.middlewareStack.use(Object(re.a)(n.config)),n.middlewareStack.use(Object(Q.a)(n.config)),n.middlewareStack.use(Object(ee.a)(n.config)),n.middlewareStack.use(Object(te.a)(n.config)),n}return u(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this)},t}(M.a),oe=n(36),se=n.n(oe),ae=n(108),ue=n.n(ae),ce=n(27),fe=n(26),le=(new r.a("AmazonPersonalizeProvider"),function(){function e(e){void 0===e&&(e=""),this._isBrowser=i.a.browserOrNode().isBrowser,this._timerKey=Object(ce.v1)().substr(0,15),this._refreshTimer()}return e.prototype._refreshTimer=function(){this._timer&&clearInterval(this._timer);var e=this;this._timer=setInterval((function(){e._timerKey=Object(ce.v1)().substr(0,15)}),3e4)},e.prototype.storeValue=function(e,t){var n=new Date,r=new Date;r.setTime(n.getTime()+6048e5),fe.a.setItem(this._getCachePrefix(e),t,{expires:r.getTime()})},e.prototype.retrieveValue=function(e){return fe.a.getItem(this._getCachePrefix(e))},e.prototype._getCachePrefix=function(e){return this._isBrowser?e+"."+window.location.host:"peronslize"},e.prototype.getTimerKey=function(){return this._timerKey},e.prototype.updateSessionInfo=function(e,t){var n=t.userId,r=t.sessionId;if(this._isRequireNewSession(e,n,r)){var i=Object(ce.v1)();this.storeValue("_awsct_uid",e),this.storeValue("_awsct_sid",i),t.sessionId=i}else this._isRequireUpdateSessionInfo(e,n,r)&&this.storeValue("_awsct_uid",e);t.userId=e},e.prototype._isRequireUpdateSessionInfo=function(e,t,n){return!se()(n)&&se()(t)&&!se()(e)},e.prototype.retrieveSessionInfo=function(e){var t={};return t.trackingId=e,t.sessionId=this.retrieveValue("_awsct_sid"),t.userId=this.retrieveValue("_awsct_uid"),se()(t.sessionId)&&(t.sessionId=Object(ce.v1)(),this.storeValue("_awsct_sid",t.sessionId)),this.storeValue("_awsct",e),t},e.prototype._isRequireNewSession=function(e,t,n){var r=se()(n),i=se()(e)&&!se()(t),o=!se()(e)&&!se()(t)&&!ue()(e,t);return r||i||o},e}());!function(e){e.PLAY="play",e.PAUSE="pause",e.ENDED="Ended"}(w||(w={})),function(e){e.IFRAME="IFRAME",e.VIDEO="VIDEO",e.AUDIO="AUDIO"}(_||(_={})),function(e){e.PLAY="Play",e.ENDED="Ended",e.PAUSE="Pause",e.TIME_WATCHED="TimeWatched"}(S||(S={}));var de=function(){function e(e,t){var n;this.eventActionMapping=((n={})[S.ENDED]=this.endedEventAction.bind(this),n[S.PLAY]=this.playEventAction.bind(this),n[S.PAUSE]=this.pauseEventAction.bind(this),n);var r=e.eventData;this._params=e,this._mediaElement=document.getElementById(r.properties.domElementId),this._started=!1,this._provider=t,{IFRAME:this._iframeMediaTracker,VIDEO:this._html5MediaTracker,AUDIO:this._html5MediaTracker}[this._mediaElement.tagName].bind(this)(),this._initYoutubeFrame()}return e.prototype._initYoutubeFrame=function(){this._youTubeIframeLoader={src:"https://www.youtube.com/iframe_api",loading:!1,loaded:!1,listeners:[],load:function(e){var t=this;if(this.listeners.push(e),this.loaded)setTimeout((function(){t.done()}));else if(!this.loading){this.loading=!0,window.onYouTubeIframeAPIReady=function(){t.loaded=!0,t.done()};var n=document.createElement("script");n.type="text/javascript",n.src=this.src,document.body.appendChild(n)}},done:function(){for(delete window.onYouTubeIframeAPIReady;this.listeners.length;)this.listeners.pop()(window.YT)}}},e.prototype._iframeMediaTracker=function(){var e=this;setInterval((function(){e._started&&e.recordEvent(_.IFRAME,S.TIME_WATCHED)}),3e3),this._youTubeIframeLoader.load((function(t){e._iframePlayer=new t.Player(e._mediaElement.id,{events:{onStateChange:e._onPlayerStateChange.bind(e)}})}))},e.prototype._onPlayerStateChange=function(e){var t={0:S.ENDED,1:S.PLAY,2:S.PAUSE}[e.data];t&&this.eventActionMapping[t](_.IFRAME)},e.prototype._html5MediaTracker=function(){var e=this;setInterval((function(){e._started&&e.recordEvent(_.VIDEO,S.TIME_WATCHED)}),3e3),this._mediaElement.addEventListener(w.PLAY,(function(){e.eventActionMapping[S.PLAY](_.VIDEO)}),!1),this._mediaElement.addEventListener(w.PAUSE,(function(){e.eventActionMapping[S.PAUSE](_.VIDEO)}),!1),this._mediaElement.addEventListener(w.ENDED,(function(){e.eventActionMapping[S.ENDED](_.VIDEO)}),!1)},e.prototype.playEventAction=function(e){this._started=!0,this.recordEvent(e,S.PLAY)},e.prototype.pauseEventAction=function(e){this._started=!1,this.recordEvent(e,S.PAUSE)},e.prototype.endedEventAction=function(e){this._started=!1,this.recordEvent(e,S.ENDED)},e.prototype.recordEvent=function(e,t){var n=Object.assign({},this._params),r=n.eventData;r.eventType=t,e===_.VIDEO?(r.properties.timestamp=this._mediaElement.currentTime,r.properties.duration=this._mediaElement.duration):(r.properties.timestamp=this._financial(this._iframePlayer.getCurrentTime()),r.properties.duration=this._financial(this._iframePlayer.getDuration()));var i=parseFloat(r.properties.timestamp)/parseFloat(r.properties.duration);r.properties.eventValue=Number(i.toFixed(4)),delete r.properties.domElementId,this._provider.putToBuffer(n)},e.prototype._financial=function(e){return Number.parseFloat(e).toFixed(4)},e}(),he=n(252),pe=n.n(he),ve=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},ge=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},me=new r.a("AmazonPersonalizeProvider"),be=function(){function e(e){this._buffer=[],this._config=e||{},this._config.flushSize=this._config.flushSize>0&&this._config.flushSize<=10?this._config.flushSize:5,this._config.flushInterval=this._config.flushInterval||5e3,this._sessionManager=new le,se()(this._config.trackingId)||(this._sessionInfo=this._sessionManager.retrieveSessionInfo(this._config.trackingId)),this._isBrowser=i.a.browserOrNode().isBrowser,this._setupTimer()}return e.prototype._setupTimer=function(){this._timer&&clearInterval(this._timer);var e=this._config.flushInterval,t=this;this._timer=setInterval((function(){t._sendFromBuffer()}),e)},e.prototype.record=function(e){return ve(this,void 0,void 0,(function(){var t,n,r,i,o;return ge(this,(function(s){switch(s.label){case 0:return[4,this._getCredentials()];case 1:return(t=s.sent())?(Object.assign(e,{config:this._config,credentials:t,sentAt:new Date}),n=e.event,r=n.eventType,i=n.properties,"Identify"===r?(this._sessionManager.updateSessionInfo(i&&i.userId?i.userId:"",this._sessionInfo),[2]):(se()(e.event.userId)||this._sessionManager.updateSessionInfo(e.event.userId,this._sessionInfo),o=this.generateRequestParams(e,this._sessionInfo),"MediaAutoTrack"!==r?[3,7]:this._isBrowser?se()(pe()(o,"eventData.properties.domElementId",null))?[3,3]:[4,this.isElementFullyLoaded(this.loadElement,o.eventData.properties.domElementId,500,5)]:[3,5])):[2,Promise.resolve(!1)];case 2:return s.sent()?new de(o,this):me.debug("Cannot find the media element."),[3,4];case 3:me.debug("Missing domElementId field in 'properties' for MediaAutoTrack event type."),s.label=4;case 4:return[3,6];case 5:me.debug("MediaAutoTrack only for browser"),s.label=6;case 6:return[2];case 7:return[2,this.putToBuffer(o)]}}))}))},e.prototype.loadElement=function(e){return new Promise((function(t,n){return document.getElementById(e)&&document.getElementById(e).clientHeight?t(!0):n(!0)}))},e.prototype.isElementFullyLoaded=function(e,t,n,r){var i=this;return new Promise((function(o,s){return e(t).then(o).catch((function(a){return r-1>0?(u=n,new Promise((function(e){return setTimeout(e,u)}))).then(i.isElementFullyLoaded.bind(null,e,t,n,r-1)).then(o).catch(s):s(a);var u}))}))},e.prototype.getCategory=function(){return"Analytics"},e.prototype.getProviderName=function(){return"AmazonPersonalize"},e.prototype.configure=function(e){me.debug("configure Analytics",e);var t=e||{};return this._config=Object.assign({},this._config,t),se()(this._config.trackingId)||(this._sessionInfo=this._sessionManager.retrieveSessionInfo(this._config.trackingId)),this._setupTimer(),this._config},e.prototype.generateRequestParams=function(e,t){var n={},r=e.event,i=r.eventType,o=r.properties;return n.eventData={eventType:i,properties:o},n.sessionInfo=t,n.sentAt=e.sentAt,n.credentials=e.credentials,n.config=e.config,n},e.prototype._sendEvents=function(e){var t=e.length;if(0!==t){var n=e[0],r=n.config,i=n.credentials,o=n.sessionInfo;if(!this._init(r,i))return!1;if(t>0){for(var s=[],a=0;a<t;a+=1){var u=e.shift(),c=this._generateSingleRecordPayload(u,o);s.push(c)}var f={};f.trackingId=o.trackingId,f.sessionId=o.sessionId,f.userId=o.userId,f.eventList=[],s.forEach((function(e){f.eventList.push(e)}));var l=new R(f);this._personalize.send(l,(function(e){e?me.debug("Failed to call putEvents in Personalize",e):me.debug("Put events")}))}}else me.debug("events array is empty, directly return")},e.prototype.putToBuffer=function(e){return this._buffer.length<this._config.flushSize?this._buffer.push(e):(this._buffer.push(e),this._sendFromBuffer()),Promise.resolve(!0)},e.prototype._sendFromBuffer=function(){var e=this,t=this._buffer.length;if(!(t<=0)){for(var n=[],r=null,i=[],o=0;o<t;o+=1){var s=this._buffer.shift(),a=s.credentials,u=s.sessionInfo;0===o?(i.push(s),r=a):ue()(u,this._sessionInfo)&&a.sessionToken===r.sessionToken&&a.identityId===r.identityId?(me.debug("no change for cred, put event in the same group"),i.push(s)):(n.push(i),(i=[]).push(s),r=a,this._sessionInfo=u)}n.push(i),n.map((function(t){e._sendEvents(t)}))}},e.prototype._generateSingleRecordPayload=function(e,t){var n=e.eventData,r=e.sentAt,i={};return i.sentAt=r,i.properties=n.properties&&JSON.stringify(n.properties),i.eventId=this._sessionManager.getTimerKey()+t.sessionId,i.eventType=n.eventType,i},e.prototype._init=function(e,t){if(me.debug("init clients"),this._personalize&&this._config.credentials&&this._config.credentials.sessionToken===t.sessionToken&&this._config.credentials.identityId===t.identityId)return me.debug("no change for analytics config, directly return from init"),!0;this._config.credentials=t;var n=e.region;return me.debug("initialize personalize with credentials",t),this._personalize=new ie({region:n,credentials:t,customUserAgent:Object(o.b)()}),!0},e.prototype._getCredentials=function(){var e=this;return s.a.get().then((function(t){return t?(me.debug("set credentials for analytics",e._config.credentials),s.a.shear(t)):null})).catch((function(e){return me.debug("ensure credentials error",e),null}))},e}()},function(e,t,n){"use strict";n.d(t,"a",(function(){return X}));var r=n(44),i=n(88),o=n(221),s=n(19),a=n(144),u=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},c=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(u(arguments[t]));return e},f=[],l=function(){function e(e,t){this.context=e,this.methodName=t,this._originalMethod=e[t].bind(e)}return e.add=function(e,t,n){d(e,t).set(n)},e.remove=function(e,t){d(e,t).remove()},e.prototype.set=function(e){var t=this;this.context[this.methodName]=function(){for(var n=[],r=0;r<arguments.length;r++)n[r]=arguments[r];return e(t._originalMethod.apply(t,c(n)))}},e.prototype.remove=function(){this.context[this.methodName]=this._originalMethod},e}();function d(e,t){var n=f.filter((function(n){return n.context===e&&n.methodName===t}))[0];return n||(n=new l(e,t),f.push(n)),n}var h=n(33),p=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},v=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},g=new r.a("PageViewTracker"),m="aws-amplify-analytics-prevUrl",b={enable:!1,provider:"AWSPinpoint",getUrl:function(){return h.a.browserOrNode().isBrowser?window.location.origin+window.location.pathname:""}},y=function(){function e(e,t){g.debug("initialize pageview tracker with opts",t),this._config=Object.assign({},b,t),this._tracker=e,this._hasEnabled=!1,this._trackFunc=this._trackFunc.bind(this),"SPA"===this._config.type?this._pageViewTrackSPA():this._pageViewTrackDefault()}return e.prototype.configure=function(e){return Object.assign(this._config,e),"SPA"===this._config.type&&this._pageViewTrackSPA(),this._config},e.prototype._isSameUrl=function(){return sessionStorage.getItem(m)===this._config.getUrl()&&(g.debug("the url is same"),!0)},e.prototype._pageViewTrackDefault=function(){return p(this,void 0,void 0,(function(){var e,t,n,r;return v(this,(function(i){switch(i.label){case 0:return h.a.browserOrNode().isBrowser&&window.addEventListener&&window.sessionStorage?(e=this._config.getUrl(),"function"!=typeof this._config.attributes?[3,2]:[4,this._config.attributes()]):(g.debug("not in the supported web enviroment"),[2]);case 1:return n=i.sent(),[3,3];case 2:n=this._config.attributes,i.label=3;case 3:return t=n,r=Object.assign({url:e},t),this._config.enable&&!this._isSameUrl()&&(this._tracker({name:this._config.eventName||"pageView",attributes:r},this._config.provider).catch((function(e){g.debug("Failed to record the page view event",e)})),sessionStorage.setItem(m,e)),[2]}}))}))},e.prototype._trackFunc=function(){return p(this,void 0,void 0,(function(){var e,t,n,r;return v(this,(function(i){switch(i.label){case 0:return h.a.browserOrNode().isBrowser&&window.addEventListener&&history.pushState&&window.sessionStorage?(e=this._config.getUrl(),"function"!=typeof this._config.attributes?[3,2]:[4,this._config.attributes()]):(g.debug("not in the supported web enviroment"),[2]);case 1:return n=i.sent(),[3,3];case 2:n=this._config.attributes,i.label=3;case 3:return t=n,r=Object.assign({url:e},t),this._isSameUrl()||(this._tracker({name:this._config.eventName||"pageView",attributes:r},this._config.provider).catch((function(e){g.debug("Failed to record the page view event",e)})),sessionStorage.setItem(m,e)),[2]}}))}))},e.prototype._pageViewTrackSPA=function(){h.a.browserOrNode().isBrowser&&window.addEventListener&&history.pushState?this._config.enable&&!this._hasEnabled?(l.add(history,"pushState",this._trackFunc),l.add(history,"replaceState",this._trackFunc),window.addEventListener("popstate",this._trackFunc),this._trackFunc(),this._hasEnabled=!0):(l.remove(history,"pushState"),l.remove(history,"replaceState"),window.removeEventListener("popstate",this._trackFunc),this._hasEnabled=!1):g.debug("not in the supported web enviroment")},e}(),w=h.a.browserOrNode().isBrowser&&window.Element?window.Element.prototype:null,_=w?w.matches||w.matchesSelector||w.webkitMatchesSelector||w.mozMatchesSelector||w.msMatchesSelector||w.oMatchesSelector:null;function S(e,t){if(e&&1===e.nodeType&&t){if("string"==typeof t||1===t.nodeType)return e===t||E(e,t);if("length"in t)for(var n=0,r=void 0;r=t[n];n++)if(e===r||E(e,r))return!0}return!1}function E(e,t){if("string"!=typeof t)return!1;if(_)return _.call(e,t);for(var n=e.parentNode.querySelectorAll(t),r=0,i=void 0;i=n[r];r++)if(i===e)return!0;return!1}function M(e,t,n,r,i){void 0===i&&(i={});var o=function(e){var t;if(i.composed&&"function"==typeof e.composedPath)for(var o=e.composedPath(),s=0,a=void 0;a=o[s];s++)1===a.nodeType&&S(a,n)&&(t=a);else t=function(e,t,n){if(void 0===n&&(n=!1),e&&1===e.nodeType&&t)for(var r,i=(n?[e]:[]).concat(function(e){for(var t=[],n=e;n&&n.parentNode&&1===n.parentNode.nodeType;)n=n.parentNode,t.push(n);return t}(e)),o=0;r=i[o];o++)if(S(r,t))return r}(e.target,n,!0);t&&r.call(t,e,t)};return e.addEventListener(t,o,i.useCapture),{destroy:function(){e.removeEventListener(t,o,i.useCapture)}}}var A=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},I=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},k=new r.a("EventTracker"),O={enable:!1,events:["click"],selectorPrefix:"data-amplify-analytics-",provider:"AWSPinpoint"},x=function(){function e(e,t){h.a.browserOrNode().isBrowser&&window.addEventListener?(this._config=Object.assign({},O,t),this._tracker=e,this._delegates={},this._trackFunc=this._trackFunc.bind(this),k.debug("initialize pageview tracker with opts",this._config),this.configure(this._config)):k.debug("not in the supported web environment")}return e.prototype.configure=function(e){var t=this;if(Object.assign(this._config,e),this._config.enable){if(this._config.enable&&0===Object.keys(this._delegates).length){var n="["+this._config.selectorPrefix+"on]";this._config.events.forEach((function(e){t._delegates[e]=M(document,e,n,t._trackFunc,{composed:!0,useCapture:!0})}))}}else Object.keys(this._delegates).forEach((function(e){"function"==typeof t._delegates[e].destroy&&t._delegates[e].destroy()})),this._delegates={};return this._config},e.prototype._trackFunc=function(e,t){return A(this,void 0,void 0,(function(){var n,r,i,o,s,a,u;return I(this,(function(c){switch(c.label){case 0:return n={},r=t.getAttribute(this._config.selectorPrefix+"on").split(/\s*,\s*/),i=t.getAttribute(this._config.selectorPrefix+"name"),(o=t.getAttribute(this._config.selectorPrefix+"attrs"))&&o.split(/\s*,\s*/).forEach((function(e){var t=e.trim().split(/\s*:\s*/);n[t[0]]=t[1]})),"function"!=typeof this._config.attributes?[3,2]:[4,this._config.attributes()];case 1:return a=c.sent(),[3,3];case 2:a=this._config.attributes,c.label=3;case 3:return s=a,u=Object.assign({type:e.type,target:e.target.localName+" with id "+e.target.id},s,n),k.debug("events needed to be recorded",r),k.debug("attributes needed to be attached",n),r.indexOf(e.type)<0?(k.debug("event "+e.type+" is not selected to be recorded"),[2]):(this._tracker({name:i||"event",attributes:u},this._config.provider).catch((function(t){k.debug("Failed to record the "+e.type+" event', "+t)})),[2])}}))}))},e}(),C=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},T=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},P=new r.a("SessionTracker"),N={enable:!1,provider:"AWSPinpoint"},R=!1,L=function(){function e(e,t){this._config=Object.assign({},N,t),this._tracker=e,this._hasEnabled=!1,this._trackFunc=this._trackFunc.bind(this),this._trackBeforeUnload=this._trackBeforeUnload.bind(this),this.configure(this._config)}return e.prototype._envCheck=function(){if(!h.a.browserOrNode().isBrowser)return!1;if(!document||!document.addEventListener)return P.debug("not in the supported web environment"),!1;if(void 0!==document.hidden)this._hidden="hidden",this._visibilityChange="visibilitychange";else if(void 0!==document.msHidden)this._hidden="msHidden",this._visibilityChange="msvisibilitychange";else{if(void 0===document.webkitHidden)return P.debug("not in the supported web environment"),!1;this._hidden="webkitHidden",this._visibilityChange="webkitvisibilitychange"}return!0},e.prototype._trackFunc=function(){return C(this,void 0,void 0,(function(){var e,t,n;return T(this,(function(r){switch(r.label){case 0:return"function"!=typeof this._config.attributes?[3,2]:[4,this._config.attributes()];case 1:return t=r.sent(),[3,3];case 2:t=this._config.attributes,r.label=3;case 3:return e=t,n=Object.assign({},e),document.visibilityState===this._hidden?this._tracker({name:"_session.stop",attributes:n},this._config.provider).catch((function(e){P.debug("record session stop event failed.",e)})):this._tracker({name:"_session.start",attributes:n},this._config.provider).catch((function(e){P.debug("record session start event failed.",e)})),[2]}}))}))},e.prototype._trackBeforeUnload=function(e){var t=this;("function"==typeof this._config.attributes?Promise.resolve(this._config.attributes()):Promise.resolve(this._config.attributes)).then((function(e){var n=Object.assign({},e);t._tracker({name:"_session.stop",attributes:n,immediate:!0},t._config.provider).catch((function(e){P.debug("record session stop event failed.",e)}))}))},e.prototype._sendInitialEvent=function(){return C(this,void 0,void 0,(function(){var e,t,n;return T(this,(function(r){switch(r.label){case 0:return R?(P.debug("the start session has been sent when the page is loaded"),[2]):(R=!0,"function"!=typeof this._config.attributes?[3,2]:[4,this._config.attributes()]);case 1:return t=r.sent(),[3,3];case 2:t=this._config.attributes,r.label=3;case 3:return e=t,n=Object.assign({},e),this._tracker({name:"_session.start",attributes:n},this._config.provider).catch((function(e){P.debug("record session start event failed.",e)})),[2]}}))}))},e.prototype.configure=function(e){return this._envCheck()?(Object.assign(this._config,e),this._config.enable&&!this._hasEnabled?(this._sendInitialEvent(),document.addEventListener(this._visibilityChange,this._trackFunc,!1),window.addEventListener("beforeunload",this._trackBeforeUnload,!1),this._hasEnabled=!0):!this._config.enable&&this._hasEnabled&&(document.removeEventListener(this._visibilityChange,this._trackFunc,!1),window.removeEventListener("beforeunload",this._trackBeforeUnload,!1),this._hasEnabled=!1),this._config):this._config},e}(),j=function(){return(j=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},D=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},U=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},B=new r.a("AnalyticsClass"),F="undefined"!=typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("amplify_default"):"@@amplify_default",z={pageView:y,event:x,session:L},q=null,K=function(){function e(){this._config={},this._pluggables=[],this._disabled=!1,this._trackers={},q=this,this.record=this.record.bind(this),i.a.listen("auth",W),i.a.listen("storage",W),i.a.listen("analytics",W)}return e.prototype.getModuleName=function(){return"Analytics"},e.prototype.configure=function(e){var t=this;if(!e)return this._config;B.debug("configure Analytics",e);var n,r,s,u=o.a.parseMobilehubConfig(e);return this._config=Object.assign({},this._config,u.Analytics,e),this._config.disabled&&(this._disabled=!0),void 0===this._config.autoSessionRecord&&(this._config.autoSessionRecord=!0),this._pluggables.forEach((function(e){var n="AWSPinpoint"!==e.getProviderName()||t._config.AWSPinpoint?t._config[e.getProviderName()]:t._config;e.configure(j({disabled:t._config.disabled,autoSessionRecord:t._config.autoSessionRecord},n))})),0===this._pluggables.length&&this.addPluggable(new a.a),n="configured",r=null,s="The Analytics category has been configured successfully",i.a.dispatch("analytics",{event:n,data:r,message:s},"Analytics",F),B.debug("current configuration",this._config),this._config},e.prototype.addPluggable=function(e){if(e&&"Analytics"===e.getCategory()){this._pluggables.push(e);var t="AWSPinpoint"!==e.getProviderName()||this._config.AWSPinpoint?this._config[e.getProviderName()]:this._config,n=j({disabled:this._config.disabled},t);return e.configure(n),n}},e.prototype.getPluggable=function(e){for(var t=0;t<this._pluggables.length;t+=1){var n=this._pluggables[t];if(n.getProviderName()===e)return n}return B.debug("No plugin found with providerName",e),null},e.prototype.removePluggable=function(e){for(var t=0;t<this._pluggables.length&&this._pluggables[t].getProviderName()!==e;)t+=1;return t===this._pluggables.length?void B.debug("No plugin found with providerName",e):void this._pluggables.splice(t,t+1)},e.prototype.disable=function(){this._disabled=!0},e.prototype.enable=function(){this._disabled=!1},e.prototype.startSession=function(e){return D(this,void 0,void 0,(function(){var t;return U(this,(function(n){return t={event:{name:"_session.start"},provider:e},[2,this._sendEvent(t)]}))}))},e.prototype.stopSession=function(e){return D(this,void 0,void 0,(function(){var t;return U(this,(function(n){return t={event:{name:"_session.stop"},provider:e},[2,this._sendEvent(t)]}))}))},e.prototype.record=function(e,t,n){return D(this,void 0,void 0,(function(){var r;return U(this,(function(i){return r=null,r="string"==typeof e?{event:{name:e,attributes:t,metrics:n},provider:"AWSPinpoint"}:{event:e,provider:t},[2,this._sendEvent(r)]}))}))},e.prototype.updateEndpoint=function(e,t){return D(this,void 0,void 0,(function(){var n;return U(this,(function(r){return n=j(j({},e),{name:"_update_endpoint"}),[2,this.record(n,t)]}))}))},e.prototype._sendEvent=function(e){var t=this;if(this._disabled)return B.debug("Analytics has been disabled"),Promise.resolve();var n=e.provider?e.provider:"AWSPinpoint";return new Promise((function(r,i){t._pluggables.forEach((function(t){t.getProviderName()===n&&t.record(e,{resolve:r,reject:i})}))}))},e.prototype.autoTrack=function(e,t){if(z[e]){"session"===e&&(this._config.autoSessionRecord=t.enable);var n=this._trackers[e];n?n.configure(t):this._trackers[e]=new z[e](this.record,t)}else B.debug("invalid tracker type")},e}(),H=!1,V=!1,G=!1,W=function(e){var t=e.channel,n=e.payload;switch(B.debug("on hub capsule "+t,n),t){case"auth":Y(n);break;case"storage":$(n);break;case"analytics":J(n)}},$=function(e){var t=e.data,n=t.attrs,r=t.metrics;n&&G&&q.record({name:"Storage",attributes:n,metrics:r}).catch((function(e){B.debug("Failed to send the storage event automatically",e)}))},Y=function(e){var t=e.event;if(t){var n=function(e){return D(void 0,void 0,void 0,(function(){var t;return U(this,(function(n){switch(n.label){case 0:if(!V||!G)return[3,4];n.label=1;case 1:return n.trys.push([1,3,,4]),[4,q.record({name:"_userauth."+e})];case 2:return[2,n.sent()];case 3:return t=n.sent(),B.debug("Failed to send the "+e+" event automatically",t),[3,4];case 4:return[2]}}))}))};switch(t){case"signIn":return n("sign_in");case"signUp":return n("sign_up");case"signOut":return n("sign_out");case"signIn_failure":return n("auth_fail");case"configured":(V=!0)&&G&&Z()}}},J=function(e){var t=e.event;if(t)switch(t){case"pinpointProvider_configured":G=!0,V&&G&&Z()}},Z=function(){var e=q.configure();!H&&e.autoSessionRecord&&(q.updateEndpoint({immediate:!0}).catch((function(e){B.debug("Failed to update the endpoint",e)})),H=!0),q.autoTrack("session",{enable:e.autoSessionRecord})},X=new K;s.a.register(X)},function(e,t,n){"use strict";n.d(t,"a",(function(){return Zt}));var r=n(44),i=n(145),o=function(e,t){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function u(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))}function c(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}}Object.create;Object.create;var f,l,d,h,p,v,g,m,b,y,w,_,S,E,M,A,I,k,O,x,C,T,P,N,R,L,j,D,U,B,F,z,q,K,H,V,G,W,$,Y,J,Z,X,Q,ee,te,ne,re,ie,oe,se,ae,ue,ce,fe,le,de,he,pe,ve,ge,me,be,ye,we,_e,Se,Ee,Me,Ae,Ie,ke,Oe,xe,Ce,Te,Pe,Ne,Re,Le,je,De,Ue,Be,Fe,ze,qe,Ke,He,Ve,Ge,We,$e,Ye,Je,Ze,Xe,Qe,et,tt,nt,rt=n(0);(f||(f={})).filterSensitiveLog=function(e){return a({},e)},(l||(l={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.GZIP="GZIP",e.HADOOP_SNAPPY="HADOOP_SNAPPY",e.SNAPPY="Snappy",e.UNCOMPRESSED="UNCOMPRESSED",e.ZIP="ZIP"}(d||(d={})),(h||(h={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.GZIP="GZIP",e.NONE="NONE"}(p||(p={})),(v||(v={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.AWS_OWNED_CMK="AWS_OWNED_CMK",e.CUSTOMER_MANAGED_CMK="CUSTOMER_MANAGED_CMK"}(g||(g={})),(m||(m={})).filterSensitiveLog=function(e){return a({},e)},(b||(b={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.BUFFER_INTERVAL_IN_SECONDS="BufferIntervalInSeconds",e.BUFFER_SIZE_IN_MB="BufferSizeInMBs",e.LAMBDA_ARN="LambdaArn",e.LAMBDA_NUMBER_OF_RETRIES="NumberOfRetries",e.ROLE_ARN="RoleArn"}(y||(y={})),(w||(w={})).filterSensitiveLog=function(e){return a({},e)},(_||(_={})).filterSensitiveLog=function(e){return a({},e)},(S||(S={})).filterSensitiveLog=function(e){return a({},e)},(E||(E={})).filterSensitiveLog=function(e){return a({},e)},(M||(M={})).filterSensitiveLog=function(e){return a({},e)},(A||(A={})).filterSensitiveLog=function(e){return a({},e)},(I||(I={})).filterSensitiveLog=function(e){return a({},e)},(k||(k={})).filterSensitiveLog=function(e){return a({},e)},(O||(O={})).filterSensitiveLog=function(e){return a({},e)},(x||(x={})).filterSensitiveLog=function(e){return a({},e)},(C||(C={})).filterSensitiveLog=function(e){return a({},e)},(T||(T={})).filterSensitiveLog=function(e){return a({},e)},(P||(P={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.NONE="NONE",e.SNAPPY="SNAPPY",e.ZLIB="ZLIB"}(N||(N={})),function(e){e.V0_11="V0_11",e.V0_12="V0_12"}(R||(R={})),(L||(L={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.GZIP="GZIP",e.SNAPPY="SNAPPY",e.UNCOMPRESSED="UNCOMPRESSED"}(j||(j={})),function(e){e.V1="V1",e.V2="V2"}(D||(D={})),(U||(U={})).filterSensitiveLog=function(e){return a({},e)},(B||(B={})).filterSensitiveLog=function(e){return a({},e)},(F||(F={})).filterSensitiveLog=function(e){return a({},e)},(z||(z={})).filterSensitiveLog=function(e){return a({},e)},(q||(q={})).filterSensitiveLog=function(e){return a({},e)},(K||(K={})).filterSensitiveLog=function(e){return a({},e)},(H||(H={})).filterSensitiveLog=function(e){return a({},e)},(V||(V={})).filterSensitiveLog=function(e){return a(a(a({},e),e.Url&&{Url:rt.d}),e.AccessKey&&{AccessKey:rt.d})},(G||(G={})).filterSensitiveLog=function(e){return a(a(a({},e),e.AttributeName&&{AttributeName:rt.d}),e.AttributeValue&&{AttributeValue:rt.d})},(W||(W={})).filterSensitiveLog=function(e){return a(a({},e),e.CommonAttributes&&{CommonAttributes:e.CommonAttributes.map((function(e){return G.filterSensitiveLog(e)}))})},($||($={})).filterSensitiveLog=function(e){return a({},e)},(Y||(Y={})).filterSensitiveLog=function(e){return a(a(a({},e),e.RequestConfiguration&&{RequestConfiguration:W.filterSensitiveLog(e.RequestConfiguration)}),e.EndpointConfiguration&&{EndpointConfiguration:V.filterSensitiveLog(e.EndpointConfiguration)})},(J||(J={})).filterSensitiveLog=function(e){return a({},e)},(Z||(Z={})).filterSensitiveLog=function(e){return a({},e)},(X||(X={})).filterSensitiveLog=function(e){return a(a(a({},e),e.Password&&{Password:rt.d}),e.Username&&{Username:rt.d})},(Q||(Q={})).filterSensitiveLog=function(e){return a({},e)},(ee||(ee={})).filterSensitiveLog=function(e){return a({},e)},(te||(te={})).filterSensitiveLog=function(e){return a({},e)},(ne||(ne={})).filterSensitiveLog=function(e){return a(a(a({},e),e.HttpEndpointDestinationConfiguration&&{HttpEndpointDestinationConfiguration:Y.filterSensitiveLog(e.HttpEndpointDestinationConfiguration)}),e.RedshiftDestinationConfiguration&&{RedshiftDestinationConfiguration:X.filterSensitiveLog(e.RedshiftDestinationConfiguration)})},(re||(re={})).filterSensitiveLog=function(e){return a({},e)},(ie||(ie={})).filterSensitiveLog=function(e){return a({},e)},(oe||(oe={})).filterSensitiveLog=function(e){return a({},e)},(se||(se={})).filterSensitiveLog=function(e){return a({},e)},(ae||(ae={})).filterSensitiveLog=function(e){return a({},e)},(ue||(ue={})).filterSensitiveLog=function(e){return a({},e)},(ce||(ce={})).filterSensitiveLog=function(e){return a({},e)},(fe||(fe={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.CREATE_ENI_FAILED="CREATE_ENI_FAILED",e.CREATE_KMS_GRANT_FAILED="CREATE_KMS_GRANT_FAILED",e.DELETE_ENI_FAILED="DELETE_ENI_FAILED",e.DISABLED_KMS_KEY="DISABLED_KMS_KEY",e.ENI_ACCESS_DENIED="ENI_ACCESS_DENIED",e.INVALID_KMS_KEY="INVALID_KMS_KEY",e.KMS_ACCESS_DENIED="KMS_ACCESS_DENIED",e.KMS_KEY_NOT_FOUND="KMS_KEY_NOT_FOUND",e.KMS_OPT_IN_REQUIRED="KMS_OPT_IN_REQUIRED",e.RETIRE_KMS_GRANT_FAILED="RETIRE_KMS_GRANT_FAILED",e.SECURITY_GROUP_ACCESS_DENIED="SECURITY_GROUP_ACCESS_DENIED",e.SECURITY_GROUP_NOT_FOUND="SECURITY_GROUP_NOT_FOUND",e.SUBNET_ACCESS_DENIED="SUBNET_ACCESS_DENIED",e.SUBNET_NOT_FOUND="SUBNET_NOT_FOUND",e.UNKNOWN_ERROR="UNKNOWN_ERROR"}(le||(le={})),(de||(de={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.DISABLED="DISABLED",e.DISABLING="DISABLING",e.DISABLING_FAILED="DISABLING_FAILED",e.ENABLED="ENABLED",e.ENABLING="ENABLING",e.ENABLING_FAILED="ENABLING_FAILED"}(he||(he={})),(pe||(pe={})).filterSensitiveLog=function(e){return a({},e)},function(e){e.ACTIVE="ACTIVE",e.CREATING="CREATING",e.CREATING_FAILED="CREATING_FAILED",e.DELETING="DELETING",e.DELETING_FAILED="DELETING_FAILED"}(ve||(ve={})),(ge||(ge={})).filterSensitiveLog=function(e){return a({},e)},(me||(me={})).filterSensitiveLog=function(e){return a({},e)},(be||(be={})).filterSensitiveLog=function(e){return a({},e)},(ye||(ye={})).filterSensitiveLog=function(e){return a({},e)},(we||(we={})).filterSensitiveLog=function(e){return a(a({},e),e.Url&&{Url:rt.d})},(_e||(_e={})).filterSensitiveLog=function(e){return a(a(a({},e),e.EndpointConfiguration&&{EndpointConfiguration:we.filterSensitiveLog(e.EndpointConfiguration)}),e.RequestConfiguration&&{RequestConfiguration:W.filterSensitiveLog(e.RequestConfiguration)})},(Se||(Se={})).filterSensitiveLog=function(e){return a(a({},e),e.Username&&{Username:rt.d})},(Ee||(Ee={})).filterSensitiveLog=function(e){return a({},e)},(Me||(Me={})).filterSensitiveLog=function(e){return a(a(a({},e),e.HttpEndpointDestinationDescription&&{HttpEndpointDestinationDescription:_e.filterSensitiveLog(e.HttpEndpointDestinationDescription)}),e.RedshiftDestinationDescription&&{RedshiftDestinationDescription:Se.filterSensitiveLog(e.RedshiftDestinationDescription)})},(Ae||(Ae={})).filterSensitiveLog=function(e){return a({},e)},(Ie||(Ie={})).filterSensitiveLog=function(e){return a({},e)},(ke||(ke={})).filterSensitiveLog=function(e){return a(a({},e),e.Destinations&&{Destinations:e.Destinations.map((function(e){return Me.filterSensitiveLog(e)}))})},(Oe||(Oe={})).filterSensitiveLog=function(e){return a({},e)},(xe||(xe={})).filterSensitiveLog=function(e){return a(a({},e),e.DeliveryStreamDescription&&{DeliveryStreamDescription:ke.filterSensitiveLog(e.DeliveryStreamDescription)})},(Ce||(Ce={})).filterSensitiveLog=function(e){return a({},e)},(Te||(Te={})).filterSensitiveLog=function(e){return a({},e)},(Pe||(Pe={})).filterSensitiveLog=function(e){return a({},e)},(Ne||(Ne={})).filterSensitiveLog=function(e){return a({},e)},(Re||(Re={})).filterSensitiveLog=function(e){return a({},e)},(Le||(Le={})).filterSensitiveLog=function(e){return a({},e)},(je||(je={})).filterSensitiveLog=function(e){return a({},e)},(De||(De={})).filterSensitiveLog=function(e){return a({},e)},(Ue||(Ue={})).filterSensitiveLog=function(e){return a({},e)},(Be||(Be={})).filterSensitiveLog=function(e){return a({},e)},(Fe||(Fe={})).filterSensitiveLog=function(e){return a({},e)},(ze||(ze={})).filterSensitiveLog=function(e){return a({},e)},(qe||(qe={})).filterSensitiveLog=function(e){return a({},e)},(Ke||(Ke={})).filterSensitiveLog=function(e){return a({},e)},(He||(He={})).filterSensitiveLog=function(e){return a({},e)},(Ve||(Ve={})).filterSensitiveLog=function(e){return a({},e)},(Ge||(Ge={})).filterSensitiveLog=function(e){return a({},e)},(We||(We={})).filterSensitiveLog=function(e){return a({},e)},($e||($e={})).filterSensitiveLog=function(e){return a({},e)},(Ye||(Ye={})).filterSensitiveLog=function(e){return a({},e)},(Je||(Je={})).filterSensitiveLog=function(e){return a({},e)},(Ze||(Ze={})).filterSensitiveLog=function(e){return a({},e)},(Xe||(Xe={})).filterSensitiveLog=function(e){return a(a(a({},e),e.EndpointConfiguration&&{EndpointConfiguration:V.filterSensitiveLog(e.EndpointConfiguration)}),e.RequestConfiguration&&{RequestConfiguration:W.filterSensitiveLog(e.RequestConfiguration)})},(Qe||(Qe={})).filterSensitiveLog=function(e){return a(a(a({},e),e.Username&&{Username:rt.d}),e.Password&&{Password:rt.d})},(et||(et={})).filterSensitiveLog=function(e){return a({},e)},(tt||(tt={})).filterSensitiveLog=function(e){return a(a(a({},e),e.HttpEndpointDestinationUpdate&&{HttpEndpointDestinationUpdate:Xe.filterSensitiveLog(e.HttpEndpointDestinationUpdate)}),e.RedshiftDestinationUpdate&&{RedshiftDestinationUpdate:Qe.filterSensitiveLog(e.RedshiftDestinationUpdate)})},(nt||(nt={})).filterSensitiveLog=function(e){return a({},e)};var it,ot=n(2),st=function(e,t){return u(void 0,void 0,void 0,(function(){var n,r,i,o,s,u,f,l,d,h,p,v;return c(this,(function(c){switch(c.label){case 0:return r=[a({},e)],v={},[4,Et(e.body,t)];case 1:switch(n=a.apply(void 0,r.concat([(v.body=c.sent(),v)])),o="UnknownError",s=n.body.__type.split("#"),o=void 0===s[1]?s[0]:s[1],o){case"InvalidArgumentException":case"com.amazonaws.firehose#InvalidArgumentException":return[3,2];case"InvalidKMSResourceException":case"com.amazonaws.firehose#InvalidKMSResourceException":return[3,4];case"ResourceNotFoundException":case"com.amazonaws.firehose#ResourceNotFoundException":return[3,6];case"ServiceUnavailableException":case"com.amazonaws.firehose#ServiceUnavailableException":return[3,8]}return[3,10];case 2:return u=[{}],[4,at(n,t)];case 3:return i=a.apply(void 0,[a.apply(void 0,u.concat([c.sent()])),{name:o,$metadata:wt(e)}]),[3,11];case 4:return f=[{}],[4,ut(n,t)];case 5:return i=a.apply(void 0,[a.apply(void 0,f.concat([c.sent()])),{name:o,$metadata:wt(e)}]),[3,11];case 6:return l=[{}],[4,ct(n,t)];case 7:return i=a.apply(void 0,[a.apply(void 0,l.concat([c.sent()])),{name:o,$metadata:wt(e)}]),[3,11];case 8:return d=[{}],[4,ft(n,t)];case 9:return i=a.apply(void 0,[a.apply(void 0,d.concat([c.sent()])),{name:o,$metadata:wt(e)}]),[3,11];case 10:h=n.body,o=h.code||h.Code||o,i=a(a({},h),{name:""+o,message:h.message||h.Message||o,$fault:"client",$metadata:wt(e)}),c.label=11;case 11:return p=i.message||i.Message||o,i.message=p,delete i.Message,[2,Promise.reject(Object.assign(new Error(p),i))]}}))}))},at=function(e,t){return u(void 0,void 0,void 0,(function(){var n,r;return c(this,(function(i){return n=e.body,r=pt(n,t),[2,a({name:"InvalidArgumentException",$fault:"client",$metadata:wt(e)},r)]}))}))},ut=function(e,t){return u(void 0,void 0,void 0,(function(){var n,r;return c(this,(function(i){return n=e.body,r=vt(n,t),[2,a({name:"InvalidKMSResourceException",$fault:"client",$metadata:wt(e)},r)]}))}))},ct=function(e,t){return u(void 0,void 0,void 0,(function(){var n,r;return c(this,(function(i){return n=e.body,r=bt(n,t),[2,a({name:"ResourceNotFoundException",$fault:"client",$metadata:wt(e)},r)]}))}))},ft=function(e,t){return u(void 0,void 0,void 0,(function(){var n,r;return c(this,(function(i){return n=e.body,r=yt(n,t),[2,a({name:"ServiceUnavailableException",$fault:"server",$metadata:wt(e)},r)]}))}))},lt=function(e,t){return a(a({},void 0!==e.DeliveryStreamName&&{DeliveryStreamName:e.DeliveryStreamName}),void 0!==e.Records&&{Records:dt(e.Records,t)})},dt=function(e,t){return e.map((function(e){return ht(e,t)}))},ht=function(e,t){return a({},void 0!==e.Data&&{Data:t.base64Encoder(e.Data)})},pt=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},vt=function(e,t){return{code:void 0!==e.code&&null!==e.code?e.code:void 0,message:void 0!==e.message&&null!==e.message?e.message:void 0}},gt=function(e,t){return{Encrypted:void 0!==e.Encrypted&&null!==e.Encrypted?e.Encrypted:void 0,FailedPutCount:void 0!==e.FailedPutCount&&null!==e.FailedPutCount?e.FailedPutCount:void 0,RequestResponses:void 0!==e.RequestResponses&&null!==e.RequestResponses?mt(e.RequestResponses,t):void 0}},mt=function(e,t){return(e||[]).map((function(e){return function(e,t){return{ErrorCode:void 0!==e.ErrorCode&&null!==e.ErrorCode?e.ErrorCode:void 0,ErrorMessage:void 0!==e.ErrorMessage&&null!==e.ErrorMessage?e.ErrorMessage:void 0,RecordId:void 0!==e.RecordId&&null!==e.RecordId?e.RecordId:void 0}}(e)}))},bt=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},yt=function(e,t){return{message:void 0!==e.message&&null!==e.message?e.message:void 0}},wt=function(e){return{httpStatusCode:e.statusCode,httpHeaders:e.headers,requestId:e.headers["x-amzn-requestid"]}},_t=function(e,t){return function(e,t){return void 0===e&&(e=new Uint8Array),e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array)}(e,t).then((function(e){return t.utf8Encoder(e)}))},St=function(e,t,n,r,i){return u(void 0,void 0,void 0,(function(){var o,s,a,u,f,l;return c(this,(function(c){switch(c.label){case 0:return[4,e.endpoint()];case 1:return o=c.sent(),s=o.hostname,a=o.protocol,u=void 0===a?"https":a,f=o.port,l={protocol:u,hostname:s,port:f,method:"POST",path:n,headers:t},void 0!==r&&(l.hostname=r),void 0!==i&&(l.body=i),[2,new ot.a(l)]}}))}))},Et=function(e,t){return _t(e,t).then((function(e){return e.length?JSON.parse(e):{}}))},Mt=n(10),At=function(e){function t(t){var n=e.call(this)||this;return n.input=t,n}return s(t,e),t.prototype.resolveMiddleware=function(e,t,n){this.middlewareStack.use(Object(Mt.a)(t,this.serialize,this.deserialize));var r=e.concat(this.middlewareStack),i=t.logger,o={logger:i,clientName:"FirehoseClient",commandName:"PutRecordBatchCommand",inputFilterSensitiveLog:ze.filterSensitiveLog,outputFilterSensitiveLog:Ke.filterSensitiveLog};"function"==typeof i.info&&i.info({clientName:"FirehoseClient",commandName:"PutRecordBatchCommand"});var s=t.requestHandler;return r.resolve((function(e){return s.handle(e.request,n||{})}),o)},t.prototype.serialize=function(e,t){return function(e,t){return u(void 0,void 0,void 0,(function(){var n,r;return c(this,(function(i){return n={"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"Firehose_20150804.PutRecordBatch"},r=JSON.stringify(lt(e,t)),[2,St(t,n,"/",void 0,r)]}))}))}(e,t)},t.prototype.deserialize=function(e,t){return function(e,t){return u(void 0,void 0,void 0,(function(){var n,r,i;return c(this,(function(o){switch(o.label){case 0:return e.statusCode>=300?[2,st(e,t)]:[4,Et(e.body,t)];case 1:return n=o.sent(),{},r=gt(n,t),i=a({$metadata:wt(e)},r),[2,Promise.resolve(i)]}}))}))}(e,t)},t}(rt.b),It=n(151),kt=n(38),Ot=n(18),xt=n(24),Ct=n(11),Tt=n(39),Pt=n(17),Nt=n(40),Rt=n(41),Lt=n(15),jt=new Set(["ap-east-1","ap-northeast-1","ap-northeast-2","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-north-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-west-1","us-west-2"]),Dt=new Set(["cn-north-1","cn-northwest-1"]),Ut=new Set(["us-iso-east-1"]),Bt=new Set(["us-isob-east-1"]),Ft=new Set(["us-gov-east-1","us-gov-west-1"]),zt=a(a({},{apiVersion:"2015-08-04",disableHostPrefix:!1,logger:{},regionInfoProvider:function(e,t){var n=void 0;switch(e){case"ap-east-1":n={hostname:"firehose.ap-east-1.amazonaws.com",partition:"aws"};break;case"ap-northeast-1":n={hostname:"firehose.ap-northeast-1.amazonaws.com",partition:"aws"};break;case"ap-northeast-2":n={hostname:"firehose.ap-northeast-2.amazonaws.com",partition:"aws"};break;case"ap-south-1":n={hostname:"firehose.ap-south-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-1":n={hostname:"firehose.ap-southeast-1.amazonaws.com",partition:"aws"};break;case"ap-southeast-2":n={hostname:"firehose.ap-southeast-2.amazonaws.com",partition:"aws"};break;case"ca-central-1":n={hostname:"firehose.ca-central-1.amazonaws.com",partition:"aws"};break;case"cn-north-1":n={hostname:"firehose.cn-north-1.amazonaws.com.cn",partition:"aws-cn"};break;case"cn-northwest-1":n={hostname:"firehose.cn-northwest-1.amazonaws.com.cn",partition:"aws-cn"};break;case"eu-central-1":n={hostname:"firehose.eu-central-1.amazonaws.com",partition:"aws"};break;case"eu-north-1":n={hostname:"firehose.eu-north-1.amazonaws.com",partition:"aws"};break;case"eu-west-1":n={hostname:"firehose.eu-west-1.amazonaws.com",partition:"aws"};break;case"eu-west-2":n={hostname:"firehose.eu-west-2.amazonaws.com",partition:"aws"};break;case"eu-west-3":n={hostname:"firehose.eu-west-3.amazonaws.com",partition:"aws"};break;case"me-south-1":n={hostname:"firehose.me-south-1.amazonaws.com",partition:"aws"};break;case"sa-east-1":n={hostname:"firehose.sa-east-1.amazonaws.com",partition:"aws"};break;case"us-east-1":n={hostname:"firehose.us-east-1.amazonaws.com",partition:"aws"};break;case"us-east-2":n={hostname:"firehose.us-east-2.amazonaws.com",partition:"aws"};break;case"us-gov-east-1":n={hostname:"firehose.us-gov-east-1.amazonaws.com",partition:"aws-us-gov"};break;case"us-gov-west-1":n={hostname:"firehose.us-gov-west-1.amazonaws.com",partition:"aws-us-gov"};break;case"us-west-1":n={hostname:"firehose.us-west-1.amazonaws.com",partition:"aws"};break;case"us-west-2":n={hostname:"firehose.us-west-2.amazonaws.com",partition:"aws"};break;default:jt.has(e)&&(n={hostname:"firehose.{region}.amazonaws.com".replace("{region}",e),partition:"aws"}),Dt.has(e)&&(n={hostname:"firehose.{region}.amazonaws.com.cn".replace("{region}",e),partition:"aws-cn"}),Ut.has(e)&&(n={hostname:"firehose.{region}.c2s.ic.gov".replace("{region}",e),partition:"aws-iso"}),Bt.has(e)&&(n={hostname:"firehose.{region}.sc2s.sgov.gov".replace("{region}",e),partition:"aws-iso-b"}),Ft.has(e)&&(n={hostname:"firehose.{region}.amazonaws.com".replace("{region}",e),partition:"aws-us-gov"}),void 0===n&&(n={hostname:"firehose.{region}.amazonaws.com".replace("{region}",e),partition:"aws"})}return Promise.resolve(n)},signingName:"firehose"}),{runtime:"browser",base64Decoder:Pt.a,base64Encoder:Pt.b,bodyLengthChecker:Nt.a,credentialDefaultProvider:Object(xt.a)("Credential is missing"),defaultUserAgent:Object(Rt.a)(It.name,It.version),maxAttempts:Ct.a,region:Object(xt.a)("Region is missing"),requestHandler:new Ot.a,sha256:kt.Sha256,streamCollector:Ot.b,urlParser:Tt.a,utf8Decoder:Lt.a,utf8Encoder:Lt.b}),qt=n(22),Kt=n(37),Ht=n(21),Vt=n(43),Gt=n(25),Wt=n(23),$t=function(e){function t(t){var n=this,r=a(a({},zt),t),i=Object(qt.b)(r),o=Object(qt.a)(i),s=Object(Gt.b)(o),u=Object(Ct.c)(s),c=Object(Wt.b)(u),f=Object(Ht.b)(c);return(n=e.call(this,f)||this).config=f,n.middlewareStack.use(Object(Gt.a)(n.config)),n.middlewareStack.use(Object(Ct.b)(n.config)),n.middlewareStack.use(Object(Wt.a)(n.config)),n.middlewareStack.use(Object(Kt.a)(n.config)),n.middlewareStack.use(Object(Ht.a)(n.config)),n.middlewareStack.use(Object(Vt.a)(n.config)),n}return s(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this)},t}(rt.a),Yt=(it=function(e,t){return(it=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}it(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),Jt=new r.a("AWSKineisFirehoseProvider"),Zt=function(e){function t(t){return e.call(this,t)||this}return Yt(t,e),t.prototype.getProviderName=function(){return"AWSKinesisFirehose"},t.prototype._sendEvents=function(e){var t=this;if(0!==e.length){var n=e[0],r=n.config,i=n.credentials;if(!this._init(r,i))return!1;var o={};e.map((function(e){var t=e.event,n=t.streamName,r=t.data;void 0===o[n]&&(o[n]=[]);var i=r&&"string"!=typeof r?JSON.stringify(r):r,s={Data:Object(Lt.a)(i)};o[n].push(s)})),Object.keys(o).map((function(e){Jt.debug("putting records to kinesis",e,"with records",o[e]),t._kinesisFirehose.send(new At({Records:o[e],DeliveryStreamName:e})).then((function(t){return Jt.debug("Upload records to stream",e)})).catch((function(e){return Jt.debug("Failed to upload records to Kinesis",e)}))}))}},t.prototype._init=function(e,t){if(Jt.debug("init clients"),this._kinesisFirehose&&this._config.credentials&&this._config.credentials.sessionToken===t.sessionToken&&this._config.credentials.identityId===t.identityId)return Jt.debug("no change for analytics config, directly return from init"),!0;this._config.credentials=t;var n=e.region;return this._initFirehose(n,t)},t.prototype._initFirehose=function(e,t){return Jt.debug("initialize kinesis firehose with credentials",t),this._kinesisFirehose=new $t({apiVersion:"2015-08-04",region:e,credentials:t}),!0},t}(i.a)},function(e,t,n){"use strict";n.d(t,"a",(function(){return T}));var r,i=n(44),o=n(19),s=n(5),a=n(89),u=n(104),c=function(){return(c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},f=new i.a("AbstractXRProvider"),l=function(){function e(e){void 0===e&&(e={}),this._config=e}return e.prototype.configure=function(e){return void 0===e&&(e={}),this._config=c(c({},e),this._config),f.debug("configure "+this.getProviderName(),this._config),this.options},e.prototype.getCategory=function(){return"XR"},Object.defineProperty(e.prototype,"options",{get:function(){return c({},this._config)},enumerable:!0,configurable:!0}),e}(),d=(r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),h=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return d(t,e),t}(Error),p=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return d(t,e),t}(h),v=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return d(t,e),t}(h),g=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return d(t,e),t}(h),m=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return d(t,e),t}(h),b=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return d(t,e),t}(h),y=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return d(t,e),t}(h),w=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),_=function(){return(_=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},S=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},E=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},M=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},A=new i.a("SumerianProvider"),I=function(e){function t(t){return void 0===t&&(t={}),e.call(this,t)||this}return w(t,e),t.prototype.getProviderName=function(){return"SumerianProvider"},t.prototype.loadScript=function(e){return S(this,void 0,void 0,(function(){return E(this,(function(t){return[2,new Promise((function(t,n){var r=document.createElement("script");r.src=e,r.addEventListener("load",(function(e){t()})),r.addEventListener("error",(function(t){n(new Error("Failed to load script: "+e))})),document.head.appendChild(r)}))]}))}))},t.prototype.loadScene=function(e,t,n){return S(this,void 0,void 0,(function(){var r,i,o,c,f,l,d,h,p,v,g,y,w,S,I,k,O,x,C,T,P,N,R,L,j,D,U;return E(this,(function(E){switch(E.label){case 0:if(!e)throw l="No scene name passed into loadScene",A.error(l),new b(l);if(!t)throw l="No dom element id passed into loadScene",A.error(l),new m(l);if(!(r=document.getElementById(t)))throw l="DOM element id, "+t+" not found",A.error(l),new m(l);if(!(i=this.getScene(e)).sceneConfig)throw l="No scene config configured for scene: "+e,A.error(l),new b(l);if(o=i.sceneConfig.url,c=i.sceneConfig.sceneId,i.sceneConfig.hasOwnProperty("region"))f=i.sceneConfig.region;else{if(!this.options.hasOwnProperty("region"))throw l="No region configured for scene: "+e,A.error(l),new b(l);f=this.options.region}d={region:f,customUserAgent:s.a.userAgent+"-SumerianScene"},h={headers:{"X-Amz-User-Agent":s.a.userAgent}},p=o,E.label=1;case 1:return E.trys.push([1,3,,4]),[4,a.a.get()];case 2:return v=E.sent(),d.credentials=v,g={secret_key:v.secretAccessKey,access_key:v.accessKeyId,session_token:v.sessionToken},y={region:f,service:"sumerian"},w=u.a.sign({method:"GET",url:o},g,y),h.headers=_(_({},h.headers),w.headers),p=w.url,[3,4];case 3:return E.sent(),A.debug("No credentials available, the request will be unsigned"),[3,4];case 4:return[4,fetch(p,h)];case 5:return[4,(S=E.sent()).json()];case 6:if(I=E.sent(),403===S.status)throw I.message?(A.error("Failure to authenticate user: "+I.message),new b("Failure to authenticate user: "+I.message)):(A.error("Failure to authenticate user"),new b("Failure to authenticate user"));return k=I.bundleData[c],[4,fetch(k.url,{headers:k.headers})];case 7:return[4,E.sent().json()];case 8:O=E.sent(),E.label=9;case 9:return E.trys.push([9,11,,12]),[4,this.loadScript(O[c].bootstrapperUrl)];case 10:return E.sent(),[3,12];case 11:throw x=E.sent(),A.error(x),new b(x);case 12:return C=n.progressCallback?n.progressCallback:void 0,T=i.publishParamOverrides?i.publishParamOverrides:void 0,P={element:r,sceneId:c,sceneBundle:O,apiResponse:I,progressCallback:C,publishParamOverrides:T,awsSDKConfigOverride:d},[4,window.SumerianBootstrapper.loadScene(P)];case 13:N=E.sent(),i.sceneController=N,i.isLoaded=!0;try{for(R=M(N.sceneLoadWarnings),L=R.next();!L.done;L=R.next())j=L.value,A.warn("loadScene warning: "+j)}catch(e){D={error:e}}finally{try{L&&!L.done&&(U=R.return)&&U.call(R)}finally{if(D)throw D.error}}return[2]}}))}))},t.prototype.isSceneLoaded=function(e){return this.getScene(e).isLoaded||!1},t.prototype.getScene=function(e){if(!this.options.scenes){var t="No scenes were defined in the configuration";throw A.error(t),new p(t)}if(!e){t="No scene name was passed";throw A.error(t),new v(t)}if(!this.options.scenes[e]){t="Scene '"+e+"' is not configured";throw A.error(t),new v(t)}return this.options.scenes[e]},t.prototype.getSceneController=function(e){if(!this.options.scenes){var t="No scenes were defined in the configuration";throw A.error(t),new p(t)}var n=this.options.scenes[e];if(!n){t="Scene '"+e+"' is not configured";throw A.error(t),new v(t)}var r=n.sceneController;if(!r){t="Scene controller for '"+e+"' has not been loaded";throw A.error(t),new g(t)}return r},t.prototype.isVRCapable=function(e){return this.getSceneController(e).vrCapable},t.prototype.isVRPresentationActive=function(e){return this.getSceneController(e).vrPresentationActive},t.prototype.start=function(e){this.getSceneController(e).start()},t.prototype.enterVR=function(e){this.getSceneController(e).enterVR()},t.prototype.exitVR=function(e){this.getSceneController(e).exitVR()},t.prototype.isMuted=function(e){return this.getSceneController(e).muted},t.prototype.setMuted=function(e,t){this.getSceneController(e).muted=t},t.prototype.onSceneEvent=function(e,t,n){this.getSceneController(e).on(t,n)},t.prototype.enableAudio=function(e){this.getSceneController(e).enableAudio()},t}(l),k=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},O=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},x=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},C=new i.a("XR"),T=new(function(){function e(e){this._options=e,C.debug("XR Options",this._options),this._defaultProvider="SumerianProvider",this._pluggables={},this.addPluggable(new I)}return e.prototype.configure=function(e){var t=this,n=e?e.XR||e:{};return C.debug("configure XR",{opt:n}),this._options=Object.assign({},this._options,n),Object.entries(this._pluggables).map((function(e){var r=x(e,2),i=r[0],o=r[1];i!==t._defaultProvider||n[t._defaultProvider]?o.configure(t._options[i]):o.configure(t._options)})),this._options},e.prototype.addPluggable=function(e){return k(this,void 0,void 0,(function(){return O(this,(function(t){return e&&"XR"===e.getCategory()?(this._pluggables[e.getProviderName()]=e,[2,e.configure(this._options)]):[2]}))}))},e.prototype.loadScene=function(e,t,n,r){return void 0===n&&(n={}),void 0===r&&(r=this._defaultProvider),k(this,void 0,void 0,(function(){return O(this,(function(i){switch(i.label){case 0:if(!this._pluggables[r])throw new y("Provider '"+r+"' not configured");return[4,this._pluggables[r].loadScene(e,t,n)];case 1:return[2,i.sent()]}}))}))},e.prototype.isSceneLoaded=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].isSceneLoaded(e)},e.prototype.getSceneController=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].getSceneController(e)},e.prototype.isVRCapable=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].isVRCapable(e)},e.prototype.isVRPresentationActive=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].isVRPresentationActive(e)},e.prototype.start=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].start(e)},e.prototype.enterVR=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].enterVR(e)},e.prototype.exitVR=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].exitVR(e)},e.prototype.isMuted=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].isMuted(e)},e.prototype.setMuted=function(e,t,n){if(void 0===n&&(n=this._defaultProvider),!this._pluggables[n])throw new y("Provider '"+n+"' not configured");return this._pluggables[n].setMuted(e,t)},e.prototype.onSceneEvent=function(e,t,n,r){if(void 0===r&&(r=this._defaultProvider),!this._pluggables[r])throw new y("Provider '"+r+"' not configured");return this._pluggables[r].onSceneEvent(e,t,n)},e.prototype.enableAudio=function(e,t){if(void 0===t&&(t=this._defaultProvider),!this._pluggables[t])throw new y("Provider '"+t+"' not configured");return this._pluggables[t].enableAudio(e)},e}())(null);o.a.register(T)},function(e,t,n){"use strict";n.r(t),n.d(t,"fromUtf8",(function(){return r})),n.d(t,"toUtf8",(function(){return i}));var r=function(e){return"function"==typeof TextEncoder?function(e){return(new TextEncoder).encode(e)}(e):function(e){for(var t=[],n=0,r=e.length;n<r;n++){var i=e.charCodeAt(n);if(i<128)t.push(i);else if(i<2048)t.push(i>>6|192,63&i|128);else if(n+1<e.length&&55296==(64512&i)&&56320==(64512&e.charCodeAt(n+1))){var o=65536+((1023&i)<<10)+(1023&e.charCodeAt(++n));t.push(o>>18|240,o>>12&63|128,o>>6&63|128,63&o|128)}else t.push(i>>12|224,i>>6&63|128,63&i|128)}return Uint8Array.from(t)}(e)},i=function(e){return"function"==typeof TextDecoder?function(e){return new TextDecoder("utf-8").decode(e)}(e):function(e){for(var t="",n=0,r=e.length;n<r;n++){var i=e[n];if(i<128)t+=String.fromCharCode(i);else if(192<=i&&i<224){var o=e[++n];t+=String.fromCharCode((31&i)<<6|63&o)}else if(240<=i&&i<365){var s="%"+[i,e[++n],e[++n],e[++n]].map((function(e){return e.toString(16)})).join("%");t+=decodeURIComponent(s)}else t+=String.fromCharCode((15&i)<<12|(63&e[++n])<<6|63&e[++n])}return t}(e)}},function(e,t,n){"use strict";n.r(t);var r=n(44);let i,o;const s=new WeakMap,a=new WeakMap,u=new WeakMap,c=new WeakMap,f=new WeakMap;let l={get(e,t,n){if(e instanceof IDBTransaction){if("done"===t)return a.get(e);if("objectStoreNames"===t)return e.objectStoreNames||u.get(e);if("store"===t)return n.objectStoreNames[1]?void 0:n.objectStore(n.objectStoreNames[0])}return p(e[t])},set:(e,t,n)=>(e[t]=n,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function d(e){return e!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(o||(o=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(e)?function(...t){return e.apply(v(this),t),p(s.get(this))}:function(...t){return p(e.apply(v(this),t))}:function(t,...n){const r=e.call(v(this),t,...n);return u.set(r,t.sort?t.sort():[t]),p(r)}}function h(e){return"function"==typeof e?d(e):(e instanceof IDBTransaction&&function(e){if(a.has(e))return;const t=new Promise((t,n)=>{const r=()=>{e.removeEventListener("complete",i),e.removeEventListener("error",o),e.removeEventListener("abort",o)},i=()=>{t(),r()},o=()=>{n(e.error||new DOMException("AbortError","AbortError")),r()};e.addEventListener("complete",i),e.addEventListener("error",o),e.addEventListener("abort",o)});a.set(e,t)}(e),t=e,(i||(i=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])).some(e=>t instanceof e)?new Proxy(e,l):e);var t}function p(e){if(e instanceof IDBRequest)return function(e){const t=new Promise((t,n)=>{const r=()=>{e.removeEventListener("success",i),e.removeEventListener("error",o)},i=()=>{t(p(e.result)),r()},o=()=>{n(e.error),r()};e.addEventListener("success",i),e.addEventListener("error",o)});return t.then(t=>{t instanceof IDBCursor&&s.set(t,e)}).catch(()=>{}),f.set(t,e),t}(e);if(c.has(e))return c.get(e);const t=h(e);return t!==e&&(c.set(e,t),f.set(t,e)),t}const v=e=>f.get(e);function g(e,t,{blocked:n,upgrade:r,blocking:i,terminated:o}={}){const s=indexedDB.open(e,t),a=p(s);return r&&s.addEventListener("upgradeneeded",e=>{r(p(s.result),e.oldVersion,e.newVersion,p(s.transaction))}),n&&s.addEventListener("blocked",()=>n()),a.then(e=>{o&&e.addEventListener("close",()=>o()),i&&e.addEventListener("versionchange",()=>i())}).catch(()=>{}),a}function m(e,{blocked:t}={}){const n=indexedDB.deleteDatabase(e);return t&&n.addEventListener("blocked",()=>t()),p(n).then(()=>{})}const b=["get","getKey","getAll","getAllKeys","count"],y=["put","add","delete","clear"],w=new Map;function _(e,t){if(!(e instanceof IDBDatabase)||t in e||"string"!=typeof t)return;if(w.get(t))return w.get(t);const n=t.replace(/FromIndex$/,""),r=t!==n,i=y.includes(n);if(!(n in(r?IDBIndex:IDBObjectStore).prototype)||!i&&!b.includes(n))return;const o=async function(e,...t){const o=this.transaction(e,i?"readwrite":"readonly");let s=o.store;r&&(s=s.index(t.shift()));const a=await s[n](...t);return i&&await o.done,a};return w.set(t,o),o}l=(e=>({...e,get:(t,n,r)=>_(t,n)||e.get(t,n,r),has:(t,n)=>!!_(t,n)||e.has(t,n)}))(l);var S=n(9),E=n(245),M=n(4),A=n(3);function I(e){return(I="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)}var k=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},O=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},x=function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e="function"==typeof C?C(e):e[Symbol.iterator](),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,i){(function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)})(r,i,(t=e[n](t)).done,t.value)}))}}},C=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},T=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},P=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(T(arguments[t]));return e},N=new r.a("DataStore"),R=function(){function e(){this.dbName="amplify-datastore"}return e.prototype.checkPrivate=function(){return k(this,void 0,void 0,(function(){return O(this,(function(e){switch(e.label){case 0:return[4,Object(A.u)().then((function(e){return e}))];case 1:return e.sent()?(N.error("IndexedDB not supported in this browser's private mode"),[2,Promise.reject("IndexedDB not supported in this browser's private mode")]):[2,Promise.resolve()]}}))}))},e.prototype.getStorenameForModel=function(e){var t=this.namespaceResolver(e),n=e.name;return this.getStorename(t,n)},e.prototype.getStorename=function(e,t){return e+"_"+t},e.prototype.setUp=function(e,t,n,r,i){return k(this,void 0,void 0,(function(){var o,s,a=this;return O(this,(function(u){switch(u.label){case 0:return[4,this.checkPrivate()];case 1:return u.sent(),this.initPromise?[3,2]:(this.initPromise=new Promise((function(e,t){a.resolve=e,a.reject=t})),[3,4]);case 2:return[4,this.initPromise];case 3:u.sent(),u.label=4;case 4:i&&(this.dbName="amplify-datastore-"+i),this.schema=e,this.namespaceResolver=t,this.modelInstanceCreator=n,this.getModelConstructorByModelName=r,u.label=5;case 5:return u.trys.push([5,8,,9]),this.db?[3,7]:(2,o=this,[4,g(this.dbName,2,{upgrade:function(t,n,r,i){return k(a,void 0,void 0,(function(){var o,s,a,u,c,f,l,d,h,p,v,g,m=this;return O(this,(function(b){switch(b.label){case 0:if(0===n)return Object.keys(e.namespaces).forEach((function(n){var r=e.namespaces[n];Object.keys(r.models).forEach((function(e){var r=m.getStorename(n,e),i=t.createObjectStore(r,{autoIncrement:!0});m.schema.namespaces[n].relationships[e].indexes.forEach((function(e){return i.createIndex(e,e)})),i.createIndex("byId","id",{unique:!0})}))})),[2];if(1!==n||2!==r)return[3,16];b.label=1;case 1:b.trys.push([1,14,,15]),b.label=2;case 2:b.trys.push([2,11,12,13]),o=C(i.objectStoreNames),s=o.next(),b.label=3;case 3:return s.done?[3,10]:(a=s.value,u=i.objectStore(a),c="tmp_"+a,u.name=c,(f=t.createObjectStore(a,{keyPath:void 0,autoIncrement:!0})).createIndex("byId","id",{unique:!0}),[4,u.openCursor()]);case 4:l=b.sent(),d=0,b.label=5;case 5:return l&&l.value?[4,f.put(l.value)]:[3,8];case 6:return b.sent(),[4,l.continue()];case 7:return l=b.sent(),d++,[3,5];case 8:t.deleteObjectStore(c),N.debug(d+" "+a+" records migrated"),b.label=9;case 9:return s=o.next(),[3,3];case 10:return[3,13];case 11:return h=b.sent(),v={error:h},[3,13];case 12:try{s&&!s.done&&(g=o.return)&&g.call(o)}finally{if(v)throw v.error}return[7];case 13:return[3,15];case 14:throw p=b.sent(),N.error("Error migrating IndexedDB data",p),i.abort(),p;case 15:case 16:return[2]}}))}))}})]);case 6:o.db=u.sent(),this.resolve(),u.label=7;case 7:return[3,9];case 8:return s=u.sent(),this.reject(s),[3,9];case 9:return[2]}}))}))},e.prototype._get=function(e,t){return k(this,void 0,void 0,(function(){var n,r;return O(this,(function(i){switch(i.label){case 0:return"string"==typeof e?(r=e,n=this.db.transaction(r,"readonly").store.index("byId")):n=e.index("byId"),[4,n.get(t)];case 1:return[2,i.sent()]}}))}))},e.prototype.save=function(e,t){var n,r;return k(this,void 0,void 0,(function(){var i,o,s,a,u,c,f,l,d,h,p,v,g,m,b,y,w,_,E,I,k,C,T,R,L,j=this;return O(this,(function(O){switch(O.label){case 0:return[4,this.checkPrivate()];case 1:return O.sent(),i=Object.getPrototypeOf(e).constructor,o=this.getStorenameForModel(i),s=Object(A.x)(i.name,e,this.schema.namespaces[this.namespaceResolver(i)],this.modelInstanceCreator,this.getModelConstructorByModelName),a=this.namespaceResolver(i),u=new Set,c=Object.values(s).map((function(e){var t=e.modelName,n=e.item,r=e.instance,i=j.getStorename(a,t);return u.add(i),{storeName:i,item:n,instance:r}})),f=this.db.transaction(P([o],Array.from(u.values())),"readwrite"),l=f.objectStore(o),[4,this._get(l,e.id)];case 2:if(d=O.sent(),t&&d&&(h=S.a.getPredicates(t),p=h.predicates,v=h.type,!Object(A.y)(d,v,p)))throw g="Conditional update failed",N.error(g,{model:d,condition:p}),new Error(g);m=[],O.label=3;case 3:O.trys.push([3,14,15,20]),b=x(c),O.label=4;case 4:return[4,b.next()];case 5:return(y=O.sent()).done?[3,13]:(w=y.value,_=w.storeName,E=w.item,I=w.instance,k=f.objectStore(_),C=E.id,[4,this._get(k,C)]);case 6:return T=void 0===O.sent()?M.c.INSERT:M.c.UPDATE,C!==e.id?[3,9]:[4,k.index("byId").getKey(E.id)];case 7:return R=O.sent(),[4,k.put(E,R)];case 8:return O.sent(),m.push([I,T]),[3,12];case 9:return T!==M.c.INSERT?[3,12]:[4,k.index("byId").getKey(E.id)];case 10:return R=O.sent(),[4,k.put(E,R)];case 11:O.sent(),m.push([I,T]),O.label=12;case 12:return[3,4];case 13:return[3,20];case 14:return L=O.sent(),n={error:L},[3,20];case 15:return O.trys.push([15,,18,19]),y&&!y.done&&(r=b.return)?[4,r.call(b)]:[3,17];case 16:O.sent(),O.label=17;case 17:return[3,19];case 18:if(n)throw n.error;return[7];case 19:return[7];case 20:return[4,f.done];case 21:return O.sent(),[2,m]}}))}))},e.prototype.load=function(e,t,n){var r,i,o,s,a,u,c,f,l,d;return k(this,void 0,void 0,(function(){var h,p,v,g,m,b,y,w,_,S,E,M,I,k,C,T,N,R,L,j=this;return O(this,(function(O){switch(O.label){case 0:if(h=this.schema.namespaces[e],p=h.relationships[t].relationTypes,v=p.map((function(t){var n=t.modelName;return j.getStorename(e,n)})),g=this.getModelConstructorByModelName(e,t),0===v.length)return[2,n.map((function(e){return j.modelInstanceCreator(g,e)}))];m=this.db.transaction(P(v),"readonly"),O.label=1;case 1:O.trys.push([1,34,35,40]),b=x(p),O.label=2;case 2:return[4,b.next()];case 3:if((y=O.sent()).done)return[3,33];switch(w=y.value,_=w.fieldName,S=w.modelName,E=w.targetName,M=this.getStorename(e,S),I=m.objectStore(M),k=this.getModelConstructorByModelName(e,S),w.relationType){case"HAS_ONE":return[3,4];case"BELONGS_TO":return[3,17];case"HAS_MANY":return[3,30]}return[3,31];case 4:O.trys.push([4,10,11,16]),r=x(n),O.label=5;case 5:return[4,r.next()];case 6:return(i=O.sent()).done?[3,9]:(T=i.value)[_]?[4,this._get(I,T[_])]:[3,8];case 7:N=O.sent(),T[_]=N&&this.modelInstanceCreator(k,N),O.label=8;case 8:return[3,5];case 9:return[3,16];case 10:return C=O.sent(),c={error:C},[3,16];case 11:return O.trys.push([11,,14,15]),i&&!i.done&&(f=r.return)?[4,f.call(r)]:[3,13];case 12:O.sent(),O.label=13;case 13:return[3,15];case 14:if(c)throw c.error;return[7];case 15:return[7];case 16:return[3,32];case 17:O.trys.push([17,23,24,29]),o=x(n),O.label=18;case 18:return[4,o.next()];case 19:return(s=O.sent()).done?[3,22]:(T=s.value)[E]?[4,this._get(I,T[E])]:[3,21];case 20:N=O.sent(),T[_]=N&&this.modelInstanceCreator(k,N),delete T[E],O.label=21;case 21:return[3,18];case 22:return[3,29];case 23:return R=O.sent(),l={error:R},[3,29];case 24:return O.trys.push([24,,27,28]),s&&!s.done&&(d=o.return)?[4,d.call(o)]:[3,26];case 25:O.sent(),O.label=26;case 26:return[3,28];case 27:if(l)throw l.error;return[7];case 28:return[7];case 29:case 30:return[3,32];case 31:return Object(A.f)(w.relationType),[3,32];case 32:return[3,2];case 33:return[3,40];case 34:return L=O.sent(),a={error:L},[3,40];case 35:return O.trys.push([35,,38,39]),y&&!y.done&&(u=b.return)?[4,u.call(b)]:[3,37];case 36:O.sent(),O.label=37;case 37:return[3,39];case 38:if(a)throw a.error;return[7];case 39:return[7];case 40:return[2,n.map((function(e){return j.modelInstanceCreator(g,e)}))]}}))}))},e.prototype.query=function(e,t,n){return k(this,void 0,void 0,(function(){var r,i,o,s,a,u,c,f,l,d,h,p,v,g;return O(this,(function(m){switch(m.label){case 0:return[4,this.checkPrivate()];case 1:return m.sent(),r=this.getStorenameForModel(e),i=this.namespaceResolver(e),o=n&&n.sort,t?(s=S.a.getPredicates(t))?(a=s.predicates,u=s.type,(c=1===a.length&&a.find((function(e){return Object(M.k)(e)&&"id"===e.field&&"eq"===e.operator})))?(f=c.operand,[4,this._get(r,f)]):[3,5]):[3,8]:[3,8];case 2:return(l=m.sent())?[4,this.load(i,e.name,[l])]:[3,4];case 3:return d=T.apply(void 0,[m.sent(),1]),[2,[d[0]]];case 4:return[2,[]];case 5:return[4,this.db.getAll(r)];case 6:return p=m.sent(),h=a?p.filter((function(e){return Object(A.y)(e,u,a)})):p,[4,this.load(i,e.name,this.inMemoryPagination(h,n))];case 7:return[2,m.sent()];case 8:return o?[4,this.db.getAll(r)]:[3,11];case 9:return p=m.sent(),[4,this.load(i,e.name,this.inMemoryPagination(p,n))];case 10:return[2,m.sent()];case 11:return v=this.load,g=[i,e.name],[4,this.enginePagination(r,n)];case 12:return[4,v.apply(this,g.concat([m.sent()]))];case 13:return[2,m.sent()]}}))}))},e.prototype.inMemoryPagination=function(e,t){if(t){if(t.sort){var n=E.a.getPredicates(t.sort);if(n.length){var r=Object(A.w)(n);e.sort(r)}}var i=t.page,o=void 0===i?0:i,s=t.limit,a=void 0===s?0:s,u=Math.max(0,o*a)||0,c=a>0?u+a:e.length;return e.slice(u,c)}return e},e.prototype.enginePagination=function(e,t){return k(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d;return O(this,(function(h){switch(h.label){case 0:return t?(r=t.page,i=void 0===r?0:r,o=t.limit,s=void 0===o?0:o,a=Math.max(0,i*s)||0,[4,this.db.transaction(e).objectStore(e).openCursor()]):[3,7];case 1:return(u=h.sent())&&a>0?[4,u.advance(a)]:[3,3];case 2:h.sent(),h.label=3;case 3:c=[],f="number"==typeof s&&s>0,l=!0,d=s,h.label=4;case 4:return l&&u&&u.value?(c.push(u.value),[4,u.continue()]):[3,6];case 5:return u=h.sent(),f?(d--,l=d>0&&null!==u):l=null!==u,[3,4];case 6:return n=c,[3,9];case 7:return[4,this.db.getAll(e)];case 8:n=h.sent(),h.label=9;case 9:return[2,n]}}))}))},e.prototype.queryOne=function(e,t){return void 0===t&&(t=M.d.FIRST),k(this,void 0,void 0,(function(){var n,r,i;return O(this,(function(o){switch(o.label){case 0:return[4,this.checkPrivate()];case 1:return o.sent(),n=this.getStorenameForModel(e),[4,this.db.transaction([n],"readonly").objectStore(n).openCursor(void 0,t===M.d.FIRST?"next":"prev")];case 2:return r=o.sent(),[2,(i=r?r.value:void 0)&&this.modelInstanceCreator(e,i)]}}))}))},e.prototype.delete=function(e,t){return k(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d,h,p,v,g;return O(this,(function(m){switch(m.label){case 0:return[4,this.checkPrivate()];case 1:return m.sent(),n=[],Object(A.s)(e)?(o=e,s=this.namespaceResolver(o),a=this.getStorenameForModel(o),[4,this.query(o,t)]):[3,9];case 2:return r=m.sent(),v=this.schema.namespaces[s].relationships[o.name].relationTypes,void 0===t?[3,5]:[4,this.deleteTraverse(v,r,o.name,s,n)];case 3:return m.sent(),[4,this.deleteItem(n)];case 4:return m.sent(),g=n.reduce((function(e,t){var n=t.items;return e.concat(n)}),[]),[2,[r,g]];case 5:return[4,this.deleteTraverse(v,r,o.name,s,n)];case 6:return m.sent(),[4,this.db.transaction([a],"readwrite").objectStore(a).clear()];case 7:return m.sent(),g=n.reduce((function(e,t){var n=t.items;return e.concat(n)}),[]),[2,[r,g]];case 8:return[3,17];case 9:return i=e,o=Object.getPrototypeOf(i).constructor,s=this.namespaceResolver(o),a=this.getStorenameForModel(o),t?(u=this.db.transaction([a],"readwrite"),c=u.objectStore(a),[4,this._get(c,i.id)]):[3,13];case 10:if(void 0===(f=m.sent()))return p="Model instance not found in storage",N.warn(p,{model:i}),[2,[[i],[]]];if(l=S.a.getPredicates(t),d=l.predicates,h=l.type,!Object(A.y)(f,h,d))throw p="Conditional update failed",N.error(p,{model:f,condition:d}),new Error(p);return[4,u.done];case 11:return m.sent(),v=this.schema.namespaces[s].relationships[o.name].relationTypes,[4,this.deleteTraverse(v,[i],o.name,s,n)];case 12:return m.sent(),[3,15];case 13:return v=this.schema.namespaces[s].relationships[o.name].relationTypes,[4,this.deleteTraverse(v,[i],o.name,s,n)];case 14:m.sent(),m.label=15;case 15:return[4,this.deleteItem(n)];case 16:return m.sent(),g=n.reduce((function(e,t){var n=t.items;return e.concat(n)}),[]),[2,[[i],g]];case 17:return[2]}}))}))},e.prototype.deleteItem=function(e){var t,n,r,i,o,s;return k(this,void 0,void 0,(function(){var a,u,c,f,l,d,h,p,v,g,m,b;return O(this,(function(y){switch(y.label){case 0:a=e.map((function(e){return e.storeName})),u=this.db.transaction(P(a),"readwrite"),y.label=1;case 1:y.trys.push([1,22,23,28]),t=x(e),y.label=2;case 2:return[4,t.next()];case 3:if((n=y.sent()).done)return[3,21];c=n.value,f=c.storeName,l=c.items,d=u.objectStore(f),y.label=4;case 4:y.trys.push([4,14,15,20]),h=x(l),y.label=5;case 5:return[4,h.next()];case 6:return(p=y.sent()).done?[3,13]:(v=p.value)?(g=void 0,"object"!==I(v)?[3,8]:[4,d.index("byId").getKey(v.id)]):[3,12];case 7:return g=y.sent(),[3,10];case 8:return[4,d.index("byId").getKey(v.toString())];case 9:g=y.sent(),y.label=10;case 10:return void 0===g?[3,12]:[4,d.delete(g)];case 11:y.sent(),y.label=12;case 12:return[3,5];case 13:return[3,20];case 14:return m=y.sent(),o={error:m},[3,20];case 15:return y.trys.push([15,,18,19]),p&&!p.done&&(s=h.return)?[4,s.call(h)]:[3,17];case 16:y.sent(),y.label=17;case 17:return[3,19];case 18:if(o)throw o.error;return[7];case 19:return[7];case 20:return[3,2];case 21:return[3,28];case 22:return b=y.sent(),r={error:b},[3,28];case 23:return y.trys.push([23,,26,27]),n&&!n.done&&(i=t.return)?[4,i.call(t)]:[3,25];case 24:y.sent(),y.label=25;case 25:return[3,27];case 26:if(r)throw r.error;return[7];case 27:return[7];case 28:return[2]}}))}))},e.prototype.deleteTraverse=function(e,t,n,r,i){var o,s,a,u,c,f,l,d,h,p,v,g;return k(this,void 0,void 0,(function(){var m,b,y,w,_,S,E,M,I,k,C,T=this;return O(this,(function(O){switch(O.label){case 0:O.trys.push([0,35,36,41]),o=x(e),O.label=1;case 1:return[4,o.next()];case 2:if((s=O.sent()).done)return[3,34];switch(m=s.value,b=m.relationType,m.fieldName,y=m.modelName,w=this.getStorename(r,y),_=Object(A.g)(this.schema.namespaces[r].relationships[y].relationTypes,n)||Object(A.h)(this.schema.namespaces[r].relationships[y].indexes,m.associatedWith),b){case"HAS_ONE":return[3,3];case"HAS_MANY":return[3,17];case"BELONGS_TO":return[3,31]}return[3,32];case 3:O.trys.push([3,10,11,16]),a=x(t),O.label=4;case 4:return[4,a.next()];case 5:return(u=O.sent()).done?[3,9]:(M=u.value,[4,this.db.transaction(w,"readwrite").objectStore(w).index(_).get(M.id)]);case 6:return S=O.sent(),[4,this.deleteTraverse(this.schema.namespaces[r].relationships[y].relationTypes,S?[S]:[],y,r,i)];case 7:O.sent(),O.label=8;case 8:return[3,4];case 9:return[3,16];case 10:return E=O.sent(),h={error:E},[3,16];case 11:return O.trys.push([11,,14,15]),u&&!u.done&&(p=a.return)?[4,p.call(a)]:[3,13];case 12:O.sent(),O.label=13;case 13:return[3,15];case 14:if(h)throw h.error;return[7];case 15:return[7];case 16:return[3,33];case 17:O.trys.push([17,24,25,30]),c=x(t),O.label=18;case 18:return[4,c.next()];case 19:return(f=O.sent()).done?[3,23]:(M=f.value,[4,this.db.transaction(w,"readwrite").objectStore(w).index(_).getAll(M.id)]);case 20:return I=O.sent(),[4,this.deleteTraverse(this.schema.namespaces[r].relationships[y].relationTypes,I,y,r,i)];case 21:O.sent(),O.label=22;case 22:return[3,18];case 23:return[3,30];case 24:return k=O.sent(),v={error:k},[3,30];case 25:return O.trys.push([25,,28,29]),f&&!f.done&&(g=c.return)?[4,g.call(c)]:[3,27];case 26:O.sent(),O.label=27;case 27:return[3,29];case 28:if(v)throw v.error;return[7];case 29:return[7];case 30:case 31:return[3,33];case 32:return Object(A.f)(b),[3,33];case 33:return[3,1];case 34:return[3,41];case 35:return C=O.sent(),l={error:C},[3,41];case 36:return O.trys.push([36,,39,40]),s&&!s.done&&(d=o.return)?[4,d.call(o)]:[3,38];case 37:O.sent(),O.label=38;case 38:return[3,40];case 39:if(l)throw l.error;return[7];case 40:return[7];case 41:return i.push({storeName:this.getStorename(r,n),items:t.map((function(e){return T.modelInstanceCreator(T.getModelConstructorByModelName(r,n),e)}))}),[2]}}))}))},e.prototype.clear=function(){return k(this,void 0,void 0,(function(){return O(this,(function(e){switch(e.label){case 0:return[4,this.checkPrivate()];case 1:return e.sent(),this.db.close(),[4,m(this.dbName)];case 2:return e.sent(),this.db=void 0,this.initPromise=void 0,[2]}}))}))},e.prototype.batchSave=function(e,t){return k(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,l,d,h;return O(this,(function(p){switch(p.label){case 0:return 0===t.length?[2,[]]:[4,this.checkPrivate()];case 1:p.sent(),n=[],r=this.getStorenameForModel(e),i=this.db.transaction(r,"readwrite"),o=i.store,s=function(t){var r,i,s,u,c;return O(this,(function(f){switch(f.label){case 0:return r=Object(A.x)(e.name,a.modelInstanceCreator(e,t),a.schema.namespaces[a.namespaceResolver(e)],a.modelInstanceCreator,a.getModelConstructorByModelName),i=t.id,s=t._deleted,[4,o.index("byId").getKey(i)];case 1:return u=f.sent(),s?[3,3]:(c=r.find((function(e){return e.instance.id===i})).instance,n.push([c,u?M.c.UPDATE:M.c.INSERT]),[4,o.put(c,u)]);case 2:return f.sent(),[3,5];case 3:return n.push([t,M.c.DELETE]),u?[4,o.delete(u)]:[3,5];case 4:f.sent(),f.label=5;case 5:return[2]}}))},a=this,p.label=2;case 2:p.trys.push([2,7,8,9]),u=C(t),c=u.next(),p.label=3;case 3:return c.done?[3,6]:(f=c.value,[5,s(f)]);case 4:p.sent(),p.label=5;case 5:return c=u.next(),[3,3];case 6:return[3,9];case 7:return l=p.sent(),d={error:l},[3,9];case 8:try{c&&!c.done&&(h=u.return)&&h.call(u)}finally{if(d)throw d.error}return[7];case 9:return[4,i.done];case 10:return p.sent(),[2,n]}}))}))},e}();t.default=new R},function(e,t,n){"use strict";n.r(t),n.d(t,"AsyncStorageAdapter",(function(){return I}));var r=n(44),i=n(4),o=n(3),s=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},a=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},u=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},c=function(){var e=this;this.db=new Map,this.getAllKeys=function(){return s(e,void 0,void 0,(function(){return a(this,(function(e){return[2,Array.from(this.db.keys())]}))}))},this.multiGet=function(t){return s(e,void 0,void 0,(function(){var e=this;return a(this,(function(n){return[2,t.reduce((function(t,n){return t.push([n,e.db.get(n)]),t}),[])]}))}))},this.multiRemove=function(t,n){return s(e,void 0,void 0,(function(){var e=this;return a(this,(function(r){return t.forEach((function(t){return e.db.delete(t)})),n(),[2]}))}))},this.multiSet=function(t,n){return s(e,void 0,void 0,(function(){var e=this;return a(this,(function(r){return t.forEach((function(t){var n=u(t,2),r=n[0],i=n[1];e.setItem(r,i)})),n(),[2]}))}))},this.setItem=function(t,n){return s(e,void 0,void 0,(function(){return a(this,(function(e){return[2,this.db.set(t,n)]}))}))},this.removeItem=function(t){return s(e,void 0,void 0,(function(){return a(this,(function(e){return[2,this.db.delete(t)]}))}))},this.getItem=function(t){return s(e,void 0,void 0,(function(){return a(this,(function(e){return[2,this.db.get(t)]}))}))}};var f=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},l=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},d=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},h=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},p="@AmplifyDatastore",v=new Map,g=function(){function e(){this._collectionInMemoryIndex=new Map,this.storage=new c}return e.prototype.getCollectionIndex=function(e){return this._collectionInMemoryIndex.has(e)||this._collectionInMemoryIndex.set(e,new Map),this._collectionInMemoryIndex.get(e)},e.prototype.getMonotonicFactory=function(e){return v.has(e)||v.set(e,Object(o.v)()),v.get(e)},e.prototype.init=function(){return f(this,void 0,void 0,(function(){var e,t,n,r,i,o,s,a,u,c,f,v,g,m,b,y,w,_,S,E;return l(this,(function(l){switch(l.label){case 0:return this._collectionInMemoryIndex.clear(),[4,this.storage.getAllKeys()];case 1:e=l.sent(),t=[],l.label=2;case 2:l.trys.push([2,12,13,14]),n=d(e),r=n.next(),l.label=3;case 3:return r.done?[3,11]:(i=r.value,o=h(i.split("::"),5),s=o[0],a=o[1],u=o[2],c=o[3],f=o[4],s!==p?[3,10]:"Data"!==u?[3,9]:(v=void 0,void 0!==f?[3,7]:(g=c,m=this.getMonotonicFactory(a)(),b=this.getLegacyKeyForItem(a,g),y=this.getKeyForItem(a,g,m),[4,this.storage.getItem(b)])));case 4:return w=l.sent(),[4,this.storage.setItem(y,w)];case 5:return l.sent(),[4,this.storage.removeItem(b)];case 6:return l.sent(),v=m,[3,8];case 7:v=c,l.label=8;case 8:return this.getCollectionIndex(a).set(f,v),[3,10];case 9:"Collection"===u&&t.push(i),l.label=10;case 10:return r=n.next(),[3,3];case 11:return[3,14];case 12:return _=l.sent(),S={error:_},[3,14];case 13:try{r&&!r.done&&(E=n.return)&&E.call(n)}finally{if(S)throw S.error}return[7];case 14:return t.length>0?[4,this.storage.multiRemove(t)]:[3,16];case 15:l.sent(),l.label=16;case 16:return[2]}}))}))},e.prototype.save=function(e,t){return f(this,void 0,void 0,(function(){var n,r;return l(this,(function(i){switch(i.label){case 0:return n=this.getCollectionIndex(t).get(e.id)||this.getMonotonicFactory(t)(),r=this.getKeyForItem(t,e.id,n),this.getCollectionIndex(t).set(e.id,n),[4,this.storage.setItem(r,JSON.stringify(e))];case 1:return i.sent(),[2]}}))}))},e.prototype.batchSave=function(e,t){return f(this,void 0,void 0,(function(){var n,r,o,s,a,u,c,f,p,v,g,m,b,y,w,_,S,E,M,A,I,k=this;return l(this,(function(l){switch(l.label){case 0:if(0===t.length)return[2,[]];n=[],r=this.getCollectionIndex(e),o=new Set,s=new Set,a=[],u={};try{for(c=d(t),f=c.next();!f.done;f=c.next())p=f.value,v=p.id,g=p._deleted,m=r.get(v)||this.getMonotonicFactory(e)(),S=this.getKeyForItem(e,v,m),a.push(S),u[S]={ulid:m,model:p},g?o.add(S):s.add(S)}catch(e){E={error:e}}finally{try{f&&!f.done&&(M=c.return)&&M.call(c)}finally{if(E)throw E.error}}return[4,this.storage.multiGet(a)];case 1:return b=l.sent(),y=b.filter((function(e){return!!h(e,2)[1]})).reduce((function(e,t){var n=h(t,1)[0];return e.add(n)}),new Set),[4,new Promise((function(e,t){if(0!==o.size){var n=Array.from(o);n.forEach((function(e){return r.delete(u[e].model.id)})),k.storage.multiRemove(n,(function(n){n&&n.length>0?t(n):e()}))}else e()}))];case 2:return l.sent(),[4,new Promise((function(e,t){if(0!==s.size){var n=Array.from(s).map((function(e){return[e,JSON.stringify(u[e].model)]}));s.forEach((function(e){var t=u[e],n=t.model.id,i=t.ulid;r.set(n,i)})),k.storage.multiSet(n,(function(n){n&&n.length>0?t(n):e()}))}else e()}))];case 3:l.sent();try{for(w=d(a),_=w.next();!_.done;_=w.next())S=_.value,o.has(S)&&y.has(S)?n.push([u[S].model,i.c.DELETE]):s.has(S)&&n.push([u[S].model,y.has(S)?i.c.UPDATE:i.c.INSERT])}catch(e){A={error:e}}finally{try{_&&!_.done&&(I=w.return)&&I.call(w)}finally{if(A)throw A.error}}return[2,n]}}))}))},e.prototype.get=function(e,t){return f(this,void 0,void 0,(function(){var n,r,i;return l(this,(function(o){switch(o.label){case 0:return n=this.getCollectionIndex(t).get(e),r=this.getKeyForItem(t,e,n),[4,this.storage.getItem(r)];case 1:return i=o.sent(),[2,i&&JSON.parse(i)]}}))}))},e.prototype.getOne=function(e,t){return f(this,void 0,void 0,(function(){var n,r,o,s,a,u,c;return l(this,(function(f){switch(f.label){case 0:return n=this.getCollectionIndex(t),r=h(e===i.d.FIRST?function(){var e,t,r,i,o;try{for(var s=d(n),a=s.next();!a.done;a=s.next()){i=(r=h(a.value,2))[0],o=r[1];break}}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}return[i,o]}():function(){var e,t,r,i,o;try{for(var s=d(n),a=s.next();!a.done;a=s.next())i=(r=h(a.value,2))[0],o=r[1]}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}return[i,o]}(),2),o=r[0],s=r[1],a=this.getKeyForItem(t,o,s),(c=a)?[4,this.storage.getItem(a)]:[3,2];case 1:c=f.sent(),f.label=2;case 2:return[2,(u=c)&&JSON.parse(u)||void 0]}}))}))},e.prototype.getAll=function(e,t){return f(this,void 0,void 0,(function(){var n,r,i,o,s,a,u,c,f,p,v,g,m,b,y,w,_,S;return l(this,(function(l){switch(l.label){case 0:n=this.getCollectionIndex(e),i=(r=t||{}).page,o=void 0===i?0:i,s=r.limit,a=void 0===s?0:s,u=Math.max(0,o*a)||0,c=a>0?u+a:void 0,f=[],p=0;try{for(v=d(n),g=v.next();!g.done&&(m=h(g.value,2),b=m[0],y=m[1],++p<=u||(f.push(this.getKeyForItem(e,b,y)),p!==c));g=v.next());}catch(e){_={error:e}}finally{try{g&&!g.done&&(S=v.return)&&S.call(v)}finally{if(_)throw _.error}}return[4,this.storage.multiGet(f)];case 1:return w=l.sent(),[2,w.filter((function(e){return h(e,2)[1]})).map((function(e){var t=h(e,2)[1];return JSON.parse(t)}))]}}))}))},e.prototype.delete=function(e,t){return f(this,void 0,void 0,(function(){var n,r;return l(this,(function(i){switch(i.label){case 0:return n=this.getCollectionIndex(t).get(e),r=this.getKeyForItem(t,e,n),this.getCollectionIndex(t).delete(e),[4,this.storage.removeItem(r)];case 1:return i.sent(),[2]}}))}))},e.prototype.clear=function(){return f(this,void 0,void 0,(function(){var e,t;return l(this,(function(n){switch(n.label){case 0:return[4,this.storage.getAllKeys()];case 1:return e=n.sent(),t=e.filter((function(e){return e.startsWith(p)})),[4,this.storage.multiRemove(t)];case 2:return n.sent(),this._collectionInMemoryIndex.clear(),[2]}}))}))},e.prototype.getKeyForItem=function(e,t,n){return this.getKeyPrefixForStoreItems(e)+"::"+n+"::"+t},e.prototype.getLegacyKeyForItem=function(e,t){return this.getKeyPrefixForStoreItems(e)+"::"+t},e.prototype.getKeyPrefixForStoreItems=function(e){return p+"::"+e+"::Data"},e}(),m=n(9),b=n(245);function y(e){return(y="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)}var w=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},_=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},S=function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e="function"==typeof M?M(e):e[Symbol.iterator](),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,i){(function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)})(r,i,(t=e[n](t)).done,t.value)}))}}},E=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},M=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},A=new r.a("DataStore"),I=function(){function e(){}return e.prototype.getStorenameForModel=function(e){var t=this.namespaceResolver(e),n=e.name;return this.getStorename(t,n)},e.prototype.getStorename=function(e,t){return e+"_"+t},e.prototype.setUp=function(e,t,n,r){return w(this,void 0,void 0,(function(){var i,o=this;return _(this,(function(s){switch(s.label){case 0:return this.initPromise?[3,1]:(this.initPromise=new Promise((function(e,t){o.resolve=e,o.reject=t})),[3,3]);case 1:return[4,this.initPromise];case 2:return s.sent(),[2];case 3:this.schema=e,this.namespaceResolver=t,this.modelInstanceCreator=n,this.getModelConstructorByModelName=r,s.label=4;case 4:return s.trys.push([4,7,,8]),this.db?[3,6]:(this.db=new g,[4,this.db.init()]);case 5:s.sent(),this.resolve(),s.label=6;case 6:return[3,8];case 7:return i=s.sent(),this.reject(i),[3,8];case 8:return[2]}}))}))},e.prototype.save=function(e,t){var n,r;return w(this,void 0,void 0,(function(){var s,a,u,c,f,l,d,h,p,v,g,b,y,w,E,M,I,k,O,x,C,T=this;return _(this,(function(_){switch(_.label){case 0:return s=Object.getPrototypeOf(e).constructor,a=this.getStorenameForModel(s),u=Object(o.x)(s.name,e,this.schema.namespaces[this.namespaceResolver(s)],this.modelInstanceCreator,this.getModelConstructorByModelName),c=this.namespaceResolver(s),f=new Set,l=Object.values(u).map((function(e){var t=e.modelName,n=e.item,r=e.instance,i=T.getStorename(c,t);return f.add(i),{storeName:i,item:n,instance:r}})),[4,this.db.get(e.id,a)];case 1:if(d=_.sent(),t&&d&&(h=m.a.getPredicates(t),p=h.predicates,v=h.type,!Object(o.y)(d,v,p)))throw g="Conditional update failed",A.error(g,{model:d,condition:p}),new Error(g);b=[],_.label=2;case 2:_.trys.push([2,11,12,17]),y=S(l),_.label=3;case 3:return[4,y.next()];case 4:return(w=_.sent()).done?[3,10]:(E=w.value,M=E.storeName,I=E.item,k=E.instance,O=I.id,[4,this.db.get(O,M)]);case 5:return x=_.sent()?i.c.UPDATE:i.c.INSERT,O!==e.id?[3,7]:[4,this.db.save(I,M)];case 6:return _.sent(),b.push([k,x]),[3,9];case 7:return x!==i.c.INSERT?[3,9]:[4,this.db.save(I,M)];case 8:_.sent(),b.push([k,x]),_.label=9;case 9:return[3,3];case 10:return[3,17];case 11:return C=_.sent(),n={error:C},[3,17];case 12:return _.trys.push([12,,15,16]),w&&!w.done&&(r=y.return)?[4,r.call(y)]:[3,14];case 13:_.sent(),_.label=14;case 14:return[3,16];case 15:if(n)throw n.error;return[7];case 16:return[7];case 17:return[2,b]}}))}))},e.prototype.load=function(e,t,n){var r,i,s,a,u,c,f,l,d,h;return w(this,void 0,void 0,(function(){var p,v,g,m,b,y,w,E,M,A,I,k,O,x,C,T,P,N,R=this;return _(this,(function(_){switch(_.label){case 0:if(p=this.schema.namespaces[e],v=p.relationships[t].relationTypes,g=v.map((function(t){var n=t.modelName;return R.getStorename(e,n)})),m=this.getModelConstructorByModelName(e,t),0===g.length)return[2,n.map((function(e){return R.modelInstanceCreator(m,e)}))];_.label=1;case 1:_.trys.push([1,34,35,40]),b=S(v),_.label=2;case 2:return[4,b.next()];case 3:if((y=_.sent()).done)return[3,33];switch(w=y.value,E=w.fieldName,M=w.modelName,A=w.targetName,I=w.relationType,k=this.getStorename(e,M),O=this.getModelConstructorByModelName(e,M),I){case"HAS_ONE":return[3,4];case"BELONGS_TO":return[3,17];case"HAS_MANY":return[3,30]}return[3,31];case 4:_.trys.push([4,10,11,16]),r=S(n),_.label=5;case 5:return[4,r.next()];case 6:return(i=_.sent()).done?[3,9]:(C=i.value)[E]?[4,this.db.get(C[E],k)]:[3,8];case 7:T=_.sent(),C[E]=T&&this.modelInstanceCreator(O,T),_.label=8;case 8:return[3,5];case 9:return[3,16];case 10:return x=_.sent(),f={error:x},[3,16];case 11:return _.trys.push([11,,14,15]),i&&!i.done&&(l=r.return)?[4,l.call(r)]:[3,13];case 12:_.sent(),_.label=13;case 13:return[3,15];case 14:if(f)throw f.error;return[7];case 15:return[7];case 16:return[3,32];case 17:_.trys.push([17,23,24,29]),s=S(n),_.label=18;case 18:return[4,s.next()];case 19:return(a=_.sent()).done?[3,22]:(C=a.value)[A]?[4,this.db.get(C[A],k)]:[3,21];case 20:T=_.sent(),C[E]=T&&this.modelInstanceCreator(O,T),delete C[A],_.label=21;case 21:return[3,18];case 22:return[3,29];case 23:return P=_.sent(),d={error:P},[3,29];case 24:return _.trys.push([24,,27,28]),a&&!a.done&&(h=s.return)?[4,h.call(s)]:[3,26];case 25:_.sent(),_.label=26;case 26:return[3,28];case 27:if(d)throw d.error;return[7];case 28:return[7];case 29:case 30:return[3,32];case 31:return Object(o.f)(I),[3,32];case 32:return[3,2];case 33:return[3,40];case 34:return N=_.sent(),u={error:N},[3,40];case 35:return _.trys.push([35,,38,39]),y&&!y.done&&(c=b.return)?[4,c.call(b)]:[3,37];case 36:_.sent(),_.label=37;case 37:return[3,39];case 38:if(u)throw u.error;return[7];case 39:return[7];case 40:return[2,n.map((function(e){return R.modelInstanceCreator(m,e)}))]}}))}))},e.prototype.query=function(e,t,n){return w(this,void 0,void 0,(function(){var r,s,a,u,c,f,l,d,h,p,v,g,b,y;return _(this,(function(w){switch(w.label){case 0:return r=this.getStorenameForModel(e),s=this.namespaceResolver(e),a=n&&n.sort,t?(u=m.a.getPredicates(t))?(c=u.predicates,f=u.type,(l=1===c.length&&c.find((function(e){return Object(i.k)(e)&&"id"===e.field&&"eq"===e.operator})))?(d=l.operand,[4,this.db.get(d,r)]):[3,4]):[3,7]:[3,7];case 1:return(h=w.sent())?[4,this.load(s,e.name,[h])]:[3,3];case 2:return p=E.apply(void 0,[w.sent(),1]),[2,[p[0]]];case 3:return[2,[]];case 4:return[4,this.db.getAll(r)];case 5:return v=w.sent(),g=c?v.filter((function(e){return Object(o.y)(e,f,c)})):v,[4,this.load(s,e.name,this.inMemoryPagination(g,n))];case 6:return[2,w.sent()];case 7:return a?[4,this.db.getAll(r)]:[3,10];case 8:return b=w.sent(),[4,this.load(s,e.name,this.inMemoryPagination(b,n))];case 9:return[2,w.sent()];case 10:return[4,this.db.getAll(r,n)];case 11:return y=w.sent(),[4,this.load(s,e.name,y)];case 12:return[2,w.sent()]}}))}))},e.prototype.inMemoryPagination=function(e,t){if(t){if(t.sort){var n=b.a.getPredicates(t.sort);if(n.length){var r=Object(o.w)(n);e.sort(r)}}var i=t.page,s=void 0===i?0:i,a=t.limit,u=void 0===a?0:a,c=Math.max(0,s*u)||0,f=u>0?c+u:e.length;return e.slice(c,f)}return e},e.prototype.queryOne=function(e,t){return void 0===t&&(t=i.d.FIRST),w(this,void 0,void 0,(function(){var n,r;return _(this,(function(i){switch(i.label){case 0:return n=this.getStorenameForModel(e),[4,this.db.getOne(t,n)];case 1:return[2,(r=i.sent())&&this.modelInstanceCreator(e,r)]}}))}))},e.prototype.delete=function(e,t){return w(this,void 0,void 0,(function(){var n,r,i,s,a,u,c,f,l,d,h,p,v;return _(this,(function(g){switch(g.label){case 0:return n=[],Object(o.s)(e)?(s=e,a=this.namespaceResolver(s),[4,this.query(s,t)]):[3,8];case 1:return r=g.sent(),p=this.schema.namespaces[a].relationships[s.name].relationTypes,void 0===t?[3,4]:[4,this.deleteTraverse(p,r,s.name,a,n)];case 2:return g.sent(),[4,this.deleteItem(n)];case 3:return g.sent(),v=n.reduce((function(e,t){var n=t.items;return e.concat(n)}),[]),[2,[r,v]];case 4:return[4,this.deleteTraverse(p,r,s.name,a,n)];case 5:return g.sent(),[4,this.deleteItem(n)];case 6:return g.sent(),v=n.reduce((function(e,t){var n=t.items;return e.concat(n)}),[]),[2,[r,v]];case 7:return[3,15];case 8:return i=e,s=Object.getPrototypeOf(i).constructor,a=this.namespaceResolver(s),u=this.getStorenameForModel(s),t?[4,this.db.get(i.id,u)]:[3,11];case 9:if(void 0===(c=g.sent()))return h="Model instance not found in storage",A.warn(h,{model:i}),[2,[[i],[]]];if(f=m.a.getPredicates(t),l=f.predicates,d=f.type,!Object(o.y)(c,d,l))throw h="Conditional update failed",A.error(h,{model:c,condition:l}),new Error(h);return p=this.schema.namespaces[a].relationships[s.name].relationTypes,[4,this.deleteTraverse(p,[i],s.name,a,n)];case 10:return g.sent(),[3,13];case 11:return p=this.schema.namespaces[a].relationships[s.name].relationTypes,[4,this.deleteTraverse(p,[i],s.name,a,n)];case 12:g.sent(),g.label=13;case 13:return[4,this.deleteItem(n)];case 14:return g.sent(),v=n.reduce((function(e,t){var n=t.items;return e.concat(n)}),[]),[2,[[i],v]];case 15:return[2]}}))}))},e.prototype.deleteItem=function(e){var t,n,r,i,o,s;return w(this,void 0,void 0,(function(){var a,u,c,f,l,d,h,p,v;return _(this,(function(g){switch(g.label){case 0:g.trys.push([0,17,18,23]),t=S(e),g.label=1;case 1:return[4,t.next()];case 2:if((n=g.sent()).done)return[3,16];a=n.value,u=a.storeName,c=a.items,g.label=3;case 3:g.trys.push([3,9,10,15]),f=S(c),g.label=4;case 4:return[4,f.next()];case 5:return(l=g.sent()).done?[3,8]:(d=l.value)?"object"!==y(d)?[3,7]:(h=d.id,[4,this.db.delete(h,u)]):[3,7];case 6:g.sent(),g.label=7;case 7:return[3,4];case 8:return[3,15];case 9:return p=g.sent(),o={error:p},[3,15];case 10:return g.trys.push([10,,13,14]),l&&!l.done&&(s=f.return)?[4,s.call(f)]:[3,12];case 11:g.sent(),g.label=12;case 12:return[3,14];case 13:if(o)throw o.error;return[7];case 14:return[7];case 15:return[3,1];case 16:return[3,23];case 17:return v=g.sent(),r={error:v},[3,23];case 18:return g.trys.push([18,,21,22]),n&&!n.done&&(i=t.return)?[4,i.call(t)]:[3,20];case 19:g.sent(),g.label=20;case 20:return[3,22];case 21:if(r)throw r.error;return[7];case 22:return[7];case 23:return[2]}}))}))},e.prototype.deleteTraverse=function(e,t,n,r,i){var s,a,u,c,f,l,d,h,p,v,g,m;return w(this,void 0,void 0,(function(){var b,y,w,E,M,A,I,k,O,x,C,T,P=this;return _(this,(function(_){switch(_.label){case 0:_.trys.push([0,35,36,41]),s=S(e),_.label=1;case 1:return[4,s.next()];case 2:if((a=_.sent()).done)return[3,34];switch(b=a.value,y=b.relationType,w=b.modelName,E=this.getStorename(r,w),M=Object(o.g)(this.schema.namespaces[r].relationships[w].relationTypes,n)||Object(o.h)(this.schema.namespaces[r].relationships[w].indexes,b.associatedWith),y){case"HAS_ONE":return[3,3];case"HAS_MANY":return[3,17];case"BELONGS_TO":return[3,31]}return[3,32];case 3:_.trys.push([3,10,11,16]),u=S(t),_.label=4;case 4:return[4,u.next()];case 5:return(c=_.sent()).done?[3,9]:(k=c.value,[4,this.db.getAll(E)]);case 6:return O=_.sent(),A=O.filter((function(e){return e[M]===k.id})),[4,this.deleteTraverse(this.schema.namespaces[r].relationships[w].relationTypes,A,w,r,i)];case 7:_.sent(),_.label=8;case 8:return[3,4];case 9:return[3,16];case 10:return I=_.sent(),p={error:I},[3,16];case 11:return _.trys.push([11,,14,15]),c&&!c.done&&(v=u.return)?[4,v.call(u)]:[3,13];case 12:_.sent(),_.label=13;case 13:return[3,15];case 14:if(p)throw p.error;return[7];case 15:return[7];case 16:return[3,33];case 17:_.trys.push([17,24,25,30]),f=S(t),_.label=18;case 18:return[4,f.next()];case 19:return(l=_.sent()).done?[3,23]:(k=l.value,[4,this.db.getAll(E)]);case 20:return O=_.sent(),x=O.filter((function(e){return e[M]===k.id})),[4,this.deleteTraverse(this.schema.namespaces[r].relationships[w].relationTypes,x,w,r,i)];case 21:_.sent(),_.label=22;case 22:return[3,18];case 23:return[3,30];case 24:return C=_.sent(),g={error:C},[3,30];case 25:return _.trys.push([25,,28,29]),l&&!l.done&&(m=f.return)?[4,m.call(f)]:[3,27];case 26:_.sent(),_.label=27;case 27:return[3,29];case 28:if(g)throw g.error;return[7];case 29:return[7];case 30:case 31:return[3,33];case 32:return Object(o.f)(y),[3,33];case 33:return[3,1];case 34:return[3,41];case 35:return T=_.sent(),d={error:T},[3,41];case 36:return _.trys.push([36,,39,40]),a&&!a.done&&(h=s.return)?[4,h.call(s)]:[3,38];case 37:_.sent(),_.label=38;case 38:return[3,40];case 39:if(d)throw d.error;return[7];case 40:return[7];case 41:return i.push({storeName:this.getStorename(r,n),items:t.map((function(e){return P.modelInstanceCreator(P.getModelConstructorByModelName(r,n),e)}))}),[2]}}))}))},e.prototype.clear=function(){return w(this,void 0,void 0,(function(){return _(this,(function(e){switch(e.label){case 0:return[4,this.db.clear()];case 1:return e.sent(),this.db=void 0,this.initPromise=void 0,[2]}}))}))},e.prototype.batchSave=function(e,t){return w(this,void 0,void 0,(function(){var n,r,i,s,a,u,c,f,l,d,h;return _(this,(function(p){switch(p.label){case 0:n=e.name,r=this.namespaceResolver(e),i=this.getStorename(r,n),s=[],a=function(t){var n=t.id,r=Object(o.x)(e.name,u.modelInstanceCreator(e,t),u.schema.namespaces[u.namespaceResolver(e)],u.modelInstanceCreator,u.getModelConstructorByModelName).find((function(e){return e.instance.id===n})).instance;s.push(r)},u=this;try{for(c=M(t),f=c.next();!f.done;f=c.next())l=f.value,a(l)}catch(e){d={error:e}}finally{try{f&&!f.done&&(h=c.return)&&h.call(c)}finally{if(d)throw d.error}}return[4,this.db.batchSave(i,s)];case 1:return[2,p.sent()]}}))}))},e}();t.default=new I},function(e,t,n){"use strict";n.r(t),n.d(t,"fromUtf8",(function(){return r})),n.d(t,"toUtf8",(function(){return i}));var r=function(e){return"function"==typeof TextEncoder?function(e){return(new TextEncoder).encode(e)}(e):function(e){for(var t=[],n=0,r=e.length;n<r;n++){var i=e.charCodeAt(n);if(i<128)t.push(i);else if(i<2048)t.push(i>>6|192,63&i|128);else if(n+1<e.length&&55296==(64512&i)&&56320==(64512&e.charCodeAt(n+1))){var o=65536+((1023&i)<<10)+(1023&e.charCodeAt(++n));t.push(o>>18|240,o>>12&63|128,o>>6&63|128,63&o|128)}else t.push(i>>12|224,i>>6&63|128,63&i|128)}return Uint8Array.from(t)}(e)},i=function(e){return"function"==typeof TextDecoder?function(e){return new TextDecoder("utf-8").decode(e)}(e):function(e){for(var t="",n=0,r=e.length;n<r;n++){var i=e[n];if(i<128)t+=String.fromCharCode(i);else if(192<=i&&i<224){var o=e[++n];t+=String.fromCharCode((31&i)<<6|63&o)}else if(240<=i&&i<365){var s="%"+[i,e[++n],e[++n],e[++n]].map((function(e){return e.toString(16)})).join("%");t+=decodeURIComponent(s)}else t+=String.fromCharCode((15&i)<<12|(63&e[++n])<<6|63&e[++n])}return t}(e)}},,,,,,,,,,,,,,,,,function(e,t,n){"use strict";n.d(t,"a",(function(){return l})),n.d(t,"b",(function(){return d}));var r,i=n(44),o=(r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),s=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{u(r.next(e))}catch(e){o(e)}}function a(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}u((r=r.apply(e,t||[])).next())}))},a=function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=t.call(e,s)}catch(e){o=[6,e],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,a])}}},u=function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s},c=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(u(arguments[t]));return e},f=new i.a("Util"),l=function(e){function t(t){var n=e.call(this,t)||this;return n.nonRetryable=!0,n}return o(t,e),t}(Error);var d=function(e,t,n){return void 0===n&&(n=3e5),function e(t,n,r,i){return void 0===i&&(i=1),s(this,void 0,void 0,(function(){var o,s;return a(this,(function(a){switch(a.label){case 0:if("function"!=typeof t)throw Error("functionToRetry must be a function");f.debug(t.name+" attempt #"+i+" with this vars: "+JSON.stringify(n)),a.label=1;case 1:return a.trys.push([1,3,,8]),[4,t.apply(void 0,c(n))];case 2:return[2,a.sent()];case 3:if(o=a.sent(),f.debug("error on "+t.name,o),(u=o)&&u.nonRetryable)throw f.debug(t.name+" non retryable error",o),o;return s=r(i,n,o),f.debug(t.name+" retrying in "+s+" ms"),!1===s?[3,6]:[4,new Promise((function(e){return setTimeout(e,s)}))];case 4:return a.sent(),[4,e(t,n,r,i+1)];case 5:return[2,a.sent()];case 6:throw o;case 7:return[3,8];case 8:return[2]}var u}))}))}(e,t,function(e){return function(t){var n=100*Math.pow(2,t)+100*Math.random();return!(n>e)&&n}}(n))}}])})); - -// version: 3.3.18 diff --git a/docs/javascript/extra.js b/docs/javascript/extra.js deleted file mode 100644 index 603c1ffc2..000000000 --- a/docs/javascript/extra.js +++ /dev/null @@ -1,76 +0,0 @@ -const Amplify = window.aws_amplify.Amplify -const Analytics = Amplify.Analytics -const KinesisFirehoseProvider = window.aws_amplify.AWSKinesisFirehoseProvider - -const awsconfig = { - "aws_project_region": "eu-west-1", - "aws_cognito_identity_pool_id": "eu-west-1:3df3caec-4bb6-4891-b154-ee940c8264b8", - "aws_cognito_region": "eu-west-1", - "aws_kinesis_firehose_stream_name": "ClickStreamKinesisFirehose-OGX7PQdrynUo", -}; - -const RUNTIME = "java" -const BASE_ORIGIN = "docs.powertools.aws.dev" - -function enableSearchOnBlurElement() { - if (document.location.hostname != BASE_ORIGIN) return // prevent unnecessary data - /* Register handler to log search on blur */ - document.addEventListener("DOMContentLoaded", function () { - recordPageView({ - prevLocation: document.referrer - }) - if (document.forms.search) { - let query = document.forms.search.query - query.addEventListener("blur", function () { - // If Search result is ever actionable - // we should populate `value` - if (this.value) { - console.info(`Search value: ${this.value}`) - recordPageView({ - searchPattern: this.value - }) - } - }) - } - }) - - // Register handler for page sections when browser history is changed - window.onpopstate = function (event) { - recordPageView({ - prevLocation: document.referrer - }) - }; -} - -const attachListeners = () => { - enableSearchOnBlurElement() -} - -const init = () => { - Analytics.addPluggable(new KinesisFirehoseProvider()) - Amplify.configure(awsconfig); - - Analytics.configure({ - AWSKinesisFirehose: { - region: awsconfig.aws_project_region - } - }) - - attachListeners() -} - -const recordPageView = ({prevLocation, searchPattern}) => { - Analytics.record({ - data: { - // Do not count page view for search - url: searchPattern ? null : window.location.href, - section: searchPattern ? null : location.pathname, - previous: prevLocation || null, - search: searchPattern || null, - language: RUNTIME - }, - streamName: awsconfig.aws_kinesis_firehose_stream_name - }, 'AWSKinesisFirehose') -} - -init() diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..5f7a59c02 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block outdated %} +You're not viewing the latest version. +<a href="{{ '../' ~ base_url }}"> + <strong>Click here to go to latest.</strong> +</a> +{% endblock %} + +{% block extrahead %} + <meta name="guide-name" content="Powertools for AWS Lambda (Java)"> + <meta name="service-name" content="Powertools for AWS Lambda"> +{% endblock %} diff --git a/docs/processes/maintainers.md b/docs/processes/maintainers.md index 6b3d9e126..f2839c532 100644 --- a/docs/processes/maintainers.md +++ b/docs/processes/maintainers.md @@ -14,21 +14,23 @@ This is document explains who the maintainers are, their responsibilities, and h ## Current Maintainers -| Maintainer | GitHub ID | Affiliation | -|-----------------------|---------------------------------------------------------------------------------| ----------- | -| Jerome Van Der Linden | [jeromevdl](https://github.com/jeromevdl){target="_blank"} | Amazon | -| Michele Ricciardi | [mriccia](https://github.com/mriccia){target="_blank"} | Amazon | -| Scott Gerring | [scottgerring](https://github.com/scottgerring){target="_blank"} | Amazon | +| Maintainer | GitHub ID | Affiliation | +| --------------- | -------------------------------------------------------------------- | ----------- | +| Philipp Page | [phipag](https://github.com/phipag){target="\_blank" rel="nofollow"} | Amazon | ## Emeritus Previous active maintainers who contributed to this project. -| Maintainer | GitHub ID | Affiliation | -|----------------|-----------------------------------------------------------------------------------------|---------------| -| Mark Sailes | [msailes](https://github.com/msailes){target="_blank"} | Amazon | -| Pankaj Agrawal | [pankajagrawal16](https://github.com/pankajagrawal16){target="_blank"} | Former Amazon | -| Steve Houel | [stevehouel](https://github.com/stevehouel) | Amazon | +| Maintainer | GitHub ID | Affiliation | +| --------------------- | -------------------------------------------------------------------------------------- | ------------- | +| Simon Thulbourn | [sthulb](https://github.com/sthulb){target="\_blank" rel="nofollow"} | Former Amazon | +| Jerome Van Der Linden | [jeromevdl](https://github.com/jeromevdl){target="\_blank" rel="nofollow"} | Amazon | +| Michele Ricciardi | [mriccia](https://github.com/mriccia){target="\_blank" rel="nofollow"} | Amazon | +| Scott Gerring | [scottgerring](https://github.com/scottgerring){target="\_blank" rel="nofollow"} | DataDog | +| Mark Sailes | [msailes](https://github.com/msailes){target="\_blank" rel="nofollow"} | Former Amazon | +| Pankaj Agrawal | [pankajagrawal16](https://github.com/pankajagrawal16){target="\_blank" rel="nofollow"} | Former Amazon | +| Steve Houel | [stevehouel](https://github.com/stevehouel){target="\_blank" rel="nofollow"} | Amazon | ## Labels diff --git a/docs/processes/versioning.md b/docs/processes/versioning.md new file mode 100644 index 000000000..d20269001 --- /dev/null +++ b/docs/processes/versioning.md @@ -0,0 +1,61 @@ +--- +title: Versioning and maintenance policy +description: Versioning and maintenance policy for Powertools for AWS Lambda (Python) +--- + +### Overview + +This document outlines the maintenance policy for Powertools for AWS Lambda and their underlying dependencies. AWS regularly provides Powertools for AWS Lambda with updates that may contain new features, enhancements, bug fixes, security patches, or documentation updates. Updates may also address changes with dependencies, language runtimes, and operating systems. Powertools for AWS Lambda is published to package managers (e.g. PyPi, NPM, Maven, NuGet), and are available as source code on GitHub. + +We recommend users to stay up-to-date with Powertools for AWS Lambda releases to keep up with the latest features, security updates, and underlying dependencies. Continued use of an unsupported Powertools for AWS Lambda version is not recommended and is done at the user’s discretion. + +!!! info "For brevity, we will interchangeably refer to Powertools for AWS Lambda as "SDK" _(Software Development Toolkit)_." + +### Versioning + +Powertools for AWS Lambda release versions are in the form of X.Y.Z where X represents the major version. Increasing the major version of an SDK indicates that this SDK underwent significant and substantial changes to support new idioms and patterns in the language. Major versions are introduced when public interfaces _(e.g. classes, methods, types, etc.)_, behaviors, or semantics have changed. Applications need to be updated in order for them to work with the newest SDK version. It is important to update major versions carefully and in accordance with the upgrade guidelines provided by AWS. + +### SDK major version lifecycle + +The lifecycle for major Powertools for AWS Lambda versions consists of 5 phases, which are outlined below. + +- **Developer Preview** (Phase 0) - During this phase, SDKs are not supported, should not be used in production environments, and are meant for early access and feedback purposes only. It is possible for future releases to introduce breaking changes. Once AWS identifies a release to be a stable product, it may mark it as a Release Candidate. Release Candidates are ready for GA release unless significant bugs emerge, and will receive full AWS support. +- **General Availability (GA)** (Phase 1) - During this phase, SDKs are fully supported. AWS will provide regular SDK releases that include support for new features, enhancements, as well as bug and security fixes. AWS will support the GA version of an SDK for _at least 24 months_, unless otherwise specified. +- **Maintenance Announcement** (Phase 2) - AWS will make a public announcement at least 6 months before an SDK enters maintenance mode. During this period, the SDK will continue to be fully supported. Typically, maintenance mode is announced at the same time as the next major version is transitioned to GA. +- **Maintenance** (Phase 3) - During the maintenance mode, AWS limits SDK releases to address critical bug fixes and security issues only. An SDK will not receive API updates for new or existing services, or be updated to support new regions. Maintenance mode has a _default duration of 6 months_, unless otherwise specified. +- **End-of-Support** (Phase 4) - When an SDK reaches end-of support, it will no longer receive updates or releases. Previously published releases will continue to be available via public package managers and the code will remain on GitHub. The GitHub repository may be archived. Use of an SDK which has reached end-of-support is done at the user’s discretion. We recommend users upgrade to the new major version. + +!!! note "Please note that the timelines shown below are illustrative and not binding" + +![Maintenance policy timelines](https://docs.aws.amazon.com/images/sdkref/latest/guide/images/maint-policy.png) + +### Dependency lifecycle + +Most AWS SDKs have underlying dependencies, such as language runtimes, AWS Lambda runtime, or third party libraries and frameworks. These dependencies are typically tied to the language community or the vendor who owns that particular component. Each community or vendor publishes their own end-of-support schedule for their product. + +The following terms are used to classify underlying third party dependencies: + +- [**AWS Lambda Runtime**](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html): Examples include `java17`, `nodejs20.x`, `python3.13`, etc. +- **Language Runtime**: Examples include Java 17, Python 3.13, NodeJS 20, .NET Core, etc. +- **Third party Library**: Examples include Jackson Project, AWS X-Ray SDK, AWS Encryption SDK, etc. + +Powertools for AWS Lambda follows the [AWS Lambda Runtime deprecation policy cycle](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-support-policy), when it comes to Language Runtime. This means we will stop supporting their respective deprecated Language Runtime _(e.g., `java8`)_ without increasing the major SDK version. + +!!! note "AWS reserves the right to stop support for an underlying dependency without increasing the major SDK version" + +### Communication methods + +Maintenance announcements are communicated in several ways: + +- A pinned GitHub Request For Comments (RFC) issue indicating the campaign for the next major version. The RFC will outline the path to end-of-support, specify campaign timelines, and upgrade guidance. +- AWS SDK documentation, such as API reference documentation, user guides, SDK product marketing pages, and GitHub readme(s) are updated to indicate the campaign timeline and provide guidance on upgrading affected applications. +- Deprecation warnings are added to the SDKs, outlining the path to end-of-support and linking to the upgrade guide. + +To see the list of available major versions of Powertools for AWS Lambda and where they are in their maintenance lifecycle, see the [version support matrix](#version-support-matrix). + +### Version support matrix + +| SDK | Major version | Current Phase | General Availability Date | Notes | +| -------------------------------- | ------------- | -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- | +| Powertools for AWS Lambda (Java) | 2.x | General Availability | 06/12/2025 | See [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v2.0.0) | +| Powertools for AWS Lambda (Java) | 1.x | End-of-life | 11/04/2020 | See [announcement](https://github.com/aws-powertools/powertools-lambda-java/issues/1895) | diff --git a/docs/requirements.in b/docs/requirements.in new file mode 100644 index 000000000..e8acc7112 --- /dev/null +++ b/docs/requirements.in @@ -0,0 +1,3 @@ +mkdocs-git-revision-date-plugin==0.3.2 +mkdocs-macros-plugin==1.3.7 +mkdocs-llmstxt==0.2.0 diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..16529cf3b --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,296 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --generate-hashes --output-file=requirements.txt requirements.in +# +beautifulsoup4==4.13.3 \ + --hash=sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b \ + --hash=sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16 + # via + # markdownify + # mkdocs-llmstxt +click==8.1.8 \ + --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ + --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a + # via mkdocs +ghp-import==2.1.0 \ + --hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \ + --hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343 + # via mkdocs +gitdb==4.0.12 \ + --hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \ + --hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf + # via gitpython +gitpython==3.1.44 \ + --hash=sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110 \ + --hash=sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269 + # via mkdocs-git-revision-date-plugin +hjson==3.1.0 \ + --hash=sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75 \ + --hash=sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89 + # via + # mkdocs-macros-plugin + # super-collections +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # mkdocs + # mkdocs-git-revision-date-plugin + # mkdocs-macros-plugin +markdown==3.7 \ + --hash=sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2 \ + --hash=sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803 + # via mkdocs +markdown-it-py==3.0.0 \ + --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ + --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb + # via mdformat +markdownify==1.1.0 \ + --hash=sha256:32a5a08e9af02c8a6528942224c91b933b4bd2c7d078f9012943776fc313eeef \ + --hash=sha256:449c0bbbf1401c5112379619524f33b63490a8fa479456d41de9dc9e37560ebd + # via mkdocs-llmstxt +markupsafe==3.0.2 \ + --hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \ + --hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \ + --hash=sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0 \ + --hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \ + --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ + --hash=sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13 \ + --hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \ + --hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \ + --hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \ + --hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \ + --hash=sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0 \ + --hash=sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b \ + --hash=sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579 \ + --hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \ + --hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \ + --hash=sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff \ + --hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \ + --hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \ + --hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \ + --hash=sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb \ + --hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \ + --hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \ + --hash=sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a \ + --hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \ + --hash=sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a \ + --hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \ + --hash=sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8 \ + --hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \ + --hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \ + --hash=sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144 \ + --hash=sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f \ + --hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \ + --hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \ + --hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \ + --hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \ + --hash=sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158 \ + --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ + --hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \ + --hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \ + --hash=sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171 \ + --hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \ + --hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \ + --hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \ + --hash=sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d \ + --hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \ + --hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \ + --hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \ + --hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \ + --hash=sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29 \ + --hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \ + --hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \ + --hash=sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c \ + --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \ + --hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \ + --hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \ + --hash=sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a \ + --hash=sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178 \ + --hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \ + --hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \ + --hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \ + --hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50 + # via + # jinja2 + # mkdocs +mdformat==0.7.22 \ + --hash=sha256:61122637c9e1d9be1329054f3fa216559f0d1f722b7919b060a8c2a4ae1850e5 \ + --hash=sha256:eef84fa8f233d3162734683c2a8a6222227a229b9206872e6139658d99acb1ea + # via mkdocs-llmstxt +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +mergedeep==1.3.4 \ + --hash=sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8 \ + --hash=sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307 + # via + # mkdocs + # mkdocs-get-deps +mkdocs==1.6.1 \ + --hash=sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2 \ + --hash=sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e + # via + # mkdocs-git-revision-date-plugin + # mkdocs-macros-plugin +mkdocs-get-deps==0.2.0 \ + --hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \ + --hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134 + # via mkdocs +mkdocs-git-revision-date-plugin==0.3.2 \ + --hash=sha256:2e67956cb01823dd2418e2833f3623dee8604cdf223bddd005fe36226a56f6ef + # via -r requirements.in +mkdocs-llmstxt==0.2.0 \ + --hash=sha256:104f10b8101167d6baf7761942b4743869be3d8f8a8d909f4e9e0b63307f709e \ + --hash=sha256:907de892e0c8be74002e8b4d553820c2b5bbcf03cc303b95c8bca48fb49c1a29 + # via -r requirements.in +mkdocs-macros-plugin==1.3.7 \ + --hash=sha256:02432033a5b77fb247d6ec7924e72fc4ceec264165b1644ab8d0dc159c22ce59 \ + --hash=sha256:17c7fd1a49b94defcdb502fd453d17a1e730f8836523379d21292eb2be4cb523 + # via -r requirements.in +packaging==24.2 \ + --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ + --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f + # via + # mkdocs + # mkdocs-macros-plugin +pathspec==0.12.1 \ + --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ + --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 + # via + # mkdocs + # mkdocs-macros-plugin +platformdirs==4.3.7 \ + --hash=sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94 \ + --hash=sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351 + # via mkdocs-get-deps +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via + # ghp-import + # mkdocs-macros-plugin +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via + # mkdocs + # mkdocs-get-deps + # mkdocs-macros-plugin + # pyyaml-env-tag +pyyaml-env-tag==0.1 \ + --hash=sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb \ + --hash=sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069 + # via mkdocs +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # markdownify + # python-dateutil +smmap==5.0.2 \ + --hash=sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5 \ + --hash=sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e + # via gitdb +soupsieve==2.6 \ + --hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \ + --hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9 + # via beautifulsoup4 +super-collections==0.5.3 \ + --hash=sha256:907d35b25dc4070910e8254bf2f5c928348af1cf8a1f1e8259e06c666e902cff \ + --hash=sha256:94c1ec96c0a0d5e8e7d389ed8cde6882ac246940507c5e6b86e91945c2968d46 + # via mkdocs-macros-plugin +termcolor==3.0.1 \ + --hash=sha256:a6abd5c6e1284cea2934443ba806e70e5ec8fd2449021be55c280f8a3731b611 \ + --hash=sha256:da1ed4ec8a5dc5b2e17476d859febdb3cccb612be1c36e64511a6f2485c10c69 + # via mkdocs-macros-plugin +typing-extensions==4.13.2 \ + --hash=sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c \ + --hash=sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef + # via beautifulsoup4 +watchdog==6.0.0 \ + --hash=sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a \ + --hash=sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2 \ + --hash=sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f \ + --hash=sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c \ + --hash=sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c \ + --hash=sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c \ + --hash=sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0 \ + --hash=sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13 \ + --hash=sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134 \ + --hash=sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa \ + --hash=sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e \ + --hash=sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379 \ + --hash=sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a \ + --hash=sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11 \ + --hash=sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282 \ + --hash=sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b \ + --hash=sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f \ + --hash=sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c \ + --hash=sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112 \ + --hash=sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948 \ + --hash=sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881 \ + --hash=sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860 \ + --hash=sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3 \ + --hash=sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680 \ + --hash=sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26 \ + --hash=sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26 \ + --hash=sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e \ + --hash=sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8 \ + --hash=sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c \ + --hash=sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2 + # via mkdocs diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 000000000..4ef1ef06e --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,142 @@ +--- +title: Roadmap +description: Public roadmap for Powertools for AWS Lambda (Java) +--- + +## Overview + +Our public roadmap outlines the high level direction we are working towards. We update this document when our priorities change: security and stability are our top priority. + +### Key areas + +Security and operational excellence take precedence above all else. This means bug fixing, stability, customer's support, and internal compliance may delay one or more key areas below. + +!!! info "We may choose to re-prioritize or defer items based on customer feedback, security, and operational impacts, and business value." + +#### Release Security (p0) + +Our top priority is to establish the processes and infrastructure needed for a fully automated and secure end-to-end release process of new versions to Maven Central. + +- [x] [Implement GitHub workflows](https://github.com/aws-powertools/powertools-lambda-java/issues/1231){target="\_blank"} and create infrastructure to release to Maven Central +- [x] [Implement end-to-end tests](https://github.com/aws-powertools/powertools-lambda-java/issues/1815){target="\_blank"} +- [x] Implement [OpenSSF Scorecard](https://openssf.org/projects/scorecard/){target="\_blank"} + +#### `v2` Release: Consistency and Ecosystem (p1) + +As part of a new major version `v2` release, we prioritize the Java project's consistency of core utilities (Logging, Metrics, Tracing) with the other runtimes (Python, TypeScript, .NET). Additionally, we will focus on integrating the library with popular technologies and frameworks from the Java and AWS ecosystem. Particularly, we aim at leveraging new techniques to allow customers to reduce Lambda cold-start time. The `v2` release will also drop support for Java 8 moving to Java 11 as the baseline. + +##### Core Utilities + +- [x] [Review public interfaces and reduce public API surface area](https://github.com/aws-powertools/powertools-lambda-java/issues/1283){target="\_blank"} +- [x] [Release Logging `v2` module](https://github.com/aws-powertools/powertools-lambda-java/issues/965){target="\_blank"} allowing customers to choose the logging framework and adding support for logging deeply nested objects as JSON +- [x] [Support high resolution metrics](https://github.com/aws-powertools/powertools-lambda-java/issues/1041){target="\_blank"} +- [x] [Improve modularity of metrics module](https://github.com/aws-powertools/powertools-lambda-java/issues/1848){target="\_blank"} to remove coupling with EMF library and enable future support for additional metrics providers / backends + +##### Ecosystem + +- [x] [Add GraalVM support for core utilities](https://github.com/aws-powertools/powertools-lambda-java/issues/764){target="\_blank"} +- [ ] [Implement priming using CRaC to improve AWS Snapstart support](https://github.com/aws-powertools/powertools-lambda-java/issues/1588){target="\_blank"} +- [ ] [Evaluate integration with popular Java frameworks such as Micronaut, Spring Cloud Function, or Quarkus](https://github.com/aws-powertools/powertools-lambda-java/issues/1701){target="\_blank"} + +##### Other + +- [x] [Validation module integration with HTTP requests](https://github.com/aws-powertools/powertools-lambda-java/issues/1298){target="\_blank"} +- [x] [Support validation module from within the batch module](https://github.com/aws-powertools/powertools-lambda-java/issues/1496){target="\_blank"} +- [x] [Add support for parallel processing in Batch Processing utility](https://github.com/aws-powertools/powertools-lambda-java/issues/1540){target="\_blank"} +- [ ] [Documentation: Review and improve documentation to be consistent with other runtimes](https://github.com/aws-powertools/powertools-lambda-java/issues/1352){target="\_blank"} + +#### Feature Parity (p2) + +If priorities `p0` and `p1` are addressed, we will also focus on feature parity of non-core utilities. This allows customers to achieve better standardization of their development processes across different Powertools runtimes. + +- [ ] [Re-evaluate if there is a need for adding a lightweight customer Powertools event handler](https://github.com/aws-powertools/powertools-lambda-java/issues/1103){target="\_blank"} +- [ ] [Add comprehensive GraalVM support for all utilities](){target="\_blank"} +- [ ] [Add Feature Flags module](https://github.com/aws-powertools/powertools-lambda-java/issues/1086){target="\_blank"} +- [ ] [Add S3 Streaming module](https://github.com/aws-powertools/powertools-lambda-java/issues/1085){target="\_blank"} +- [ ] Add support for Data Masking during JSON serialization + +### Missing something? + +You can help us prioritize by [upvoting existing feature requests](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue%20state%3Aopen%20label%3Aenhancement){target="\_blank"}, +leaving a comment on what use cases it could unblock for you, and by joining our discussions on Discord. + +[![Join our Discord](https://dcbadge.vercel.app/api/server/B8zZKbbyET)](https://discord.gg/B8zZKbbyET){target="\_blank"} + +### Roadmap status definition + +<center> +```mermaid +graph LR + Ideas --> Backlog --> Work["Working on it"] --> Merged["Coming soon"] --> Shipped +``` +<i>Visual representation</i> +</center> + +Within our [public board](https://github.com/orgs/aws-powertools/projects/4/){target="\_blank"}, you'll see the following values in the `Status` column: + +- **Ideas**. Incoming and existing feature requests that are not being actively considered yet. These will be reviewed + when bandwidth permits. +- **Backlog**. Accepted feature requests or enhancements that we want to work on. +- **Working on it**. Features or enhancements we're currently either researching or implementing it. +- **Coming soon**. Any feature, enhancement, or bug fixes that have been merged and are coming in the next release. +- **Shipped**. Features or enhancements that are now available in the most recent release. + +> Tasks or issues with empty `Status` will be categorized in upcoming review cycles. + +### Process + +<center> +```mermaid +graph LR + PFR[Feature request] --> Triage{Need RFC?} + Triage --> |Complex/major change or new utility?| RFC[Ask or write RFC] --> Approval{Approved?} + Triage --> |Minor feature or enhancement?| NoRFC[No RFC required] --> Approval + Approval --> |Yes| Backlog + Approval --> |No | Reject["Inform next steps"] + Backlog --> |Prioritized| Implementation + Backlog --> |Defer| WelcomeContributions["help-wanted label"] +``` +<i>Visual representation</i> +</center> + +Our end-to-end mechanism follows four major steps: + +- **Feature Request**. Ideas start with a [feature request](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=feature_request.md){target="\_blank"} to outline their use case at a high level. For complex use cases, maintainers might ask for/write a + RFC. + - Maintainers review requests based on [project tenets](index.md#tenets){target="\_blank"}, customers reaction (👍), + and use cases. +- **Request-for-comments (RFC)**. Design proposals use + our [RFC template](https://github.com/aws-powertools/powertools-lambda-java/issues/new?q=is%3Aissue+state%3Aopen+label%3Aenhancement&template=rfc.md){target="\_blank"} to describe its implementation, challenges, developer experience, dependencies, and alternative solutions. + - This helps refine the initial idea with community feedback before a decision is made. +- **Decision**. After carefully reviewing and discussing them, maintainers make a final decision on whether to start + implementation, defer or reject it, and update everyone with the next steps. +- **Implementation**. For approved features, maintainers give priority to the original authors for implementation unless + it is a sensitive task that is best handled by maintainers. + +!!! info "See [Maintainers](./processes/maintainers.md){target="\_blank"} document to understand how we triage issues and pull requests, labels and governance." + +### Disclaimer + +The Powertools for AWS Lambda (Java) team values feedback and guidance from its community of users, although final +decisions on inclusion into the project will be made by AWS. + +We determine the high-level direction for our open roadmap based on customer feedback and popularity (👍🏽 and comments), +security and operational impacts, and business value. Where features don’t meet our goals and longer-term strategy, we +will communicate that clearly and openly as quickly as possible with an explanation of why the decision was made. + +### FAQs + +**Q: Why did you build this?** + +A: We know that our customers are making decisions and plans based on what we are developing, and we want to provide our +customers the insights they need to plan. + +**Q: Why are there no dates on your roadmap?** + +A: Because job zero is security and operational stability, we can't provide specific target dates for features. The +roadmap is subject to change at any time, and roadmap issues in this repository do not guarantee a feature will be +launched as proposed. + +**Q: How can I provide feedback or ask for more information?** + +A: For existing features, you can directly comment on issues. For anything else, please open an issue. diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index de2f3b5f9..dc08ef51e 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,9 +1,11 @@ .md-grid { - max-width: 81vw + max-width: 90vw } .highlight .hll { - background-color: lavender + [data-md-color-scheme="default"] { + background-color: lavender; + } } .md-typeset table:not([class]) { diff --git a/docs/upgrade.md b/docs/upgrade.md new file mode 100644 index 000000000..c9662a3db --- /dev/null +++ b/docs/upgrade.md @@ -0,0 +1,458 @@ +--- +title: Upgrade guide +description: Guide to update between major Powertools for AWS Lambda (Java) versions +--- + +## End of support v1 + +<!-- prettier-ignore-start --> +!!! warning "End of support notice" + On December 12th, 2025, Powertools for AWS Lambda (Java) v1 reached end-of-life and will no longer receive updates or releases. If you are still using v1, we strongly recommend you to read our upgrade guide and update to the latest version. Refer to [our announcement](https://github.com/aws-powertools/powertools-lambda-java/issues/1895) for details. +<!-- prettier-ignore-end --> + +Given our commitment to all of our customers using Powertools for AWS Lambda (Java), we will keep [Maven Central](https://central.sonatype.com/search?q=powertools){target="\_blank"} `v1` releases and a `v1` documentation archive to prevent any disruption. + +## Migrate to v2 from v1 + +!!! info "We strongly encourage you to migrate to `v2`. Refer to our [versioning policy](./processes/versioning.md) to learn more about our version support process." + +We've made minimal breaking changes to make your transition to `v2` as smooth as possible. + +### Quick summary + +The following table shows a summary of the changes made in `v2` and whether code changes are necessary. Each change that requires a code change links to a section below explaining more details. + +| Area | Change | Code change required | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| **Logging** | The [logging module was re-designed](#redesigned-logging-utility) from scratch to support popular Java logging paradigms and libraries like `log4j2`, `logback`, and `slf4j`. | Yes | +| **Metrics** | [Changed public interface](#updated-metrics-utility-interface) to remove direct coupling with `aws-embedded-metrics-java`. | Yes | +| **Tracing** | [Removed deprecated `captureResponse` and `captureError` options](#deprecated-capture-mode-related-tracing-annotation-parameters) on `@Tracing` annotation. | Yes | +| **Idempotency** | The [`powertools-idempotency` module was split by provider](#idempotency-utility-split-into-sub-modules-by-provider) to improve modularity and reduce the deployment package size. | Yes | +| **Idempotency** | Updated `IdempotencyConfig` interface to support addition of response hooks. | No | +| **Parameters** | The [`powertools-parameters` module was split by provider](#parameters-utility-split-into-sub-modules-by-provider) to improve modularity and reduce the deployment package size. | Yes | +| **Batch Processing** | [Removed deprecated `powertools-sqs` module](#removed-powertools-sqs-module-in-favor-of-powertools-batch) in favor of the more generic [Batch Processing](./utilities/batch.md) utility. | Yes | +| **Batch Processing** | Updated Batch Processing `BatchMessageHandler` interface to add support for parallel processing. | No | +| **Validation** | The `@Validation` utility returns 4xx error codes instead of 5xx error codes when used with API Gateway now. | No | +| **Validation** | Validating batch event sources now adds failed events as partial batch failures and does not fail the whole batch anymore. | No | +| **Custom Resources** | [Removed deprecated `Response.failed()` and `Response.success()` methods](#custom-resources-updates-the-response-class). | Yes | +| **Custom Resources** | Changed interface of `Response` class to add an optional `reason` field. | No | +| **Dependencies** | Renamed `powertools-core` to `powertools-common`. This module should not be used as direct dependency and is listed here for completeness. | No | +| **Dependencies** | [Removed `org.aspectj.aspectjrt` as project dependency](#aspectj-runtime-not-included-by-default-anymore) in favor of consumers including the version they prefer. | Yes | +| **Language support** | Removed support for Java 8. The minimum required Java version is Java 11. | N/A | + +### First Steps + +Before you start, we suggest making a copy of your current working project or create a new branch with `git`. + +1. **Upgrade** Java to at least version 11. While version 11 is supported, we recommend using the [newest available LTS version](https://downloads.corretto.aws/#/downloads){target="\_blank"} of Java. +2. **Review** the following section to confirm if you need to make changes to your code. + +## Redesigned Logging Utility + +<!-- +- Add new logging module: https://github.com/aws-powertools/powertools-lambda-java/pull/1539 +- Add advanced features to new logging module: https://github.com/aws-powertools/powertools-lambda-java/pull/1435 +- Block reserved keys: https://github.com/aws-powertools/powertools-lambda-java/commit/374b38db3b91d421a14bb71b4df0194c72304efa +--> + +The logging utility was re-designed from scratch to integrate better with Java idiomatic conventions and to remove the hard dependency on `log4j` as logging implementation. The new logging utility now supports `slfj4` as logging interface and gives you the choice among `log4j2` and `logback` as logging implementations. Consider the following steps to migrate from the v1 logging utility to the v2 logging utility: + +**1. Remove `powertools-logging` dependency and replace it with your logging backend of choice** + +In order to support different logging implementations, dedicated logging modules were created for the different logging implementations. Remove `powertools-logging` as a dependency and replace it with either `powertools-logging-log4j` or `powertools-logging-logback`. + +```diff +<!-- BEFORE v2 --> +- <dependency> +- <groupId>software.amazon.lambda</groupId> +- <artifactId>powertools-logging</artifactId> +- <version>1.x.x</version> +- </dependency> + +<!-- AFTER v2 --> ++ <dependency> ++ <groupId>software.amazon.lambda</groupId> ++ <artifactId>powertools-logging-log4j</artifactId> ++ <version>2.x.x</version> ++ </dependency> +``` + +<!-- prettier-ignore-start --> +!!! info "The AspectJ configuration still needs to depend on `powertools-logging`" + We have only replaced the logging implementation dependency. The AspectJ configuration still needs to depend on `powertools-logging` which contains the main logic. + + ```xml + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + ``` +<!-- prettier-ignore-end --> + +**2. Update `log4j2.xml` including new `JsonTemplateLayout`** + +This step is only required if you are using log4j2 as your logging implementation. The deprecated `#!xml <LambdaJsonLayout/>` element was removed. Replace it with the log4j2 agnostic `#!xml <JsonTemplateLayout/>` element. + +```diff +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> +- <LambdaJsonLayout compact="true" eventEol="true"/> ++ <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender"/> + </Root> + </Loggers> +</Configuration> +``` + +**3. Migrate all logging specific calls to SLF4J native primitives (recommended)** + +The new logging utility is designed to integrate seamlessly with Java SLF4J to allow customers adopt the Logging utility without large code refactorings. This improvement requires the migration of non-native SLF4J primitives from the v1 Logging utility. + +!!! info "While we recommend using SLF4J as a logging implementation independent facade, you can still use the log4j2 and logback interfaces directly." + +Consider the following code example which gives you hints on how to achieve the same functionality between v1 and v2 Logging: + +```diff +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; +// ... other imports + +public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + // BEFORE v2: Uses org.apache.logging.log4j.LogManager +- private static final Logger LOGGER = LogManager.getLogger(PaymentFunction.class); + // AFTER v2: Use org.slf4j.LoggerFactory ++ private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + + // BEFORE v2: Uses LoggingUtils.appendKey to append custom global keys + // LoggingUtils was removed! +- LoggingUtils.appendKey("cardNumber", card.getId()); + // AFTER v2: Uses native SLF4J Mapped Diagnostic Context (MDC) ++ MDC.put("cardNumber", card.getId()); + + // Regular logging has not changed + LOGGER.info("My log message with argument."); + + // Adding custom keys on a specific log message + // BEFORE v2: No direct way, only supported via LoggingUtils.appendKey and LoggingUtils.removeKey + // AFTER v2: Extensive support for StructuredArguments ++ LOGGER.info("Collecting payment", StructuredArguments.entry("orderId", order.getId())); + // { "message": "Collecting payment", ..., "orderId": 123} + Map<String, String> customKeys = new HashMap<>(); + customKeys.put("paymentId", payment.getId()); + customKeys.put("amount", payment.getAmount); ++ LOGGER.info("Payment successful", StructuredArguments.entries(customKeys)); + // { "message": "Payment successful", ..., "paymentId": 123, "amount": 12.99} + } +} +``` + +!!! info "Make sure to learn more about the advanced structured argument serialization features in the [Logging v2 documentation](./core/logging.md/#custom-keys)." + +## Updated Metrics utility interface + +<!-- - Remove deprecated methods: https://github.com/aws-powertools/powertools-lambda-java/pull/1624/files#diff-0afede8005aa2baeba2770f66d611bf0e8ee3969205be27e803682a7f2d6520a --> +<!-- - Re-designed metrics module: https://github.com/aws-powertools/powertools-lambda-java/issues/1848 --> + +The Metrics utility was redesigned to be more modular and allow for the addition of new metrics providers in the future. The same EMF-based metrics logging still applies but will be called via an updated public interface. Consider the following list to understand some of changes: + +- `#!java @Metrics` was renamed to `#!java @FlushMetrics` +- `#!java MetricsLogger.metricsLogger()` was renamed to `#!java MetricsFactory.getMetricsInstance()` +- `put*` calls such as `#!java putMetric()` where replaced with `add*` nomenclature such as `#!java addMetric()` +- All direct imports from `software.amazon.cloudwatchlogs.emf` need to be replaced with Powertools counterparts from `software.amazon.lambda.powertools.metrics` (see example below) +- The `withSingleMetric` and `withMetricsLogger` methods were removed in favor of `#!java metrics.flushSingleMetric()` +- It is no longer valid to skip declaration of a namespace. If no namespace is provided, an exception will be raised instead of using the default `aws-embedded-metrics` namespace. + +The following example shows a common Lambda handler using the Metrics utility and required refactorings. + +```diff +// Metrics is not a decorator anymore but the replacement for the `MetricsLogger` Singleton +import software.amazon.lambda.powertools.metrics.Metrics; ++ import software.amazon.lambda.powertools.metrics.FlushMetrics; +- import software.amazon.lambda.powertools.metrics.MetricsUtils; ++ import software.amazon.lambda.powertools.metrics.MetricsFactory; +- import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; +- import software.amazon.cloudwatchlogs.emf.model.DimensionSet; +- import software.amazon.cloudwatchlogs.emf.model.Unit; ++ import software.amazon.lambda.powertools.metrics.model.DimensionSet; ++ import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + // This is still a Singleton +- MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); ++ Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override +- @Metrics(namespace = "ExampleApplication", service = "booking") ++ @FlushMetrics(namespace = "ExampleApplication", service = "booking") + public Object handleRequest(Object input, Context context) { +- metricsLogger.putDimensions(DimensionSet.of("environment", "prod")); ++ metrics.addDimension(DimensionSet.of("environment", "prod")); + // New method overload for adding 2D dimensions more conveniently ++ metrics.addDimension("environment", "prod"); +- metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT); ++ metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + ... + } +} +``` + +Learn more about the redesigned Metrics utility in the [Metrics documentation](./core/metrics.md). + +## Deprecated capture mode related `@Tracing` annotation parameters + +<!-- - Remove deprecated methods: https://github.com/aws-powertools/powertools-lambda-java/pull/1624/files#diff-9b8ed4ca67e310d3ae90e61e2ceffbfec0402082b5a1f741d467f132e3370a21 --> + +The deprecated `captureError` and `captureResponse` arguments to the `@Tracing` annotation were removed in v2 and replaced by a new `captureMode` parameter. The parameter can be passed an Enum value of `CaptureMode`. + +You should update your code using the new `captureMode` argument: + +```diff +- @Tracing(captureError = false, captureResponse = false) ++ @Tracing(captureMode = CaptureMode.DISABLED) +public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + // ... +} +``` + +Learn more about valid `CaptureMode` values in the [Tracing documentation](./core/tracing.md). + +## Idempotency utility split into sub-modules by provider + +The Idempotency utility was split from the common `powertools-idempotency` package into individual packages for different persistence store providers. The main business logic is now in the `powertools-idempotency-core` package. + +You should now include the `powertools-idempotency-core` package as an AspectJ library and the provider package like `powertools-idempotency-dynamodb` as a regular dependency. + +```diff +<!-- BEFORE v2 --> +- <dependency> +- <groupId>software.amazon.lambda</groupId> +- <artifactId>powertools-idempotency</artifactId> +- <version>1.x.x</version> +- </dependency> +<!-- AFTER v2 --> +<!-- In dependencies section --> ++ <dependency> ++ <groupId>software.amazon.lambda</groupId> ++ <artifactId>powertools-idempotency-dynamodb</artifactId> ++ <version>2.x.x</version> ++ </dependency> +<!-- In AspectJ configuration section --> ++ <aspectLibrary> ++ <groupId>software.amazon.lambda</groupId> ++ <artifactId>powertools-idempotency-core</artifactId> ++ </aspectLibrary> +``` + +## Parameters utility split into sub-modules by provider + +Parameters utilities were split from the common `powertools-parameters` package into individual packages for different parameter providers. You should now include the specific parameters dependency for your provider. If you use multiple providers, you can include multiple packages. Each parameter provider needs to be included as a dependency and an AspectJ library to use annotations. + +This new structure reduces the bundle size of your deployment package. + +```diff +<!-- BEFORE v2 --> +<!-- In dependencies section --> +- <dependency> +- <groupId>software.amazon.lambda</groupId> +- <artifactId>powertools-parameters</artifactId> +- <version>1.x.x</version> +- </dependency> +<!-- In AspectJ configuration section --> +- <aspectLibrary> +- <groupId>software.amazon.lambda</groupId> +- <artifactId>powertools-parameters</artifactId> +- </aspectLibrary> +<!-- AFTER v2 --> +<!-- In dependencies section --> ++ <dependency> ++ <groupId>software.amazon.lambda</groupId> ++ <artifactId>powertools-parameters-secrets</artifactId> ++ <version>2.x.x</version> ++ </dependency> +<!-- ... your other providers --> +<!-- In AspectJ configuration section --> ++ <aspectLibrary> ++ <groupId>software.amazon.lambda</groupId> ++ <artifactId>powertools-parameters-secrets</artifactId> ++ </aspectLibrary> +<!-- ... your other providers --> +``` + +!!! info "Find the full list of supported providers in the [Parameters utility documentation](./utilities/parameters.md)." + +## Custom Resources updates the `Response` class + +<!-- - Remove deprecated methods: https://github.com/aws-powertools/powertools-lambda-java/pull/1624/files#diff-0afede8005aa2baeba2770f66d611bf0e8ee3969205be27e803682a7f2d6520a --> + +The `Response` class supporting CloudFormation Custom Resource implementations was updated to remove deprecated methods. + +The `#!java Response.failed()` and `#!java Response.success()` methods without parameters were removed and require the physical resource ID now. You should update your code to use: + +- `#!java Response.failed(String physicalResourceId)` +- `#!java Response.success(String physicalResourceId)` + +```diff +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler; +import software.amazon.lambda.powertools.cloudformation.Response; + +public class MyCustomResourceHandler extends AbstractCustomResourceHandler { + + // ... + + @Override + protected Response update(CloudFormationCustomResourceEvent updateEvent, Context context) { ++ String physicalResourceId = updateEvent.getPhysicalResourceId(); + UpdateResult updateResult = doUpdates(physicalResourceId); + if (updateResult.isSuccessful()) { +- return Response.success(); ++ return Response.success(physicalResourceId); + } else { +- return Response.failed(); ++ return Response.failed(physicalResourceId); + } + } + + // ... +} +``` + +## Improved integration of Validation utility with other utilities + +<!-- +- Partial failure batch validation: https://github.com/aws-powertools/powertools-lambda-java/pull/1621 +- Return 4xx errors codes with API GW: https://github.com/aws-powertools/powertools-lambda-java/pull/1489 +--> + +The Validation utility includes two updates that change the behavior of integration with other utilities and AWS services. + +**1. Updated HTTP status code when using `@Validation` with API Gateway** + +This does not require a code change in the Lambda function using the Validation utility but might impact how your calling application treats exceptions. Prior to `v2`, a 500 HTTP status code was returned when the validation did not pass. Consistent with the [HTTP specification](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status){target="\_blank"}, a 400 status code is returned now indicating a user error instead of a server error. + +Consider the following example: + +```java +import software.amazon.lambda.powertools.validation.Validation; + +public class MyFunctionHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Override + @Validation(inboundSchema = "classpath:/schema_in.json", outboundSchema = "classpath:/schema_out.json") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + // ... + return something; + } +} +``` + +If the request validation fails, you can expect the following change in the HTTP response status code on the client-side: + +```sh +# BEFORE v2: 500 Internal Server Error +❯ curl -s -o /dev/null -w "%{http_code}" https://{API_ID}.execute-api.{REGION}.amazonaws.com/{STAGE}/{PATH} +500 +# AFTER v2: 400 Bad Request +❯ curl -s -o /dev/null -w "%{http_code}" https://{API_ID}.execute-api.{REGION}.amazonaws.com/{STAGE}/{PATH} +400 +``` + +**2. Integration with partial batch failures when using Batch utility** + +This does not require a code change but might affect the batch processing flow when using the Validation utility in combination with the Batch processing utility. + +Consider the following example: + +```java +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + +public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } + + @Override + @Validation(inboundSchema = "classpath:/schema_in.json", outboundSchema = "classpath:/schema_out.json") + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); + } + + private void processMessage(Product p, Context c) { + // Process the product + } +} +``` + +- **Prior to `v2`** this caused the whole batch to fail. +- **After `v2`** this will add only the failed events to the batch item failure list in the response and process the remaining messages. + +!!! info "Check if your workload can tolerate this behavior and make sure it is designed for idempotency when using partial batch item failures. We offer the [Idempotency](./utilities/idempotency.md) utility to simplify integration of idempotent behavior in your workloads." + +## AspectJ runtime not included by default anymore + +The AspectJ runtime is no longer included as a transitive dependency of Powertools. For all utilities offering annotations using AspectJ compile-time weaving, you need to include the AspectJ runtime yourself now. This is also documented, with a complete example, in our [installation guide](./index.md). For Maven projects, make sure to add the following dependency in your dependencies section: + +```diff ++ <dependency> ++ <groupId>org.aspectj</groupId> ++ <artifactId>aspectjrt</artifactId> ++ <version>1.9.22</version> ++ </dependency> +``` + +## Removed `powertools-sqs` module in favor of `powertools-batch` + +The archived documentation contains a migration guide for both large message handling using `powertools-sqs` and batch processing using `powertools-sqs`. The sections below explain the high-level steps for your convenience. + +### Migrating SQS Batch processing (`@SqsBatch`) + +The [batch processing library](./utilities/batch.md) provides a way to process messages and gracefully handle partial failures for SQS, Kinesis Streams, and DynamoDB Streams batch sources. In comparison to the legacy SQS Batch library, it relies on [Lambda partial batch responses](https://docs.aws.amazon.com/lambda/latest/dg/services-sqs-errorhandling.html#services-sqs-batchfailurereporting){target="\_blank"}, which allows the library to provide a simpler, more reliable interface for processing batches. + +In order to get started, check out the new [processing messages from SQS](./utilities/batch.md/#processing-messages-from-sqs) documentation. In most cases, you will simply be able to retain your existing batch message handler function, and wrap it with the new batch processing interface. Unlike the `powertools-sqs` module, the new `powertools-batch` module uses _partial batch responses_ to communicate to Lambda which messages have been processed and must be removed from the queue. The return value of the handler's process function must be returned to Lambda. + +The new library also no longer requires the `SQS:DeleteMessage` action on the Lambda function's role policy, as Lambda +itself now manages removal of messages from the queue. + +<!-- prettier-ignore-start --> +!!! info "Some tuneables from `powertools-sqs` are no longer provided." + - **Non-retryable Exceptions** - there is no mechanism to indicate in a partial batch response that a particular message + should not be retried and instead moved to DLQ - a message either succeeds, or fails and is retried. A message + will be moved to the DLQ once the normal retry process has expired. + - **Suppress Exception** - The new batch processor does not throw an exception on failure of a handler. Instead, + its result must be returned by your code from your message handler to Lambda, so that Lambda can manage + the completed messages and retry behaviour. +<!-- prettier-ignore-end --> + +### Migrating SQS Large message handling (`@SqsLargeMessage`) + +- Replace the dependency in Maven / Gradle: `powertools-sqs` ==> `powertools-large-messages` +- Replace the annotation: `@SqsLargeMessage` ==> `@LargeMessage` (the new module handles both SQS and SNS) +- Move the annotation away from the Lambda `handleRequest` method and put it on a method with `SQSEvent.SQSMessage` or `SNSEvent.SNSRecord` as first parameter. +- The annotation now handles a single message, contrary to the previous version that was handling the complete batch. This gives more control, especially when dealing with partial failures with SQS (see the batch module). +- The new module only provides an annotation: an equivalent to the `SqsUtils` class is not available anymore in this new version. diff --git a/docs/usage-patterns.md b/docs/usage-patterns.md new file mode 100644 index 000000000..e66538937 --- /dev/null +++ b/docs/usage-patterns.md @@ -0,0 +1,183 @@ +--- +title: Usage patterns +description: Getting to know the Powertools for AWS Lambda toolkit +--- + +<!-- markdownlint-disable MD043 --> + +Powertools for AWS Lambda (Java) is a collection of utilities designed to help you build serverless applications on AWS. + +The toolkit is modular, so you can pick and choose the utilities you need for your application, but also combine them for a complete solution for your serverless applications. + +## Patterns + +Many of the utilities provided can be used with different patterns, depending on your preferences and the structure of your code. + +### AspectJ Annotation + +If you prefer using annotations to apply cross-cutting concerns to your Lambda handlers, the AspectJ annotation pattern is a good fit. This approach lets you decorate methods with Powertools utilities using annotations, applying their functionality with minimal code changes. + +This pattern works well when you want to keep your business logic clean and separate concerns using aspect-oriented programming. + +<!-- prettier-ignore --> +!!! note + This approach requires configuring AspectJ compile-time weaving in your build tool (Maven or Gradle). See the [installation guide](./index.md#install) for setup instructions. + +=== "Logging" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + import software.amazon.lambda.powertools.logging.Logging; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(App.class); + + @Logging(logEvent = true, correlationIdPath = CorrelationIdPaths.API_GATEWAY_REST) + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + log.info("Processing request"); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +=== "Metrics" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @FlushMetrics(namespace = "ServerlessApp", service = "payment") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +=== "Tracing" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.tracing.Tracing; + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Tracing + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.putAnnotation("operation", "payment"); + return processPayment(); + } + + @Tracing + private APIGatewayProxyResponseEvent processPayment() { + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +### Functional Approach + +If you prefer a more functional programming style or want to avoid AspectJ configuration, you can use the Powertools for AWS Lambda (Java) utilities directly in your code. This approach is more explicit and provides full control over how the utilities are applied. + +This pattern is ideal when you want to avoid AspectJ setup or prefer a more imperative style. It also eliminates the AspectJ runtime dependency, making your deployment package more lightweight. + +=== "Logging" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(App.class); + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + return PowertoolsLogging.withLogging( + context, + 0.7, + CorrelationIdPaths.API_GATEWAY_REST, + input, + () -> processRequest(input)); + } + + private APIGatewayProxyResponseEvent processRequest(APIGatewayProxyRequestEvent input) { + // do something with input + log.info("Processing request"); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +=== "Metrics" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + try { + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } finally { + metrics.flush(); + } + } + } + ``` + +=== "Tracing" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("processPayment", subsegment -> { + subsegment.putAnnotation("operation", "payment"); + // Business logic here + }); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +<!-- prettier-ignore --> +!!! note + The functional approach is available for all utilities. Further examples and detailed usage can be found in the individual documentation pages for each utility. diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 6b38b438c..b535a90f6 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -30,6 +30,7 @@ stateDiagram-v2 * Reports batch item failures to reduce number of retries for a record upon errors * Simple interface to process each batch record +* Parallel processing of batches * Integrates with Java Events library and the deserialization module * Build your own batch processor by extending primitives @@ -110,16 +111,9 @@ You can use your preferred deployment framework to set the correct configuration while the `powertools-batch` module handles generating the response, which simply needs to be returned as the result of your Lambda handler. -A complete [Serverless Application Model](https://aws.amazon.com/serverless/sam/) example can be found -[here](https://github.com/aws-powertools/powertools-lambda-java/tree/main/examples/powertools-examples-batch) covering -all of the batch sources. - -For more information on configuring `ReportBatchItemFailures`, -see the details for [SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting), -[Kinesis](https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html#services-kinesis-batchfailurereporting),and -[DynamoDB Streams](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-batchfailurereporting). - +A complete [Serverless Application Model](https://aws.amazon.com/serverless/sam/) example can be found [here](https://github.com/aws-powertools/powertools-lambda-java/tree/main/examples/powertools-examples-batch) covering all the batch sources. +For more information on configuring `ReportBatchItemFailures`, see the details for [SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting), [Kinesis](https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html#services-kinesis-batchfailurereporting), and [DynamoDB Streams](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-batchfailurereporting). !!! note "You do not need any additional IAM permissions to use this utility, except for what each event source requires." @@ -150,12 +144,10 @@ see the details for [SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs. public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { return handler.processBatch(sqsEvent, context); } - - + private void processMessage(Product p, Context c) { // Process the product } - } ``` @@ -276,7 +268,6 @@ see the details for [SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs. private void processMessage(Product p, Context c) { // process the product } - } ``` @@ -475,6 +466,156 @@ see the details for [SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs. } ``` +## Parallel processing +You can choose to process batch items in parallel using the `BatchMessageHandler#processBatchInParallel()` +instead of `BatchMessageHandler#processBatch()`. Partial batch failure works the same way but items are processed +in parallel rather than sequentially. + +This feature is available for SQS, Kinesis and DynamoDB Streams but cannot be +used with SQS FIFO. In that case, an `UnsupportedOperationException` is thrown. + +!!! warning + Note that parallel processing is not always better than sequential processing, + and you should benchmark your code to determine the best approach for your use case. + +!!! info + To get more threads available (more vCPUs), you need to increase the amount of memory allocated to your Lambda function. + While it is possible to increase the number of threads using Java options or custom thread pools, + in most cases the defaults work well, and changing them is more likely to decrease performance + (see [here](https://www.baeldung.com/java-when-to-use-parallel-stream#fork-join-framework) + and [here](https://dzone.com/articles/be-aware-of-forkjoinpoolcommonpool)). + In situations where this may be useful, such as performing IO-bound work in parallel, make sure to measure before and after! + +When using parallel processing with X-Ray tracing enabled, the Tracing utility automatically handles trace context propagation to worker threads. This ensures that subsegments created during parallel message processing appear under the correct parent segment in your X-Ray trace, maintaining proper trace hierarchy and visibility into your batch processing performance. + + +=== "Example with SQS" + + ```java hl_lines="13" + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatchInParallel(sqsEvent, context); + } + + private void processMessage(Product p, Context c) { + // Process the product + } + } + ``` +=== "Example with SQS (using custom executor)" + + ```java hl_lines="4 10 15" + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + private final ExecutorService executor; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + executor = Executors.newFixedThreadPool(2); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatchInParallel(sqsEvent, context, executor); + } + + private void processMessage(Product p, Context c) { + // Process the product + } + } + ``` + +=== "Example with X-Ray Tracing" + + ```java hl_lines="12 17" + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } + + @Override + @Tracing + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatchInParallel(sqsEvent, context); + } + + @Tracing // This will appear correctly under the handleRequest subsegment + private void processMessage(Product p, Context c) { + // Process the product - subsegments will appear under handleRequest + } + } + ``` + +### Choosing the right concurrency model + +The `processBatchInParallel` method has two overloads with different concurrency characteristics: + +#### Without custom executor (parallelStream) + +When you call `processBatchInParallel(event, context)` without providing an executor, the implementation uses Java's `parallelStream()` which leverages the common `ForkJoinPool`. + +**Best for: CPU-bound workloads** + +- Thread pool size matches available CPU cores +- Optimized for computational tasks (data transformation, calculations, parsing) +- Main thread participates in work-stealing +- Simple to use with no configuration needed + +```java +// Good for CPU-intensive processing +return handler.processBatchInParallel(sqsEvent, context); +``` + +#### With custom executor (CompletableFuture) + +When you call `processBatchInParallel(event, context, executor)` with a custom executor, the implementation uses `CompletableFuture` which gives you full control over the thread pool. + +**Best for: I/O-bound workloads** + +- You control thread pool size and characteristics +- Ideal for I/O operations (HTTP calls, database queries, S3 operations) +- Can use larger thread pools since threads spend time waiting, not computing +- Main thread only waits; worker threads do all processing + +```java +// Good for I/O-intensive processing (API calls, DB queries, etc.) +ExecutorService executor = Executors.newFixedThreadPool(50); +return handler.processBatchInParallel(sqsEvent, context, executor); +``` + +**For Java 21+: Virtual Threads** + +If you're using Java 21 or later, virtual threads are ideal for I/O-bound workloads: + +```java +ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); +return handler.processBatchInParallel(sqsEvent, context, executor); +``` + +Virtual threads are lightweight and can handle thousands of concurrent I/O operations efficiently without the overhead of platform threads. + +**Recommendation for typical Lambda SQS processing:** + +Most Lambda functions processing SQS messages perform I/O operations (calling APIs, querying databases, writing to S3). For these workloads, use the custom executor approach with a thread pool sized appropriately for your I/O operations or virtual threads for Java 21+. + ## Handling Messages @@ -490,7 +631,7 @@ In general, the deserialized message handler should be used unless you need acce === "Raw Message Handler" - ```java + ```java hl_lines="4 7" public void setup() { BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() .withSqsBatchHandler() @@ -505,7 +646,7 @@ In general, the deserialized message handler should be used unless you need acce === "Deserialized Message Handler" - ```java + ```java hl_lines="4 7" public void setup() { BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() .withSqsBatchHandler() @@ -529,20 +670,20 @@ provide a custom failure handler. Handlers can be provided when building the batch processor and are available for all event sources. For instance for DynamoDB: -```java +```java hl_lines="3 8" BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() - .withDynamoDbBatchHandler() - .withSuccessHandler((m) -> { - // Success handler receives the raw message - LOGGER.info("Message with sequenceNumber {} was successfully processed", - m.getDynamodb().getSequenceNumber()); - }) - .withFailureHandler((m, e) -> { - // Failure handler receives the raw message and the exception thrown. - LOGGER.info("Message with sequenceNumber {} failed to be processed: {}" - , e.getDynamodb().getSequenceNumber(), e); - }) - .buildWithMessageHander(this::processMessage); + .withDynamoDbBatchHandler() + .withSuccessHandler((m) -> { + // Success handler receives the raw message + LOGGER.info("Message with sequenceNumber {} was successfully processed", + m.getDynamodb().getSequenceNumber()); + }) + .withFailureHandler((m, e) -> { + // Failure handler receives the raw message and the exception thrown. + LOGGER.info("Message with sequenceNumber {} failed to be processed: {}" + , e.getDynamodb().getSequenceNumber(), e); + }) + .buildWithMessageHander(this::processMessage); ``` !!! info diff --git a/docs/utilities/custom_resources.md b/docs/utilities/custom_resources.md index d0c1eacf2..053e8a9d7 100644 --- a/docs/utilities/custom_resources.md +++ b/docs/utilities/custom_resources.md @@ -223,7 +223,7 @@ public class CustomSerializationHandler extends AbstractResourceHandler { While the library provides an easy-to-use interface, we recommend that you understand the lifecycle of CloudFormation custom resources before using them in production. #### Creating a custom resource -When CloudFormation issues a CREATE on a custom resource, there are 2 possible states: `CREATE_COMPLETE` and `CREATE_FAILED` +When CloudFormation issues a `CREATE` on a custom resource, there are 2 possible states: `CREATE_COMPLETE` and `CREATE_FAILED` ```mermaid stateDiagram direction LR @@ -237,7 +237,7 @@ If the resource is created successfully, the `physicalResourceId` is stored by C If the resource failed to create, CloudFormation triggers a rollback operation by default (rollback can be disabled, see [stack failure options](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stack-failure-options.html)) #### Updating a custom resource -CloudFormation issues an UPDATE operation on a custom resource only when one or more custom resource properties change. +CloudFormation issues an `UPDATE` operation on a custom resource only when one or more custom resource properties change. During the update, the custom resource may update successfully, or may fail the update. ```mermaid stateDiagram @@ -278,7 +278,8 @@ stateDiagram #### Deleting a custom resource -CloudFormation issues a DELETE on a custom resource when: +CloudFormation issues a `DELETE` on a custom resource when: + - the CloudFormation stack is being deleted - a new `physicalResourceId` was received during an update, and CloudFormation proceeds to rollback(DELETE) the custom resource with the previous `physicalResourceId`. @@ -289,4 +290,4 @@ stateDiagram [*] --> deleteState deleteState --> DELETE_COMPLETE deleteState --> DELETE_FAILED -``` \ No newline at end of file +``` diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 5392b8d4c..cecc65d7b 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -26,29 +26,29 @@ times with the same parameters**. This makes idempotent operations safe to retry ## Getting started ### Installation -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. -=== "Maven Java 11+" +=== "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 16 18 25-28" <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> <version>{{ powertools.version }}</version> </dependency> ... </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> <build> <plugins> ... <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>11</source> <!-- or higher --> <target>11</target> <!-- or higher --> @@ -56,10 +56,18 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-core</artifactId> </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -73,57 +81,12 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </build> ``` -=== "Maven Java 1.8" +=== "Gradle" - ```xml hl_lines="3-7 16 18 24-27" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" - - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -131,38 +94,19 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl } dependencies { - aspect 'software.amazon.lambda:powertools-idempotency:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-idempotency-core:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-idempotency-dynamodb:{{ powertools.version }}' } sourceCompatibility = 11 // or higher targetCompatibility = 11 // or higher ``` -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-idempotency:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - ### Required resources Before getting started, you need to create a persistent storage layer where the idempotency utility can store its state - your Lambda functions will need read and write access to it. -As of now, Amazon DynamoDB is the only supported persistent storage layer, so you'll need to create a table first. +As of now, Amazon DynamoDB is the only supported persistent storage layer, so you'll need to create a table first or [bring your own persistence store](#bring-your-own-persistent-store). **Default table configuration** @@ -202,33 +146,33 @@ Resources: TableName: !Ref IdempotencyTable Environment: Variables: - IDEMPOTENCY_TABLE: !Ref IdempotencyTable + TABLE_NAME: !Ref IdempotencyTable ``` !!! warning "Warning: Large responses with DynamoDB persistence layer" - When using this utility with DynamoDB, your function's responses must be [smaller than 400KB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html#limits-items). + When using this utility with DynamoDB, your function's responses must be [smaller than 400KB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Constraints.html#limits-items). Larger items cannot be written to DynamoDB and will cause exceptions. !!! info "Info: DynamoDB" - Each function invocation will generally make 2 requests to DynamoDB. If the - result returned by your Lambda is less than 1kb, you can expect 2 WCUs per invocation. For retried invocations, you will - see 1WCU and 1RCU. Review the [DynamoDB pricing documentation](https://aws.amazon.com/dynamodb/pricing/) to + Each function invocation will generally make 1 request to DynamoDB. If the + result returned by your Lambda is less than 1kb, you can expect 1 WCUs per invocation. For retried invocations, you will + see 1 WCU. In some cases, the utility might make 2 requests to DynamoDB in which case you will see 1 RCU and 1 WCU. Review the [DynamoDB pricing documentation](https://aws.amazon.com/dynamodb/pricing/) to estimate the cost. -### Idempotent annotation +### Basic usage -You can quickly start by initializing the `DynamoDBPersistenceStore` and using it with the `@Idempotent` annotation on your Lambda handler. +You can use Powertools for AWS Lambda Idempotency with either the `@Idempotent` annotation or the functional API. !!! warning "Important" Initialization and configuration of the `DynamoDBPersistenceStore` must be performed outside the handler, preferably in the constructor. -=== "App.java" +=== "@Idempotent annotation" ```java hl_lines="5-9 12 19" public class App implements RequestHandler<Subscription, SubscriptionResult> { public App() { - // we need to initialize idempotency store before the handleRequest method is called + // We need to initialize idempotency store before the handleRequest method is called Idempotency.config().withPersistenceStore( DynamoDBPersistenceStore.builder() .withTableName(System.getenv("TABLE_NAME")) @@ -249,6 +193,33 @@ You can quickly start by initializing the `DynamoDBPersistenceStore` and using i ``` +=== "Functional API" + + ```java hl_lines="5-9 13-14" + public class App implements RequestHandler<Subscription, SubscriptionResult> { + + public App() { + // We need to initialize idempotency store before the handleRequest method is called + Idempotency.config().withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build() + ).configure(); + } + + public SubscriptionResult handleRequest(final Subscription event, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processSubscription, event, SubscriptionResult.class); + } + + private SubscriptionResult processSubscription(Subscription event) { + SubscriptionPayment payment = createSubscriptionPayment(event.getUsername(), event.getProductId()); + return new SubscriptionResult(payment.getId(), "success", 200); + } + } + + ``` + === "Example event" ```json @@ -258,25 +229,32 @@ You can quickly start by initializing the `DynamoDBPersistenceStore` and using i } ``` -#### Idempotent annotation on another method +#### Making non-handler methods idempotent -You can use the `@Idempotent` annotation for any synchronous Java function, not only the `handleRequest` one. +You can make any synchronous Java function idempotent, not only the `handleRequest` handler. -When using `@Idempotent` annotation on another method, you must tell which parameter in the method signature has the data we should use: +**With the `@Idempotent` annotation**, you must specify which parameter contains the idempotency key: - If the method only has one parameter, it will be used by default. - If there are 2 or more parameters, you must set the `@IdempotencyKey` on the parameter to use. +**With the functional API**, you explicitly pass the idempotency key: + + - For single-parameter methods, use `Idempotency.makeIdempotent(this::method, param, ReturnType.class)` + - For multi-parameter methods, use `Idempotency.makeIdempotent(idempotencyKey, () -> method(param1, param2), ReturnType.class)` + !!! info "The parameter must be serializable in JSON. We use Jackson internally to (de)serialize objects" -=== "AppSqsEvent.java" +=== "@Idempotent annotation" This example also demonstrates how you can integrate with [Batch utility](batch.md), so you can process each record in an idempotent manner. - ```java hl_lines="19 23-25 30-31" - public class AppSqsEvent implements RequestHandler<SQSEvent, String> { + ```java hl_lines="6-15 17-19 27-28" + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { - public AppSqsEvent() { + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { Idempotency.config() .withPersistenceStore( DynamoDBPersistenceStore.builder() @@ -284,31 +262,66 @@ When using `@Idempotent` annotation on another method, you must tell which param .build() ).withConfig( IdempotencyConfig.builder() - .withEventKeyJMESPath("messageId") // see Choosing a payload subset section + .withEventKeyJMESPath("messageId") .build() ).configure(); - } + + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } @Override - @SqsBatch(SampleMessageHandler.class) - public String handleRequest(SQSEvent input, Context context) { - dummy("hello", "world"); - return "{\"statusCode\": 200}"; + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); } @Idempotent - private String dummy(String argOne, @IdempotencyKey String argTwo) { - return "something"; + private void processMessage(@IdempotencyKey SQSEvent.SQSMessage message) { + // Process message } + } + ``` + +=== "Functional API" + + This example also demonstrates how you can integrate with the [Batch utility](batch.md), so you can process each record in an idempotent manner. **Note: The JMESPath function still applies even when passing the idempotency key manually.** + + ```java hl_lines="6-15 17-19 24 29" + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { - public static class SampleMessageHandler implements SqsMessageHandler<Object> { - @Override - @Idempotent - // no need to use @IdempotencyKey as there is only one parameter - public String process(SQSMessage message) { - String returnVal = doSomething(message.getBody()); - return returnVal; - } + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build() + ).withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("messageId") + .build() + ).configure(); + + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + Idempotency.registerLambdaContext(context); + return handler.processBatch(sqsEvent, context); + } + + private void processMessage(SQSEvent.SQSMessage message) { + Idempotency.makeIdempotent(this::handleMessage, message, Void.class); + } + + private Void handleMessage(SQSEvent.SQSMessage message) { + // Process message + return null; } } ``` @@ -362,9 +375,9 @@ Imagine the function executes successfully, but the client never receives the re To alter this behaviour, you can use the [JMESPath built-in function](serialization.md#jmespath-functions) `powertools_json()` to treat the payload as a JSON object rather than a string. -=== "PaymentFunction.java" +=== "@Idempotent annotation" - ```java hl_lines="5-7 16 29-31" + ```java hl_lines="7 16" public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { public PaymentFunction() { @@ -402,6 +415,50 @@ Imagine the function executes successfully, but the client never receives the re } ``` +=== "Functional API" + + ```java hl_lines="7 17-18" + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public PaymentFunction() { + Idempotency.config() + .withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body)") + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent event, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processPayment, event, APIGatewayProxyResponseEvent.class); + } + + private APIGatewayProxyResponseEvent processPayment(APIGatewayProxyRequestEvent event) { + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent(); + + try { + Subscription subscription = JsonConfig.get().getObjectMapper().readValue(event.getBody(), Subscription.class); + + SubscriptionPayment payment = createSubscriptionPayment( + subscription.getUsername(), + subscription.getProductId() + ); + + return response + .withStatusCode(200) + .withBody(String.format("{\"paymentId\":\"%s\"}", payment.getId())); + + } catch (JsonProcessingException e) { + return response.withStatusCode(500); + } + } + ``` + === "Example event" ```json hl_lines="3" @@ -475,46 +532,82 @@ The client was successful in receiving the result after the retry. Since the Lam #### Lambda timeouts -This is automatically done when you annotate your Lambda handler with [@Idempotent annotation](#idempotent-annotation). - To prevent against extended failed retries when a [Lambda function times out](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-verify-invocation-timeouts/), Powertools for AWS Lambda (Java) calculates and includes the remaining invocation available time as part of the idempotency record. !!! example If a second invocation happens **after** this timestamp, and the record is marked as `INPROGRESS`, we will execute the invocation again as if it was in the `EXPIRED` state. This means that if an invocation expired during execution, it will be quickly executed again on the next retry. -!!! important - If you are using the [@Idempotent annotation on another method](#idempotent-annotation-on-another-method) to guard isolated parts of your code, you must use `registerLambdaContext` method available in the `Idempotency` object to benefit from this protection. +**With the `@Idempotent` annotation**, this is automatically done when you annotate your Lambda handler. + +**With the functional API** or when using the `@Idempotent` annotation on methods other than the handler, you must call `Idempotency.registerLambdaContext(context)` to benefit from this protection. +!!! important Here is an example on how you register the Lambda context in your handler: - ```java hl_lines="13-19" title="Registering the Lambda context" - public class PaymentHandler implements RequestHandler<SQSEvent, List<String>> { - - public PaymentHandler() { - Idempotency.config() - .withPersistenceStore( - DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("IDEMPOTENCY_TABLE")) - .build()) - .configure(); - } + === "@Idempotent annotation" + + ```java hl_lines="14" title="Registering the Lambda context" + public class PaymentHandler implements RequestHandler<SQSEvent, List<String>> { + + public PaymentHandler() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + @Override + public List<String> handleRequest(SQSEvent sqsEvent, Context context) { + Idempotency.registerLambdaContext(context); + return sqsEvent.getRecords().stream().map(record -> process(record.getMessageId(), record.getBody())).collect(Collectors.toList()); + } + + @Idempotent + private String process(String messageId, @IdempotencyKey String messageBody) { + logger.info("Processing messageId: {}", messageId); + PaymentRequest request = extractDataFrom(messageBody).as(PaymentRequest.class); + return paymentService.process(request); + } - @Override - public List<String> handleRequest(SQSEvent sqsEvent, Context context) { - Idempotency.registerLambdaContext(context); - return sqsEvent.getRecords().stream().map(record -> process(record.getMessageId(), record.getBody())).collect(Collectors.toList()); } - - @Idempotent - private String process(String messageId, @IdempotencyKey String messageBody) { - logger.info("Processing messageId: {}", messageId); - PaymentRequest request = extractDataFrom(messageBody).as(PaymentRequest.class); - return paymentService.process(request); + ``` + + === "Functional API" + + ```java hl_lines="14" title="Registering the Lambda context" + public class PaymentHandler implements RequestHandler<SQSEvent, List<String>> { + + public PaymentHandler() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + @Override + public List<String> handleRequest(SQSEvent sqsEvent, Context context) { + Idempotency.registerLambdaContext(context); + return sqsEvent.getRecords().stream() + .map(record -> Idempotency.makeIdempotent( + record.getBody(), + () -> process(record.getMessageId(), record.getBody()), + String.class)) + .collect(Collectors.toList()); + } + + private String process(String messageId, String messageBody) { + logger.info("Processing messageId: {}", messageId); + PaymentRequest request = extractDataFrom(messageBody).as(PaymentRequest.class); + return paymentService.process(request); + } + } - - } - ``` + ``` #### Lambda timeout sequence diagram @@ -557,9 +650,11 @@ sequenceDiagram ### Handling exceptions -If you are using the `@Idempotent` annotation on your Lambda handler or any other method, any unhandled exceptions that are thrown during the code execution will cause **the record in the persistence layer to be deleted**. +**With the `@Idempotent` annotation**, any unhandled exceptions that are thrown during the code execution will cause **the record in the persistence layer to be deleted**. This means that new invocations will execute your code again despite having the same payload. If you don't want the record to be deleted, you need to catch exceptions within the idempotent function and return a successful response. +**With the functional API**, exceptions are handled the same way - unhandled exceptions will cause the record to be deleted. You should catch and handle exceptions within your idempotent function if you want to preserve the record. + <center> ```mermaid sequenceDiagram @@ -611,7 +706,7 @@ If an Exception is raised _outside_ the scope of a decorated method and after yo This persistence store is built-in, and you can either use an existing DynamoDB table or create a new one dedicated for idempotency state (recommended). Use the builder to customize the table structure: -```java hl_lines="3-7" title="Customizing DynamoDBPersistenceStore to suit your table structure" +```java hl_lines="2-7" title="Customizing DynamoDBPersistenceStore to suit your table structure" DynamoDBPersistenceStore.builder() .withTableName(System.getenv("TABLE_NAME")) .withKeyAttr("idempotency_key") @@ -637,11 +732,68 @@ When using DynamoDB as a persistence layer, you can alter the attribute names by ## Advanced +### Using explicit function names + +When using the functional API, if you need to call different methods with the same payload as the idempotency key, you must provide explicit function names to differentiate between them. This ensures each function has its own idempotency scope. + +=== "Functional API with explicit names" + + ```java hl_lines="5-9 11-15" + public Response handleRequest(Order order, Context context) { + Idempotency.registerLambdaContext(context); + + // Same orderId, different operations - need explicit function names + Idempotency.makeIdempotent( + "processPayment", + order.getId(), + () -> processPayment(order), + PaymentResult.class); + + Idempotency.makeIdempotent( + "sendConfirmation", + order.getId(), + () -> sendEmail(order), + EmailResult.class); + + return new Response("success"); + } + ``` + +!!! note + When using the `@Idempotent` annotation, the function name is automatically inferred from the method name, so this is not needed. + +### Generic return types support + +The functional API supports making methods with generic return types idempotent using Jackson's `TypeReference`. This is not possible with the `@Idempotent` annotation due to type erasure. + +=== "Functional API with TypeReference" + + ```java hl_lines="1 6-10" + import com.fasterxml.jackson.core.type.TypeReference; + + public Map<String, Basket> handleRequest(Product input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent( + this::processProduct, + input, + new TypeReference<Map<String, Basket>>() {} + ); + } + + private Map<String, Basket> processProduct(Product product) { + // business logic returning generic type + Map<String, Basket> result = new HashMap<>(); + // ... + return result; + } + ``` + ### Customizing the default behavior Idempotency behavior can be further configured with **`IdempotencyConfig`** using a builder: -```java hl_lines="2-8" title="Customizing IdempotencyConfig" +```java hl_lines="2-9" title="Customizing IdempotencyConfig" IdempotencyConfig.builder() .withEventKeyJMESPath("id") .withPayloadValidationJMESPath("paymentId") @@ -650,6 +802,7 @@ IdempotencyConfig.builder() .withUseLocalCache(true) .withLocalCacheMaxItems(432) .withHashFunction("SHA-256") + .withResponseHook((responseData, dataRecord) -> responseData) .build() ``` @@ -657,13 +810,14 @@ These are the available options for further configuration: | Parameter | Default | Description | |---------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------| -| **EventKeyJMESPath** | `""` | JMESPath expression to extract the idempotency key from the event record. See available [built-in functions](serialization) | +| **EventKeyJMESPath** | `""` | JMESPath expression to extract the idempotency key from the event record. See available [built-in functions](serialization) | | **PayloadValidationJMESPath** | `""` | JMESPath expression to validate whether certain parameters have changed in the event | -| **ThrowOnNoIdempotencyKey** | `False` | Throw exception if no idempotency key was found in the request | +| **ThrowOnNoIdempotencyKey** | `false` | Throw exception if no idempotency key was found in the request | | **ExpirationInSeconds** | 3600 | The number of seconds to wait before a record is expired | | **UseLocalCache** | `false` | Whether to locally cache idempotency results (LRU cache) | | **LocalCacheMaxItems** | 256 | Max number of items to store in local cache | | **HashFunction** | `MD5` | Algorithm to use for calculating hashes, as supported by `java.security.MessageDigest` (eg. SHA-1, SHA-256, ...) | +| **ResponseHook** | `null` | Response hook to apply modifications to idempotent responses | These features are detailed below. @@ -723,7 +877,7 @@ By default, we will return the same result as it returned before, however in thi With **`PayloadValidationJMESPath`**, you can provide an additional JMESPath expression to specify which part of the event body should be validated against previous idempotent invocations -=== "App.java" +=== "@Idempotent annotation" ```java hl_lines="8 13 20 26" public App() { @@ -756,6 +910,43 @@ With **`PayloadValidationJMESPath`**, you can provide an additional JMESPath exp } ``` +=== "Functional API" + + ```java hl_lines="8 14-15 24 30" + public App() { + Idempotency.config() + .withPersistenceStore(DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("[userDetail, productId]") + .withPayloadValidationJMESPath("amount") + .build()) + .configure(); + } + + public SubscriptionResult handleRequest(final Subscription input, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processSubscription, input, SubscriptionResult.class); + } + + private SubscriptionResult processSubscription(Subscription input) { + // Creating a subscription payment is a side + // effect of calling this function! + SubscriptionPayment payment = createSubscriptionPayment( + input.getUserDetail().getUsername(), + input.getProductId(), + input.getAmount() + ) + // ... + return new SubscriptionResult( + "success", 200, + payment.getId(), + payment.getAmount() + ); + } + ``` + === "Example Event 1" ```json hl_lines="8" @@ -801,9 +992,9 @@ This means that we will throw **`IdempotencyKeyException`** if the evaluation of When set to `false` (the default), if the idempotency key is null, then the data is not persisted in the store. -=== "App.java" +=== "@Idempotent annotation" - ```java hl_lines="9-10 13" + ```java hl_lines="9" public App() { Idempotency.config() .withPersistenceStore(DynamoDBPersistenceStore.builder() @@ -823,6 +1014,32 @@ When set to `false` (the default), if the idempotency key is null, then the data } ``` +=== "Functional API" + + ```java hl_lines="9" + public App() { + Idempotency.config() + .withPersistenceStore(DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .withConfig(IdempotencyConfig.builder() + // Requires "user"."uid" and "orderId" to be present + .withEventKeyJMESPath("[user.uid, orderId]") + .withThrowOnNoIdempotencyKey(true) + .build()) + .configure(); + } + + public OrderResult handleRequest(final Order input, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processOrder, input, OrderResult.class); + } + + private OrderResult processOrder(Order input) { + // ... + } + ``` + === "Success Event" ```json hl_lines="3 6" @@ -921,6 +1138,58 @@ You can extend the `BasePersistenceStore` class and implement the abstract metho For example, the `putRecord` method needs to throw an exception if a non-expired record already exists in the data store with a matching key. +### Manipulating the Idempotent Response + +You can set up a response hook in the Idempotency configuration to manipulate the returned data when an operation is idempotent. The hook function will be called with the current de-serialized response `Object` and the Idempotency `DataRecord`. + +The example below shows how to append an HTTP header to an `APIGatewayProxyResponseEvent`. + +=== "Using an Idempotent Response Hook" + + ```java hl_lines="3-20" + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withResponseHook((responseData, dataRecord) -> { + if (responseData instanceof APIGatewayProxyResponseEvent) { + APIGatewayProxyResponseEvent proxyResponse = + (APIGatewayProxyResponseEvent) responseData; + final Map<String, String> headers = new HashMap<>(); + headers.putAll(proxyResponse.getHeaders()); + // Append idempotency headers + headers.put("x-idempotency-response", "true"); + headers.put("x-idempotency-expiration", + String.valueOf(dataRecord.getExpiryTimestamp())); + + proxyResponse.setHeaders(headers); + + return proxyResponse; + } + + return responseData; + }) + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + ``` + +???+ info "Info: Using custom de-serialization?" + + The response hook is called after de-serialization so the payload you process will be the de-serialized Java object. + +#### Being a good citizen + +When using response hooks to manipulate returned data from idempotent operations, it's important to follow best practices to avoid introducing complexity or issues. Keep these guidelines in mind: + +1. **Response hook works exclusively when operations are idempotent.** The hook will not be called when an operation is not idempotent, or when the idempotent logic fails. + +2. **Catch and Handle Exceptions.** Your response hook code should catch and handle any exceptions that may arise from your logic. Unhandled exceptions will cause the Lambda function to fail unexpectedly. + +3. **Keep Hook Logic Simple** Response hooks should consist of minimal and straightforward logic for manipulating response data. Avoid complex conditional branching and aim for hooks that are easy to reason about. + + ## Compatibility with other utilities ### Validation utility @@ -981,13 +1250,15 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "pom.xml" - ```xml hl_lines="4-6 24-26 28-31 42 45-47" + ```xml <dependencies> <!-- maven dependency for DynamoDB local --> <dependency> <groupId>com.amazonaws</groupId> <artifactId>DynamoDBLocal</artifactId> - <version>[1.12,2.0)</version> + <!-- Use newest version if you are on Java >11 --> + <!-- https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocalHistory.html --> + <version>2.2.0</version> <scope>test</scope> </dependency> <!-- Needed when building locally on M1 Mac --> @@ -1020,7 +1291,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ </systemPropertyVariables> <!-- environment variables for the tests --> <environmentVariables> - <IDEMPOTENCY_TABLE_NAME>idempotency</IDEMPOTENCY_TABLE_NAME> + <TABLE_NAME>idempotency</TABLE_NAME> <AWS_REGION>eu-central-1</AWS_REGION> </environmentVariables> </configuration> @@ -1048,7 +1319,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "AppTest.java" - ```java hl_lines="13-18 24-30 34" + ```java public class AppTest { @Mock private Context context; @@ -1124,7 +1395,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ public App(DynamoDbClient ddbClient) { Idempotency.config().withPersistenceStore( DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("IDEMPOTENCY_TABLE_NAME")) + .withTableName(System.getenv("TABLE_NAME")) .withDynamoDbClient(ddbClient) .build() ).configure(); @@ -1145,7 +1416,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "App.java" - ```java hl_lines="8 9 16" + ```java public class App implements RequestHandler<Subscription, SubscriptionResult> { public App() { @@ -1161,7 +1432,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ Idempotency.config().withPersistenceStore( DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("IDEMPOTENCY_TABLE_NAME")) + .withTableName(System.getenv("TABLE_NAME")) .withDynamoDbClient(ddbBuilder.build()) .build() ).configure(); @@ -1176,7 +1447,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "shell" - ```shell hl_lines="2 6 7 12 16 21 22" + ```shell # use or create a docker network docker network inspect sam-local || docker network create sam-local @@ -1203,10 +1474,10 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "env.json" - ```json hl_lines="3" + ```json { "IdempotentFunction": { - "IDEMPOTENCY_TABLE_NAME": "idempotency" + "TABLE_NAME": "idempotency" } } ``` diff --git a/docs/utilities/kafka.md b/docs/utilities/kafka.md new file mode 100644 index 000000000..da179bc5c --- /dev/null +++ b/docs/utilities/kafka.md @@ -0,0 +1,1001 @@ +--- +title: Kafka Consumer +description: Utility +status: new +--- + +<!-- markdownlint-disable MD043 --> + +The Kafka utility transparently handles message deserialization, provides an intuitive developer experience, and integrates seamlessly with the rest of the Powertools for AWS Lambda ecosystem. + +```mermaid +flowchart LR + KafkaTopic["Kafka Topic"] --> MSK["Amazon MSK"] + KafkaTopic --> MSKServerless["Amazon MSK Serverless"] + KafkaTopic --> SelfHosted["Self-hosted Kafka"] + MSK --> EventSourceMapping["Event Source Mapping"] + MSKServerless --> EventSourceMapping + SelfHosted --> EventSourceMapping + EventSourceMapping --> Lambda["Lambda Function"] + Lambda --> KafkaUtility["Kafka Utility"] + KafkaUtility --> Deserialization["Deserialization"] + Deserialization --> YourLogic["Your Business Logic"] +``` + +## Key features + +- Automatic deserialization of Kafka messages (JSON, Avro, and Protocol Buffers) +- Simplified event record handling with familiar Kafka `ConsumerRecords` interface +- Support for key and value deserialization +- Support for ESM with and without Schema Registry integration +- Proper error handling for deserialization issues + +## Terminology + +**Event Source Mapping (ESM)** A Lambda feature that reads from streaming sources (like Kafka) and invokes your Lambda function. It manages polling, batching, and error handling automatically, eliminating the need for consumer management code. + +**Record Key and Value** A Kafka messages contain two important parts: an optional key that determines the partition and a value containing the actual message data. Both are base64-encoded in Lambda events and can be independently deserialized. + +**Deserialization** Is the process of converting binary data (base64-encoded in Lambda events) into usable Java objects according to a specific format like JSON, Avro, or Protocol Buffers. Powertools handles this conversion automatically. + +**DeserializationType enum** Contains parameters that tell Powertools how to interpret message data, including the format type (JSON, Avro, Protocol Buffers). + +**Schema Registry** Is a centralized service that stores and validates schemas, ensuring producers and consumers maintain compatibility when message formats evolve over time. + +## Moving from traditional Kafka consumers + +Lambda processes Kafka messages as discrete events rather than continuous streams, requiring a different approach to consumer development that Powertools for AWS helps standardize. + +| Aspect | Traditional Kafka Consumers | Lambda Kafka Consumer | +| --------------------- | ----------------------------------- | -------------------------------------------------------------- | +| **Model** | Pull-based (you poll for messages) | Push-based (Lambda invoked with messages) | +| **Scaling** | Manual scaling configuration | Automatic scaling to partition count | +| **State** | Long-running application with state | Stateless, ephemeral executions | +| **Offsets** | Manual offset management | Automatic offset commitment | +| **Schema Validation** | Client-side schema validation | Optional Schema Registry integration with Event Source Mapping | +| **Error Handling** | Per-message retry control | Batch-level retry policies | + +## Getting started + +### Installation + +Add the Powertools for AWS Lambda Kafka dependency to your project. Make sure to also add the `kafka-clients` library as a dependency. The utility supports `kafka-clients >= 3.0.0`. + +=== "Maven" + + ```xml + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-kafka</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + <!-- Kafka clients dependency - compatibility works for >= 3.0.0 --> + <dependency> + <groupId>org.apache.kafka</groupId> + <artifactId>kafka-clients</artifactId> + <version>4.0.0</version> + </dependency> + ``` + +=== "Gradle" + + ```gradle + dependencies { + implementation 'software.amazon.lambda:powertools-kafka:{{ powertools.version }}' + // Kafka clients dependency - compatibility works for >= 3.0.0 + implementation 'org.apache.kafka:kafka-clients:4.0.0' + } + ``` + +### Required resources + +To use the Kafka utility, you need an AWS Lambda function configured with a Kafka event source. This can be Amazon MSK, MSK Serverless, or a self-hosted Kafka cluster. + +=== "getting_started_with_msk.yaml" + + ```yaml + AWSTemplateFormatVersion: '2010-09-09' + Transform: AWS::Serverless-2016-10-31 + Resources: + KafkaConsumerFunction: + Type: AWS::Serverless::Function + Properties: + Handler: org.example.KafkaHandler::handleRequest + Runtime: java21 + Timeout: 30 + Events: + MSKEvent: + Type: MSK + Properties: + StartingPosition: LATEST + Stream: !GetAtt MyMSKCluster.Arn + Topics: + - my-topic-1 + - my-topic-2 + Policies: + - AWSLambdaMSKExecutionRole + ``` + +### Using ESM with Schema Registry + +The Event Source Mapping configuration determines which mode is used. With `JSON`, Lambda converts all messages to JSON before invoking your function. With `SOURCE` mode, Lambda preserves the original format, requiring you function to handle the appropriate deserialization. + +Powertools for AWS supports both Schema Registry integration modes in your Event Source Mapping configuration. + +### Processing Kafka events + +The Kafka utility transforms raw Lambda Kafka events into an intuitive format for processing. To handle messages effectively, you'll need to configure the `@Deserialization` annotation that matches your data format. Based on the deserializer you choose, incoming records are directly transformed into your business objects which can be auto-generated classes from Avro / Protobuf or simple POJOs. + +<!-- prettier-ignore --> +???+ tip "Using Avro is recommended" + We recommend Avro for production Kafka implementations due to its schema evolution capabilities, compact binary format, and integration with Schema Registry. This offers better type safety and forward/backward compatibility compared to JSON. + +=== "Avro Messages" + + ```java hl_lines="18 21" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class AvroKafkaHandler implements RequestHandler<ConsumerRecords<String, User>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(AvroKafkaHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_AVRO) + public String handleRequest(ConsumerRecords<String, User> records, Context context) { + for (ConsumerRecord<String, User> record : records) { + User user = record.value(); // User class is auto-generated from Avro schema + LOGGER.info("Processing user: {}, age {}", user.getName(), user.getAge()); + } + return "OK"; + } + } + ``` + +=== "Protocol Buffers" + + ```java hl_lines="18 21" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class ProtobufKafkaHandler implements RequestHandler<ConsumerRecords<String, UserProto.User>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(ProtobufKafkaHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_PROTOBUF) + public String handleRequest(ConsumerRecords<String, UserProto.User> records, Context context) { + for (ConsumerRecord<String, UserProto.User> record : records) { + UserProto.User user = record.value(); // UserProto.User class is auto-generated from Protocol Buffer schema + LOGGER.info("Processing user: {}, age {}", user.getName(), user.getAge()); + } + return "OK"; + } + } + ``` + +=== "JSON Messages" + + ```java hl_lines="18 21" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class JsonKafkaHandler implements RequestHandler<ConsumerRecords<String, User>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(JsonKafkaHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, User> records, Context context) { + for (ConsumerRecord<String, User> record : records) { + User user = record.value(); // Deserialized JSON object into User POJO + LOGGER.info("Processing user: {}, age {}", user.getName(), user.getAge()); + } + return "OK"; + } + } + ``` + +<!-- prettier-ignore --> +???+ tip "Full examples on GitHub" + A full example including how to generate Avro and Protobuf Java classes can be found on GitHub at [https://github.com/aws-powertools/powertools-lambda-java/tree/main/examples/powertools-examples-kafka](https://github.com/aws-powertools/powertools-lambda-java/tree/main/examples/powertools-examples-kafka). + +### Deserializing keys and values + +The `@Deserialization` annotation deserializes both keys and values based on your type configuration. This flexibility allows you to work with different data formats in the same message. + +=== "Key and Value Deserialization" + + ```java hl_lines="22" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class KeyValueKafkaHandler implements RequestHandler<ConsumerRecords<ProductKey, ProductInfo>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(KeyValueKafkaHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_AVRO) + public String handleRequest(ConsumerRecords<ProductKey, ProductInfo> records, Context context) { + for (ConsumerRecord<ProductKey, ProductInfo> record : records) { + // Access both deserialized components + ProductKey key = record.key(); // ProductKey class is auto-generated from Avro schema + ProductInfo product = record.value(); // ProductInfo class is auto-generated from Avro schema + + LOGGER.info("Processing product ID: {}", key.getProductId()); + LOGGER.info("Product: {} - ${}", product.getName(), product.getPrice()); + } + return "OK"; + } + } + ``` + +=== "Value-Only Deserialization" + + ```java hl_lines="22" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class ValueOnlyKafkaHandler implements RequestHandler<ConsumerRecords<String, Order>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(ValueOnlyKafkaHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, Order> records, Context context) { + for (ConsumerRecord<String, Order> record : records) { + // Key remains as string (if present) + String key = record.key(); + if (key != null) { + LOGGER.info("Message key: {}", key); + } + + // Value is deserialized as JSON + Order order = record.value(); + LOGGER.info("Order #{} - Total: ${}", order.getOrderId(), order.getTotal()); + } + return "OK"; + } + } + ``` + +### Handling primitive types + +When working with primitive data types (strings, integers, etc.) rather than structured objects, you can use any deserialization type such as `KAFKA_JSON`. Simply place the primitive type like `Integer` or `String` in the `ConsumerRecords` generic type parameters, and the library will automatically handle primitive type deserialization. + +<!-- prettier-ignore --> +???+ tip "Common pattern: Keys with primitive values" + Using primitive types (strings, integers) as Kafka message keys is a common pattern for partitioning and identifying messages. Powertools automatically handles these primitive keys without requiring special configuration, making it easy to implement this popular design pattern. + +=== "Primitive key" + + ```java hl_lines="18 22" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class PrimitiveKeyHandler implements RequestHandler<ConsumerRecords<Integer, Customer>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(PrimitiveKeyHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<Integer, Customer> records, Context context) { + for (ConsumerRecord<Integer, Customer> record : records) { + // Key is automatically deserialized as Integer + Integer key = record.key(); + + // Value is deserialized as JSON + Customer customer = record.value(); + + LOGGER.info("Key: {}", key); + LOGGER.info("Name: {}", customer.getName()); + LOGGER.info("Email: {}", customer.getEmail()); + } + return "OK"; + } + } + ``` + +=== "Primitive key and value" + + ```java hl_lines="18 22" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class PrimitiveHandler implements RequestHandler<ConsumerRecords<String, String>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(PrimitiveHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, String> records, Context context) { + for (ConsumerRecord<String, String> record : records) { + // Key is automatically deserialized as String + String key = record.key(); + + // Value is automatically deserialized as String + String value = record.value(); + + LOGGER.info("Key: {}", key); + LOGGER.info("Value: {}", value); + } + return "OK"; + } + } + ``` + +### Message format support and comparison + +The Kafka utility supports multiple serialization formats to match your existing Kafka implementation. Choose the format that best suits your needs based on performance, schema evolution requirements, and ecosystem compatibility. + +<!-- prettier-ignore --> +???+ tip "Selecting the right format" + For new applications, consider Avro or Protocol Buffers over JSON. Both provide schema validation, evolution support, and significantly better performance with smaller message sizes. Avro is particularly well-suited for Kafka due to its built-in schema evolution capabilities. + +=== "Supported Formats" + + | Format | DeserializationType | Description | Required Dependencies | + |--------|---------------------|-------------|----------------------| + | **JSON** | `KAFKA_JSON` | Human-readable text format | Jackson | + | **Avro** | `KAFKA_AVRO` | Compact binary format with schema | Apache Avro | + | **Protocol Buffers** | `KAFKA_PROTOBUF` | Efficient binary format | Protocol Buffers | + | **Lambda Default** | `LAMBDA_DEFAULT` | Uses Lambda's built-in deserialization (equivalent to removing the `@Deserialization` annotation) | None | + +=== "Format Comparison" + + | Feature | JSON | Avro | Protocol Buffers | + |---------|------|------|-----------------| + | **Schema Definition** | Optional | Required schema file | Required .proto file | + | **Schema Evolution** | None | Strong support | Strong support | + | **Size Efficiency** | Low | High | Highest | + | **Processing Speed** | Slower | Fast | Fastest | + | **Human Readability** | High | Low | Low | + | **Implementation Complexity** | Low | Medium | Medium | + | **Additional Dependencies** | None | Apache Avro | Protocol Buffers | + +Choose the serialization format that best fits your needs: + +- **JSON**: Best for simplicity and when schema flexibility is important +- **Avro**: Best for systems with evolving schemas and when compatibility is critical +- **Protocol Buffers**: Best for performance-critical systems with structured data +- **Lambda Default**: Best for simple string-based messages or when using Lambda's built-in deserialization + +## Advanced + +### Accessing record metadata + +Each Kafka record contains important metadata that you can access alongside the deserialized message content. This metadata helps with message processing, troubleshooting, and implementing advanced patterns like exactly-once processing. + +=== "Working with Record Metadata" + + ```java + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.apache.kafka.common.header.Header; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + + public class MetadataKafkaHandler implements RequestHandler<ConsumerRecords<String, Customer>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(MetadataKafkaHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_AVRO) + public String handleRequest(ConsumerRecords<String, Customer> records, Context context) { + for (ConsumerRecord<String, Customer> record : records) { + // Log record coordinates for tracing + LOGGER.info("Processing message from topic '{}'", record.topic()); + LOGGER.info(" Partition: {}, Offset: {}", record.partition(), record.offset()); + LOGGER.info(" Produced at: {}", record.timestamp()); + + // Process message headers + if (record.headers() != null) { + for (Header header : record.headers()) { + LOGGER.info(" Header: {} = {}", + header.key(), new String(header.value())); + } + } + + // Access the Avro deserialized message content + Customer customer = record.value(); // Customer class is auto-generated from Avro schema + LOGGER.info("Processing order for: {}", customer.getName()); + LOGGER.info("Order total: ${}", customer.getOrderTotal()); + } + return "OK"; + } + } + ``` + +#### Available metadata properties + +| Property | Description | Example Use Case | +| ----------------- | ----------------------------------------------- | ------------------------------------------- | +| `topic()` | Topic name the record was published to | Routing logic in multi-topic consumers | +| `partition()` | Kafka partition number | Tracking message distribution | +| `offset()` | Position in the partition | De-duplication, exactly-once processing | +| `timestamp()` | Unix timestamp when record was created | Event timing analysis | +| `timestampType()` | Timestamp type (CREATE_TIME or LOG_APPEND_TIME) | Data lineage verification | +| `headers()` | Key-value pairs attached to the message | Cross-cutting concerns like correlation IDs | +| `key()` | Deserialized message key | Customer ID or entity identifier | +| `value()` | Deserialized message content | The actual business data | + +### Error handling + +Handle errors gracefully when processing Kafka messages to ensure your application maintains resilience and provides clear diagnostic information. The Kafka utility integrates with standard Java exception handling patterns. + +<!-- prettier-ignore --> +!!! info "Treating Deserialization errors" + Read [Deserialization failures](#deserialization-failures). Deserialization failures will fail the whole batch and do not execute your handler. + +=== "Error Handling" + + ```java + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + + public class ErrorHandlingKafkaHandler implements RequestHandler<ConsumerRecords<String, Order>, String> { + + private static final Logger LOGGER = LoggerFactory.getLogger(ErrorHandlingKafkaHandler.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @Logging + @FlushMetrics(namespace = "KafkaProcessing", service = "order-processing") + @Deserialization(type = DeserializationType.KAFKA_AVRO) + public String handleRequest(ConsumerRecords<String, Order> records, Context context) { + metrics.addMetric("TotalRecords", records.count(), MetricUnit.COUNT); + int successfulRecords = 0; + int failedRecords = 0; + + for (ConsumerRecord<String, Order> record : records) { + try { + Order order = record.value(); // Order class is auto-generated from Avro schema + processOrder(order); + successfulRecords++; + metrics.addMetric("ProcessedRecords", 1, MetricUnit.COUNT); + } catch (Exception e) { + failedRecords++; + LOGGER.error("Error processing Kafka message from topic: {}, partition: {}, offset: {}", + record.topic(), record.partition(), record.offset(), e); + metrics.addMetric("ProcessingErrors", 1, MetricUnit.COUNT); + // Optionally send to DLQ or error topic + sendToDlq(record); + } + } + + return String.format("Processed %d records successfully, %d failed", + successfulRecords, failedRecords); + } + + private void processOrder(Order order) { + // Your business logic here + LOGGER.info("Processing order: {}", order.getOrderId()); + } + + private void sendToDlq(ConsumerRecord<String, Order> record) { + // Implementation to send failed records to dead letter queue + } + } + ``` + +### Integrating with Idempotency + +When processing Kafka messages in Lambda, failed batches can result in message reprocessing. The idempotency utility prevents duplicate processing by tracking which messages have already been handled, ensuring each message is processed exactly once. + +The Idempotency utility automatically stores the result of each successful operation, returning the cached result if the same message is processed again, which prevents potentially harmful duplicate operations like double-charging customers or double-counting metrics. + +=== "Idempotent Kafka Processing" + + ```java + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.idempotency.Idempotency; + import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; + import software.amazon.lambda.powertools.idempotency.Idempotent; + import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; + import software.amazon.lambda.powertools.logging.Logging; + + public class IdempotentKafkaHandler implements RequestHandler<ConsumerRecords<String, Payment>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(IdempotentKafkaHandler.class); + + public IdempotentKafkaHandler() { + // Configure idempotency with DynamoDB persistence store + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName("IdempotencyTable") + .build()) + .configure(); + } + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, Payment> records, Context context) { + for (ConsumerRecord<String, Payment> record : records) { + // Payment class deserialized from JSON + Payment payment = record.value(); + + // Process each message with idempotency protection + processPayment(payment); + } + return "OK"; + } + + @Idempotent + private void processPayment(Payment payment) { + LOGGER.info("Processing payment {}", payment.getPaymentId()); + + // Your business logic here + PaymentService.process(payment.getPaymentId(), payment.getCustomerId(), payment.getAmount()); + } + } + ``` + +<!-- prettier-ignore --> +???+ tip "Ensuring exactly-once processing" + The `@Idempotent` annotation will use the JSON representation of the Payment object to make sure that the same object is only processed exactly once. Even if a batch fails and Lambda retries the messages, each unique payment will be processed exactly once. + +### Best practices + +#### Batch size configuration + +The number of Kafka records processed per Lambda invocation is controlled by your Event Source Mapping configuration. Properly sized batches optimize cost and performance. + +=== "Batch size configuration" + + ```yaml + Resources: + OrderProcessingFunction: + Type: AWS::Serverless::Function + Properties: + Handler: org.example.OrderHandler::handleRequest + Runtime: java21 + Events: + KafkaEvent: + Type: MSK + Properties: + Stream: !GetAtt OrdersMSKCluster.Arn + Topics: + - order-events + - payment-events + # Configuration for optimal throughput/latency balance + BatchSize: 100 + MaximumBatchingWindowInSeconds: 5 + StartingPosition: LATEST + # Enable partial batch success reporting + FunctionResponseTypes: + - ReportBatchItemFailures + ``` + +Different workloads benefit from different batch configurations: + +- **High-volume, simple processing**: Use larger batches (100-500 records) with short timeout +- **Complex processing with database operations**: Use smaller batches (10-50 records) +- **Mixed message sizes**: Set appropriate batching window (1-5 seconds) to handle variability + +#### Cross-language compatibility + +When using binary serialization formats across multiple programming languages, ensure consistent schema handling to prevent deserialization failures. + +=== "Using Python naming convention" + + ```java hl_lines="33 36 39 42 56" + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.kafka.Deserialization; + import software.amazon.lambda.powertools.kafka.DeserializationType; + import software.amazon.lambda.powertools.logging.Logging; + import com.fasterxml.jackson.annotation.JsonProperty; + import java.time.Instant; + + public class CrossLanguageKafkaHandler implements RequestHandler<ConsumerRecords<String, OrderEvent>, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(CrossLanguageKafkaHandler.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, OrderEvent> records, Context context) { + for (ConsumerRecord<String, OrderEvent> record : records) { + OrderEvent order = record.value(); // OrderEvent class handles JSON with Python field names + LOGGER.info("Processing order {} from {}", + order.getOrderId(), order.getOrderDate()); + } + return "OK"; + } + } + + // Example class that handles Python snake_case field names + public class OrderEvent { + @JsonProperty("order_id") + private String orderId; + + @JsonProperty("customer_id") + private String customerId; + + @JsonProperty("total_amount") + private double totalAmount; + + @JsonProperty("order_date") + private long orderDateMillis; + + // Getters and setters + public String getOrderId() { return orderId; } + public void setOrderId(String orderId) { this.orderId = orderId; } + + public String getCustomerId() { return customerId; } + public void setCustomerId(String customerId) { this.customerId = customerId; } + + public double getTotalAmount() { return totalAmount; } + public void setTotalAmount(double totalAmount) { this.totalAmount = totalAmount; } + + public Instant getOrderDate() { + return Instant.ofEpochMilli(orderDateMillis); + } + public void setOrderDate(long orderDateMillis) { + this.orderDateMillis = orderDateMillis; + } + } + ``` + +Common cross-language challenges to address: + +- **Field naming conventions**: camelCase in Java vs snake_case in Python +- **Date/time**: representation differences +- **Numeric precision handling**: especially decimals + +### Troubleshooting + +#### Deserialization failures + +The Java Kafka utility registers a [custom Lambda serializer](https://docs.aws.amazon.com/lambda/latest/dg/java-custom-serialization.html) that performs **eager deserialization** of all records in the batch before your handler method is invoked. + +This means that if any record in the batch fails deserialization, a `RuntimeException` will be thrown with a concrete error message explaining why deserialization failed, and your handler method will never be called. + +**Key implications:** + +- **Batch-level failure**: If one record fails deserialization, the entire batch fails +- **Early failure detection**: Deserialization errors are caught before your business logic runs +- **Clear error messages**: The `RuntimeException` provides specific details about what went wrong +- **No partial processing**: You cannot process some records while skipping failed ones within the same batch + +**Example of deserialization failure:** + +```java +// If any record in the batch has invalid Avro data, you'll see: +// RuntimeException: Failed to deserialize Kafka record: Invalid Avro schema for record at offset 12345 +``` + +<!-- prettier-ignore --> +!!! warning "Handler method not invoked on deserialization failure" + When deserialization fails, your `handleRequest` method will not be invoked at all. The `RuntimeException` is thrown before your handler code runs, preventing any processing of the batch. + +**Handling deserialization failures:** + +Since deserialization happens before your handler is called, you cannot catch these exceptions within your handler method. Instead, configure your Event Source Mapping with appropriate error handling: + +- **Dead Letter Queue (DLQ)**: Configure a DLQ to capture failed batches for later analysis +- **Maximum Retry Attempts**: Set appropriate retry limits to avoid infinite retries +- **Batch Size**: Use smaller batch sizes to minimize the impact of individual record failures + +```yaml +# Example SAM template configuration for error handling +Events: + KafkaEvent: + Type: MSK + Properties: + # ... other properties + BatchSize: 10 # Smaller batches reduce failure impact + MaximumRetryAttempts: 3 + DestinationConfig: + OnFailure: + Type: SQS + Destination: !GetAtt DeadLetterQueue.Arn +``` + +#### Schema compatibility issues + +Schema compatibility issues often manifest as successful connections but failed deserialization. Common causes include: + +- **Schema evolution without backward compatibility**: New producer schema is incompatible with consumer schema +- **Field type mismatches**: For example, a field changed from String to Integer across systems +- **Missing required fields**: Fields required by the consumer schema but absent in the message +- **Default value discrepancies**: Different handling of default values between languages + +When using Schema Registry, verify schema compatibility rules are properly configured for your topics and that all applications use the same registry. + +#### Memory and timeout optimization + +Lambda functions processing Kafka messages may encounter resource constraints, particularly with large batches or complex processing logic. + +For memory errors: + +- Increase Lambda memory allocation, which also provides more CPU resources +- Process fewer records per batch by adjusting the `BatchSize` parameter in your event source mapping +- Consider optimizing your message format to reduce memory footprint + +For timeout issues: + +- Extend your Lambda function timeout setting to accommodate processing time +- Implement chunked or asynchronous processing patterns for time-consuming operations +- Monitor and optimize database operations, external API calls, or other I/O operations in your handler + +<!-- prettier-ignore --> +???+ tip "Monitoring memory usage" + Use CloudWatch metrics to track your function's memory utilization. If it consistently exceeds 80% of allocated memory, consider increasing the memory allocation or optimizing your code. + +## Kafka workflow + +### Using ESM with Schema Registry validation (SOURCE) + +<center> +```mermaid +sequenceDiagram + participant Kafka + participant ESM as Event Source Mapping + participant SchemaRegistry as Schema Registry + participant Lambda + participant KafkaUtility + participant YourCode + Kafka->>+ESM: Send batch of records + ESM->>+SchemaRegistry: Validate schema + SchemaRegistry-->>-ESM: Confirm schema is valid + ESM->>+Lambda: Invoke with validated records (still encoded) + Lambda->>+KafkaUtility: Pass Kafka event + KafkaUtility->>KafkaUtility: Parse event structure + loop For each record + KafkaUtility->>KafkaUtility: Decode base64 data + KafkaUtility->>KafkaUtility: Deserialize based on DeserializationType + end + KafkaUtility->>+YourCode: Provide ConsumerRecords + YourCode->>YourCode: Process records + YourCode-->>-KafkaUtility: Return result + KafkaUtility-->>-Lambda: Pass result back + Lambda-->>-ESM: Return response + ESM-->>-Kafka: Acknowledge processed batch +``` +</center> + +### Using ESM with Schema Registry deserialization (JSON) + +<center> +```mermaid +sequenceDiagram + participant Kafka + participant ESM as Event Source Mapping + participant SchemaRegistry as Schema Registry + participant Lambda + participant KafkaUtility + participant YourCode + Kafka->>+ESM: Send batch of records + ESM->>+SchemaRegistry: Validate and deserialize + SchemaRegistry->>SchemaRegistry: Deserialize records + SchemaRegistry-->>-ESM: Return deserialized data + ESM->>+Lambda: Invoke with pre-deserialized JSON records + Lambda->>+KafkaUtility: Pass Kafka event + KafkaUtility->>KafkaUtility: Parse event structure + loop For each record + KafkaUtility->>KafkaUtility: Decode base64 data + KafkaUtility->>KafkaUtility: Record is already deserialized + KafkaUtility->>KafkaUtility: Map to POJO (if specified) + end + KafkaUtility->>+YourCode: Provide ConsumerRecords + YourCode->>YourCode: Process records + YourCode-->>-KafkaUtility: Return result + KafkaUtility-->>-Lambda: Pass result back + Lambda-->>-ESM: Return response + ESM-->>-Kafka: Acknowledge processed batch +``` +</center> + +### Using ESM without Schema Registry integration + +<center> +```mermaid +sequenceDiagram + participant Kafka + participant Lambda + participant KafkaUtility + participant YourCode + Kafka->>+Lambda: Invoke with batch of records (direct integration) + Lambda->>+KafkaUtility: Pass raw Kafka event + KafkaUtility->>KafkaUtility: Parse event structure + loop For each record + KafkaUtility->>KafkaUtility: Decode base64 data + KafkaUtility->>KafkaUtility: Deserialize based on DeserializationType + end + KafkaUtility->>+YourCode: Provide ConsumerRecords + YourCode->>YourCode: Process records + YourCode-->>-KafkaUtility: Return result + KafkaUtility-->>-Lambda: Pass result back + Lambda-->>-Kafka: Acknowledge processed batch +``` +</center> + +## Testing your code + +Testing Kafka consumer functions is straightforward with JUnit. You can construct Kafka `ConsumerRecords` in the default way provided by the kafka-clients library without needing a real Kafka cluster. + +=== "Testing your code" + + ```java + package org.example; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.events.KafkaEvent; + import org.apache.kafka.clients.consumer.ConsumerRecord; + import org.apache.kafka.clients.consumer.ConsumerRecords; + import org.apache.kafka.common.TopicPartition; + import org.junit.jupiter.api.Test; + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + import java.util.*; + + import static org.junit.jupiter.api.Assertions.*; + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class KafkaHandlerTest { + + @Mock + private Context context; + + @Test + void testProcessJsonMessage() { + // Create a test Kafka event with JSON data + Order testOrder = new Order("12345", 99.95); + ConsumerRecord<String, Order> record = new ConsumerRecord<>( + "orders-topic", 0, 15L, null, testOrder); + + Map<TopicPartition, List<ConsumerRecord<String, Order>>> recordsMap = new HashMap<>(); + recordsMap.put(new TopicPartition("orders-topic", 0), Arrays.asList(record)); + ConsumerRecords<String, Order> records = new ConsumerRecords<>(recordsMap); + + // Create handler and invoke + JsonKafkaHandler handler = new JsonKafkaHandler(); + String response = handler.handleRequest(records, context); + + // Verify the response + assertEquals("OK", response); + } + + @Test + void testProcessMultipleRecords() { + // Create a test event with multiple records + Customer customer1 = new Customer("A1", "Alice"); + Customer customer2 = new Customer("B2", "Bob"); + + List<ConsumerRecord<String, Customer>> recordList = Arrays.asList( + new ConsumerRecord<>("customers-topic", 0, 10L, null, customer1), + new ConsumerRecord<>("customers-topic", 0, 11L, null, customer2) + ); + + Map<TopicPartition, List<ConsumerRecord<String, Customer>>> recordsMap = new HashMap<>(); + recordsMap.put(new TopicPartition("customers-topic", 0), recordList); + ConsumerRecords<String, Customer> records = new ConsumerRecords<>(recordsMap); + + // Create handler and invoke + JsonKafkaHandler handler = new JsonKafkaHandler(); + String response = handler.handleRequest(records, context); + + // Verify the response + assertEquals("OK", response); + } + } + ``` + +## Extra Resources + +### Lambda Custom Serializers Compatibility + +This Kafka utility uses [Lambda custom serializers](https://docs.aws.amazon.com/lambda/latest/dg/java-custom-serialization.html) to provide automatic deserialization of Kafka messages. + +**Important compatibility considerations:** + +- **Existing custom serializers**: This utility will not be compatible if you already use your own custom Lambda serializer in your project +- **Non-Kafka handlers**: Installing this library will not affect default Lambda serialization behavior for non-Kafka related handlers +- **Kafka-specific**: The custom serialization only applies to handlers annotated with `@Deserialization` +- **Lambda default fallback**: Using `@Deserialization(type = DeserializationType.LAMBDA_DEFAULT)` will proxy to Lambda's default serialization behavior + +**Need help with compatibility?** + +If you are blocked from adopting this utility due to existing custom serializers or other compatibility concerns, please contact us with your specific use-cases. We'd like to understand your requirements and explore potential solutions. + +For more information about Lambda custom serialization, see the [official AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/java-custom-serialization.html). diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index c4947a6e8..9d14c8228 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -4,13 +4,7 @@ description: Utility --- The large message utility handles SQS and SNS messages which have had their payloads -offloaded to S3 if they are larger than the maximum allowed size (256 KB). - -!!! Notice - The large message utility (available in the `powertools-sqs` module for versions v1.16.1 and earlier) is now deprecated - and replaced by the `powertools-large-messages` described in this page. - You can still get the documentation [here](sqs_large_message_handling.md) - and the migration guide [here](#migration-from-the-sqs-large-message-utility). +offloaded to S3 if they are larger than the maximum allowed size (1 MB). ## Features @@ -33,12 +27,12 @@ stateDiagram-v2 sendMsg --> extendLib state extendLib { state if_big <<choice>> - bigMsg: MessageBody > 256KB ? + bigMsg: MessageBody > 1MB ? putObject: putObject(S3Bucket, S3Key, Body) updateMsg: Update MessageBody<br>with a pointer to S3<br>and add a message attribute bigMsg --> if_big - if_big --> [*]: size(body) <= 256kb - if_big --> putObject: size(body) > 256kb + if_big --> [*]: size(body) <= 1MB + if_big --> putObject: size(body) > 1MB putObject --> updateMsg updateMsg --> [*] } @@ -78,7 +72,7 @@ stateDiagram-v2 ``` -SQS and SNS message payload is limited to 256KB. If you wish to send messages with a larger payload, you can leverage the +SQS and SNS message payload is limited to 1MB. If you wish to send messages with a larger payload, you can leverage the [amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) or [amazon-sns-java-extended-client-lib](https://github.com/awslabs/amazon-sns-java-extended-client-lib) which offload the message to Amazon S3. See documentation @@ -93,62 +87,14 @@ extended client libraries. Once a message's payload has been processed successfu utility deletes the payload from S3. This utility is compatible with -versions *[1.1.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases/tag/1.1.0)* -of amazon-sqs-java-extended-client-lib -and *[1.0.0+](https://github.com/awslabs/amazon-sns-java-extended-client-lib/releases/tag/1.0.0)* -of amazon-sns-java-extended-client-lib. +versions *[1.1.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases/tag/1.1.0)* and *[2.0.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases/tag/2.0.0)* +of [amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) / [amazon-sns-java-extended-client-lib](https://github.com/awslabs/amazon-sns-java-extended-client-lib). ## Install -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. - -=== "Maven Java 11+" -```xml hl_lines="3-7 16 18 24-27" -<dependencies> -... -<dependency> -<groupId>software.amazon.lambda</groupId> -<artifactId>powertools-large-messages</artifactId> -<version>{{ powertools.version }}</version> -</dependency> -... -</dependencies> -... -<!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> -<build> -<plugins> -... -<plugin> -<groupId>dev.aspectj</groupId> -<artifactId>aspectj-maven-plugin</artifactId> -<version>1.13.1</version> -<configuration> -<source>11</source> <!-- or higher --> -<target>11</target> <!-- or higher --> -<complianceLevel>11</complianceLevel> <!-- or higher --> -<aspectLibraries> -<aspectLibrary> -<groupId>software.amazon.lambda</groupId> -<artifactId>powertools-large-messages</artifactId> -</aspectLibrary> -</aspectLibraries> -</configuration> -<executions> -<execution> -<goals> -<goal>compile</goal> -</goals> -</execution> -</executions> -</plugin> -... -</plugins> -</build> -``` - -=== "Maven Java 1.8" +=== "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 25-28" <dependencies> ... <dependency> @@ -160,43 +106,52 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> <build> <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-large-messages</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14</version> + <configuration> + <source>11</source> <!-- or higher --> + <target>11</target> <!-- or higher --> + <complianceLevel>11</complianceLevel> <!-- or higher --> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-large-messages</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> </plugin> ... </plugins> </build> ``` -=== "Gradle Java 11+" +=== "Gradle" ```groovy hl_lines="3 11" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -204,33 +159,13 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl } dependencies { - aspect 'software.amazon.lambda:powertools-large-messages:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-large-messages:{{ powertools.version }}' // Use 'implementation' instead of 'aspect' when using the functional approach } sourceCompatibility = 11 // or higher targetCompatibility = 11 // or higher ``` -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-large-messages:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - ## Permissions As the utility interacts with Amazon S3, the lambda function must have the following permissions @@ -239,14 +174,20 @@ on the S3 bucket used for the large messages offloading: - `s3:GetObject` - `s3:DeleteObject` -## Annotation +## Usage -The annotation `@LargeMessage` can be used on any method where the *first* parameter is one of: +You can use the Large Messages utility with either the `@LargeMessage` annotation or the functional API. + +The `@LargeMessage` annotation can be used on any method where the *first* parameter is one of: - `SQSEvent.SQSMessage` - `SNSEvent.SNSRecord` -=== "SQS Example" +The functional API `LargeMessages.processLargeMessage()` accepts the same message types. + +### Basic usage + +=== "@LargeMessage annotation - SQS" ```java hl_lines="8 13 15" import software.amazon.lambda.powertools.largemessages.LargeMessage; @@ -268,7 +209,28 @@ The annotation `@LargeMessage` can be used on any method where the *first* param } ``` -=== "SNS Example" +=== "Functional API - SQS" + + ```java hl_lines="1 8" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SqsMessageHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + @Override + public SQSBatchResponse handleRequest(SQSEvent event, Context context) { + for (SQSMessage message: event.getRecords()) { + LargeMessages.processLargeMessage(message, this::processRawMessage); + } + return SQSBatchResponse.builder().build(); + } + + private void processRawMessage(SQSEvent.SQSMessage sqsMessage) { + // sqsMessage.getBody() will contain the content of the S3 object + } + } + ``` + +=== "@LargeMessage annotation - SNS" ```java hl_lines="7 11 13" import software.amazon.lambda.powertools.largemessages.LargeMessage; @@ -288,6 +250,25 @@ The annotation `@LargeMessage` can be used on any method where the *first* param } ``` +=== "Functional API - SNS" + + ```java hl_lines="1 7" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SnsRecordHandler implements RequestHandler<SNSEvent, String> { + + @Override + public String handleRequest(SNSEvent event, Context context) { + return LargeMessages.processLargeMessage(event.records.get(0), this::processSNSRecord); + } + + private String processSNSRecord(SNSEvent.SNSRecord snsRecord) { + // snsRecord.getSNS().getMessage() will contain the content of the S3 object + return "Hello World"; + } + } + ``` + When the Lambda function is invoked with a SQS or SNS event, the utility first checks if the content was offloaded to S3. In the case of a large message, there is a message attribute specifying the size of the offloaded message and the message contains a pointer to the S3 object. @@ -297,9 +278,9 @@ and place the content of the object in the message payload. You can then directl If there was an error during the S3 download, the function will fail with a `LargeMessageProcessingException`. After your code is invoked and returns without error, the object is deleted from S3 -using the `deleteObject(bucket, key)` API. You can disable the deletion of S3 objects with the following configuration: +using the `deleteObject(bucket, key)` API. You can disable the deletion of S3 objects: -=== "Don't delete S3 Objects" +=== "@LargeMessage annotation" ```java @LargeMessage(deleteS3Object = false) private void processRawMessage(SQSEvent.SQSMessage sqsMessage) { @@ -307,71 +288,143 @@ using the `deleteObject(bucket, key)` API. You can disable the deletion of S3 ob } ``` +=== "Functional API" + ```java + LargeMessages.processLargeMessage(message, this::processRawMessage, false); + ``` + !!! tip "Use together with batch module" This utility works perfectly together with the batch module (`powertools-batch`), especially for SQS: - ```java hl_lines="2 5-7 12 15 16" title="Combining batch and large message modules" - public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { - private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; - - public SqsBatchHandler() { - handler = new BatchMessageHandlerBuilder() - .withSqsBatchHandler() - .buildWithRawMessageHandler(this::processMessage); - } + === "@LargeMessage annotation" + ```java hl_lines="2 5-7 12 15 16" title="Combining batch and large message modules" + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } - @Override - public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { - return handler.processBatch(sqsEvent, context); + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); + } + + @LargeMessage + private void processMessage(SQSEvent.SQSMessage sqsMessage) { + // do something with the message + } } + ``` - @LargeMessage - private void processMessage(SQSEvent.SQSMessage sqsMessage) { - // do something with the message + === "Functional API" + ```java hl_lines="7-9 14 18" title="Combining batch and large message modules" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); + } + + private void processMessage(SQSEvent.SQSMessage sqsMessage) { + LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage); + } + + private void handleProcessedMessage(SQSEvent.SQSMessage processedMessage) { + // do something with the message + } } - } - ``` + ``` !!! tip "Use together with idempotency module" - This utility also works together with the idempotency module (`powertools-idempotency`). - You can add both the `@LargeMessage` and `@Idempotent` annotations, in any order, to the same method. - The `@Idempotent` takes precedence over the `@LargeMessage` annotation. - It means Idempotency module will use the initial raw message (containing the S3 pointer) and not the large message. + When using the `@LargeMessage` annotation, you can combine it with the `@Idempotent` annotation on the same method. + The `@Idempotent` takes precedence over the `@LargeMessage` annotation, meaning the Idempotency module will use the initial raw message (containing the S3 pointer) and not the large message. + + When using the functional API, call `LargeMessages.processLargeMessage()` from within the `@Idempotent` method to ensure idempotency is based on the S3 pointer, not the unwrapped large blob. - ```java hl_lines="6 23-25" title="Combining idempotency and large message modules" - public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + === "@LargeMessage annotation" + ```java hl_lines="6 23-25" title="Combining idempotency and large message modules" + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { - public SqsBatchHandler() { - Idempotency.config().withConfig( - IdempotencyConfig.builder() - .withEventKeyJMESPath("body") // get the body of the message for the idempotency key - .build()) - .withPersistenceStore( - DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("IDEMPOTENCY_TABLE")) - .build() - ).configure(); - } + public SqsBatchHandler() { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("body") // get the body of the message for the idempotency key + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build() + ).configure(); + } - @Override - public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { - for (SQSMessage message: event.getRecords()) { - processRawMessage(message, context); + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + for (SQSMessage message: event.getRecords()) { + processRawMessage(message, context); + } + return SQSBatchResponse.builder().build(); + } + + @Idempotent + @LargeMessage + private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) { + // do something with the message } - return SQSBatchResponse.builder().build(); } + ``` - @Idempotent - @LargeMessage - private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) { - // do something with the message + === "Functional API" + ```java hl_lines="8 25 27" title="Combining idempotency and large message modules" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + public SqsBatchHandler() { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("body") // get the body of the message for the idempotency key + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build() + ).configure(); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + for (SQSMessage message: event.getRecords()) { + processRawMessage(message, context); + } + return SQSBatchResponse.builder().build(); + } + + @Idempotent + private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) { + return LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage); + } + + private String handleProcessedMessage(SQSEvent.SQSMessage processedMessage) { + // do something with the message + } } - } - ``` + ``` ## Customizing S3 client configuration -To interact with S3, the utility creates a default S3 Client : +To interact with S3, the utility creates a default S3 Client: === "Default S3 Client" ```java @@ -381,9 +434,9 @@ To interact with S3, the utility creates a default S3 Client : .build(); ``` -If you need to customize this `S3Client`, you can leverage the `LargeMessageConfig` singleton: +If you need to customize this `S3Client`, you can leverage the `LargeMessageConfig` singleton. This works with both the annotation and functional API: -=== "Custom S3 Client" +=== "@LargeMessage annotation" ```java hl_lines="6" import software.amazon.lambda.powertools.largemessages.LargeMessage; @@ -406,6 +459,28 @@ If you need to customize this `S3Client`, you can leverage the `LargeMessageConf } ``` +=== "Functional API" + ```java hl_lines="1 6" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SnsRecordHandler implements RequestHandler<SNSEvent, String> { + + public SnsRecordHandler() { + LargeMessageConfig.init().withS3Client(/* put your custom S3Client here */); + } + + @Override + public String handleRequest(SNSEvent event, Context context) { + return LargeMessages.processLargeMessage(event.records.get(0), this::processSNSRecord); + } + + private String processSNSRecord(SNSEvent.SNSRecord snsRecord) { + // snsRecord.getSNS().getMessage() will contain the content of the S3 object + return "Hello World"; + } + } + ``` + ## Migration from the SQS Large Message utility - Replace the dependency in maven / gradle: `powertools-sqs` ==> `powertools-large-messages` @@ -416,4 +491,4 @@ If you need to customize this `S3Client`, you can leverage the `LargeMessageConf It gives more control, especially when dealing with partial failures with SQS (see the batch module). - The new module only provides an annotation, an equivalent to the `SqsUtils` class is not available anymore in this new version. -Finally, if you are still using the `powertools-sqs` library for batch processing, consider moving to `powertools-batch` at the same time to remove the dependency on this library completely; it has been deprecated and will be removed in v2. \ No newline at end of file +Finally, if you are still using the `powertools-sqs` library for batch processing, consider moving to `powertools-batch` at the same time to remove the dependency on this library completely; it has been deprecated and will be removed in v2. diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 85d30d77e..6de47df68 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -4,52 +4,79 @@ description: Utility --- -The parameters utility provides a way to retrieve parameter values from +The parameters utilities provide a way to retrieve parameter values from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html), -[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/), or [Amazon DynamoDB](https://aws.amazon.com/dynamodb/). -It also provides a base class to create your parameter provider implementation. +[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/), [Amazon DynamoDB](https://aws.amazon.com/dynamodb/), +or [AWS AppConfig](https://aws.amazon.com/systems-manager/features/appconfig/). + +## Key features -**Key features** - -* Retrieve one or multiple parameters from the underlying provider +* Retrieve one or multiple parameters from an underlying provider in a standard way * Cache parameter values for a given amount of time (defaults to 5 seconds) * Transform parameter values from JSON or base 64 encoded strings +* GraalVM support ## Install -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. +In order to provide lightweight dependencies, each parameters module is available as its own +package: + +* **Secrets Manager** - `powertools-parameters-secrets` +* **SSM Parameter Store** - `powertools-parameters-ssm` +* **Amazon DynamoDB** -`powertools-parameters-dynamodb` +* **AWS AppConfig** - `powertools-parameters-appconfig` -=== "Maven Java 11+" +You can easily mix and match parameter providers within the same project for different needs. - ```xml hl_lines="3-7 16 18 24-27" +Note that you must provide the concrete parameters module you want to use below - see the TODOs! + +=== "Maven" + + ```xml hl_lines="4-12 17 24 30-34" <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - <version>{{ powertools.version }}</version> + + <!-- TODO! Provide the parameters module you want to use here --> + <artifactId>powertools-parameters-secrets</artifactId> + <artifactId>powertools-parameters-ssm</artifactId> + <artifactId>powertools-parameters-dynamodb</artifactId> + <artifactId>powertools-parameters-appconfig</artifactId> + + <version>{{ powertools.version }}</version> </dependency> ... </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the provider classes directly (without annotations) --> <build> <plugins> ... <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>11</source> <!-- or higher --> <target>11</target> <!-- or higher --> <complianceLevel>11</complianceLevel> <!-- or higher --> <aspectLibraries> + <!-- TODO! Provide an aspectLibrary for each of the parameters module(s) you want to use here --> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> + <artifactId>powertools-parameters-secrets</artifactId> </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -63,57 +90,12 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </build> ``` -=== "Maven Java 1.8" - - ```xml hl_lines="3-7 16 18 24-27" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" +=== "Gradle" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11-13" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using provider classes directly (without annotations) } repositories { @@ -121,94 +103,90 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl } dependencies { - aspect 'software.amazon.lambda:powertools-parameters:{{ powertools.version }}' + // TODO! Provide the parameters module you want to use here + aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' // Not needed when using provider classes directly (without annotations) + implementation 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' // Use this instead of 'aspect' when using provider classes directly } sourceCompatibility = 11 // or higher targetCompatibility = 11 // or higher ``` -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-parameters:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - **IAM Permissions** This utility requires additional permissions to work as expected. See the table below: -Provider | Function/Method | IAM Permission -------------------------------------------------- |----------------------------------------------------------------------| --------------------------------------------------------------------------------- -SSM Parameter Store | `SSMProvider.get(String)` `SSMProvider.get(String, Class)` | `ssm:GetParameter` -SSM Parameter Store | `SSMProvider.getMultiple(String)` | `ssm:GetParametersByPath` -Secrets Manager | `SecretsProvider.get(String)` `SecretsProvider.get(String, Class)` | `secretsmanager:GetSecretValue` -DynamoDB | `DynamoDBProvider.get(String)` `DynamoDBProvider.getMultiple(string)` | `dynamodb:GetItem` `dynamoDB:Query` +| Provider | Function/Method | IAM Permission | +|-----------|-------------------------------------------------------------------------|---------------------------------------------------------------------------| +| SSM | `SSMProvider.get(String)` `SSMProvider.get(String, Class)` | `ssm:GetParameter` | +| SSM | `SSMProvider.getMultiple(String)` | `ssm:GetParametersByPath` | +| SSM | If using `withDecryption(true)` | You must add an additional permission `kms:Decrypt` | +| Secrets | `SecretsProvider.get(String)` `SecretsProvider.get(String, Class)` | `secretsmanager:GetSecretValue` | +| DynamoDB | `DynamoDBProvider.get(String)` `DynamoDBProvider.getMultiple(string)` | `dynamodb:GetItem` `dynamoDB:Query` | +| AppConfig | `AppConfigProvider.get(String)` `AppConfigProvider.getMultiple(string)` | `appconfig:StartConfigurationSession`, `appConfig:GetLatestConfiguration` | -## SSM Parameter Store +## Retrieving Parameters +You can retrieve parameters using either annotations or provider classes directly: -You can retrieve a single parameter using SSMProvider.get() and pass the key of the parameter. -For multiple parameters, you can use SSMProvider.getMultiple() and pass the path to retrieve them all. +- **Annotations** (e.g., `@SecretsParam`, `@SSMParam`) - Simpler syntax with field injection, but requires AspectJ configuration +- **Provider classes** (e.g., `SecretsProvider`, `SSMProvider`) - No AspectJ required, useful when you need to configure the underlying SDK client (e.g., different region or credentials), or prefer avoiding AspectJ setup -Alternatively, you can retrieve an instance of a provider and configure its underlying SDK client, -in order to get data from other regions or use specific credentials. +## Built-in provider classes -=== "SSMProvider" +This section describes the built-in provider classes for each parameter store. For each provider, examples are shown for both the annotation-based approach and the provider class approach. In cases where a provider supports extra features, these will also be described. - ```java hl_lines="6" - import software.amazon.lambda.powertools.parameters.SSMProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; +### Secrets Manager - public class AppWithSSM implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the SSM Provider - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - - // Retrieve a single parameter - String value = ssmProvider.get("/my/parameter"); - - // Retrieve multiple parameters from a path prefix - // This returns a Map with the parameter name as key - Map<String, String> values = ssmProvider.getMultiple("/my/path/prefix"); +=== "@SecretsParam annotation" + + ```java hl_lines="8 9" + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import software.amazon.lambda.powertools.parameters.secrets.SecretsParam; + public class ParametersFunction implements RequestHandler<String, String> { + + // Annotation-style injection from secrets manager + @SecretsParam(key = "/powertools-java/userpwd") + String secretParam; + + public string handleRequest(String request, Context context) { + // ... do something with the secretParam here + return "something"; + } } ``` -=== "SSMProvider with a custom client" +=== "SecretsProvider class" - ```java hl_lines="5 7" - import software.amazon.lambda.powertools.parameters.SSMProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + ```java hl_lines="12-15 19" + import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; + + import com.amazonaws.services.lambda.runtime.Context; + import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; + import software.amazon.lambda.powertools.parameters.secrets.SecretsProvider; + import com.amazonaws.services.lambda.runtime.RequestHandler; - public class AppWithSSM implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - SsmClient client = SsmClient.builder().region(Region.EU_CENTRAL_1).build(); - // Get an instance of the SSM Provider - SSMProvider ssmProvider = ParamManager.getSsmProvider(client); + public class RequestHandlerWithParams implements RequestHandler<String, String> { - // Retrieve a single parameter - String value = ssmProvider.get("/my/parameter"); + // Get an instance of the SecretsProvider. We can provide a custom client here if we want, + // for instance to use a particular region. + SecretsProvider secretsProvider = SecretsProvider + .builder() + .withClient(SecretsManagerClient.builder().build()) + .build(); - // Retrieve multiple parameters from a path prefix - // This returns a Map with the parameter name as key - Map<String, String> values = ssmProvider.getMultiple("/my/path/prefix"); + public String handleRequest(String input, Context context) { + // Retrieve a single secret + String value = secretsProvider.get("/my/secret"); + // ... do something with the secretParam here + return "something"; + } } ``` -### Additional arguments +### SSM Parameter Store The AWS Systems Manager Parameter Store provider supports two additional arguments for the `get()` and `getMultiple()` methods: @@ -217,17 +195,66 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | **withDecryption()** | `False` | Will automatically decrypt the parameter. | | **recursive()** | `False` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | -**Example:** -=== "AppWithSSM.java" +=== "@SSMParam annotation" + + ```java hl_lines="8 9" + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import software.amazon.lambda.powertools.parameters.ssm.SSMParam; - ```java hl_lines="9 12" - import software.amazon.lambda.powertools.parameters.SSMProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + public class ParametersFunction implements RequestHandler<String, String> { + + // Annotation-style injection from SSM Parameter Store + @SSMParam(key = "/powertools-java/param") + String ssmParam; + + public string handleRequest(String request, Context context) { + return ssmParam; // Request handler simply returns our configuration value + } + } + ``` + +=== "SSMProvider class" + + ```java hl_lines="12-15 19-20 22" + import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import software.amazon.awssdk.services.ssm.SsmClient; + import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; + + public class RequestHandlerWithParams implements RequestHandler<String, String> { + + // Get an instance of the SSMProvider. We can provide a custom client here if we want, + // for instance to use a particular region. + SSMProvider ssmProvider = SSMProvider + .builder() + .withClient(SsmClient.builder().build()) + .build(); + + public String handleRequest(String input, Context context) { + // Retrieve a single param + String value = ssmProvider + .get("/my/secret"); + // We might instead want to retrieve multiple parameters at once, returning a Map of key/value pairs + // .getMultiple("/my/secret/path"); + + // Return the result + return value; + } + } + ``` + +=== "Additional Options" + + ```java hl_lines="5 9 12" + import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; public class AppWithSSM implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { // Get an instance of the SSM Provider - SSMProvider ssmProvider = ParamManager.getSsmProvider(); + SSMProvider ssmProvider = SSMProvider.builder().build(); // Retrieve a single parameter and decrypt it String value = ssmProvider.withDecryption().get("/my/parameter"); @@ -238,183 +265,159 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -## Secrets Manager - -For secrets stored in Secrets Manager, use `getSecretsProvider`. +### DynamoDB -Alternatively, you can retrieve an instance of a provider and configure its underlying SDK client, -in order to get data from other regions or use specific credentials. +=== "@DynamoDbParam annotation" + ```java hl_lines="8 9" + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import software.amazon.lambda.powertools.parameters.dynamodb.DynamoDBParam; -=== "SecretsProvider" + public class ParametersFunction implements RequestHandler<String, String> { - ```java hl_lines="9" - import software.amazon.lambda.powertools.parameters.SecretsProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + // Annotation-style injection from DynamoDB + @DynamoDbParam(table = "my-test-tablename", key = "myKey") + String ddbParam; - public class AppWithSecrets implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the Secrets Provider - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); - - // Retrieve a single secret - String value = secretsProvider.get("/my/secret"); - + public string handleRequest(String request, Context context) { + return ddbParam; // Request handler simply returns our configuration value + } } ``` -=== "SecretsProvider with a custom client" +=== "DynamoDbProvider class" - ```java hl_lines="5 7" - import software.amazon.lambda.powertools.parameters.SecretsProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + ```java hl_lines="12-15 19-20 22" + import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + import software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProvider; - public class AppWithSecrets implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - SecretsManagerClient client = SecretsManagerClient.builder().region(Region.EU_CENTRAL_1).build(); - // Get an instance of the Secrets Provider - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(client); + public class RequestHandlerWithParams implements RequestHandler<String, String> { + + // Get an instance of the DynamoDbProvider. We can provide a custom client here if we want, + // for instance to use a particular region. + DynamoDbProvider ddbProvider = DynamoDbProvider + .builder() + .withClient(DynamoDbClient.builder().build()) + .build(); - // Retrieve a single secret - String value = secretsProvider.get("/my/secret"); + public String handleRequest(String input, Context context) { + // Retrieve a single param + String value = ddbProvider + .get("/my/secret"); + // We might instead want to retrieve multiple values at once, returning a Map of key/value pairs + // .getMultiple("my-partition-key-value"); + // Return the result + return value; + } } ``` -## DynamoDB -To get secrets stored in DynamoDB, use `getDynamoDbProvider`, providing the name of the table that -contains the secrets. As with the other providers, an overloaded methods allows you to retrieve -a `DynamoDbProvider` providing a client if you need to configure it yourself. +### AppConfig -=== "DynamoDbProvider" +=== "@AppConfigParam annotation" - ```java hl_lines="6 9" - import software.amazon.lambda.powertools.parameters.DynamoDbProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + ```java hl_lines="8 9" + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import software.amazon.lambda.powertools.parameters.appconfig.AppConfigParam; - public class AppWithDynamoDbParameters implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the DynamoDbProvider - DynamoDbProvider ddbProvider = ParamManager.getDynamoDbProvider("my-parameters-table"); + public class ParametersFunction implements RequestHandler<String, String> { - // Retrieve a single parameter - String value = ddbProvider.get("my-key"); - } - ``` - -=== "DynamoDbProvider with a custom client" + // Annotation-style injection from AppConfig + @AppConfigParam(application = "my-app", environment = "my-env", key = "myKey") + String appConfigParam; - ```java hl_lines="9 10 11 12 15 18" - import software.amazon.lambda.powertools.parameters.DynamoDbProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; - import software.amazon.awssdk.services.dynamodb.DynamoDbClient; - import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; - import software.amazon.awssdk.regions.Region; - - public class AppWithDynamoDbParameters implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get a DynamoDB Client with an explicit region - DynamoDbClient ddbClient = DynamoDbClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.EU_CENTRAL_2) - .build(); - - // Get an instance of the DynamoDbProvider - DynamoDbProvider provider = ParamManager.getDynamoDbProvider(ddbClient, "test-table"); - - // Retrieve a single parameter - String value = ddbProvider.get("my-key"); - } + public string handleRequest(String request, Context context) { + return appConfigParam; // Request handler simply returns our configuration value + } + } ``` -## AppConfig -To get parameters stored in AppConfig, use `getAppConfigProvider`, providing the application and environment -name to retrieve configuration from. As with the other providers, an overloaded method allows you to retrieve -an `AppConfigProvider` providing a client if you need to configure it yourself. +=== "AppConfigProvider class" -=== "AppConfigProvider" - - ```java hl_lines="6 9" - import software.amazon.lambda.powertools.parameters.AppConfigProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; - - public class AppWitAppConfigParameters implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the AppConfigProvider - AppConfigProvider appConfigProvider = ParamManager.getAppConfigProvider("my-environment", "my-app"); + ```java hl_lines="12-15 19-20" + import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; - // Retrieve a single parameter - String value = appConfigProvider.get("my-key"); - } - ``` - -=== "AppConfigProvider with a custom client" - - ```java hl_lines="9 10 11 12 15 18" - import software.amazon.lambda.powertools.parameters.AppConfigProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; - import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; - import software.amazon.awssdk.regions.Region; - - public class AppWithDynamoDbParameters implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an AppConfig Client with an explicit region - AppConfigDataClient appConfigDataClient = AppConfigDataClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.EU_CENTRAL_2) - .build(); + import software.amazon.lambda.powertools.parameters.appconfig.AppConfigProvider; - // Get an instance of the DynamoDbProvider - AppConfigProvider appConfigProvider = ParamManager.getAppConfigProvider(appConfigDataClient, "my-environment", "my-app"); + public class RequestHandlerWithParams implements RequestHandler<String, String> { + + // Get an instance of the AppConfigProvider. We can provide a custom client here if we want, + // for instance to use a particular region. + AppConfigProvider appConfigProvider = AppConfigProvider + .builder() + .withClient(AppConfigDataClient.builder().build()) + .build(); + + public String handleRequest(String input, Context context) { + // Retrieve a single param + String value = appConfigProvider + .get("/my/secret"); - // Retrieve a single parameter - String value = appConfigProvider.get("my-key"); - } + // Return the result + return value; + } + } ``` - ## Advanced configuration ### Caching +Each provider uses the `CacheManager` to cache parameter values. When a value is retrieved using from the provider, a +custom cache duration can be provided using `withMaxAge(duration, unit)`. -By default, all parameters and their corresponding values are cached for 5 seconds. - -You can customize this default value using `defaultMaxAge`. You can also customize this value for each parameter using -`withMaxAge`. - -=== "Provider with default Max age" - - ```java hl_lines="9" - import software.amazon.lambda.powertools.parameters.SecretsProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; +If this is not specified, the default value set on the `CacheManager` itself will be used. This default can be customized +by calling `setDefaultExpirationTime(duration, unit)` on the `CacheManager`. - public class AppWithSecrets implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the Secrets Provider - SecretsProvider secretsProvider = ParamManager.getSecretsProvider() - .defaultMaxAge(10, ChronoUnit.SECONDS); +=== "Customize Cache" - String value = secretsProvider.get("/my/secret"); + ```java hl_lines="9 10 14 19 22-25" + import java.time.Duration; + import software.amazon.lambda.powertools.parameters.appconfig.AppConfigProvider; + import software.amazon.lambda.powertools.parameters.cache.CacheManager; - } - ``` - -=== "Provider with age for each param" - - ```java hl_lines="8" - import software.amazon.lambda.powertools.parameters.SecretsProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; - - public class AppWithSecrets implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - SecretsManagerClient client = SecretsManagerClient.builder().region(Region.EU_CENTRAL_1).build(); + public class CustomizeCache { - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(client); - - String value = secretsProvider.withMaxAge(10, ChronoUnit.SECONDS).get("/my/secret"); + public void CustomizeCache() { + + CacheManager cacheManager = new CacheManager(); + cacheManager.setDefaultExpirationTime(Duration.ofSeconds(10)); + AppConfigProvider paramProvider = AppConfigProvider + .builder() + .withCacheManager(cacheManager) + .withClient(AppConfigDataClient.builder().build()) + .build(); + + // Will use the default specified above - 10 seconds + String myParam1 = paramProvider.get("myParam1"); + + // Will override the default above + String myParam2 = paramProvider + .withMaxAge(20, ChronoUnit.SECONDS) + .get("myParam2"); + + return myParam2; + } } ``` + ### Transform values Parameter values can be transformed using ```withTransformation(transformerClass)```. -Base64 and JSON transformations are provided. For more complex transformation, you need to specify how to deserialize- +Base64 and JSON transformations are provided. For more complex transformation, you need to specify how to deserialize. -!!! warning "`SSMProvider.getMultiple()` does not support transformation and will return simple Strings." +!!! warning "`getMultiple()` does not support transformation and will return simple Strings." === "Base64 Transformation" ```java @@ -431,14 +434,16 @@ Base64 and JSON transformations are provided. For more complex transformation, y .get("/my/parameter/json", MyObj.class); ``` -## Write your own Transformer + + +#### Create your own Transformer You can write your own transformer, by implementing the `Transformer` interface and the `applyTransformation()` method. For example, if you wish to deserialize XML into an object. === "XmlTransformer.java" - ```java hl_lines="1" + ```java public class XmlTransformer<T> implements Transformer<T> { private final XmlMapper mapper = new XmlMapper(); @@ -470,172 +475,142 @@ To simplify the use of the library, you can chain all method calls before a get. ```java ssmProvider - .defaultMaxAge(10, SECONDS) // will set 10 seconds as the default cache TTL .withMaxAge(1, MINUTES) // will set the cache TTL for this value at 1 minute .withTransformation(json) // json is a static import from Transformer.json .withDecryption() // enable decryption of the parameter value .get("/my/param", MyObj.class); // finally get the value ``` -## Create your own provider +### Create your own Provider +You can create your own custom parameter store provider by implementing a handful of classes: -You can create your own custom parameter store provider by inheriting the ```BaseProvider``` class and implementing the -```String getValue(String key)``` method to retrieve data from your underlying store. All transformation and caching logic is handled by the get() methods in the base class. - -=== "Example implementation using S3 as a custom parameter" +=== "CustomProvider.java" ```java - public class S3Provider extends BaseProvider { - - private final S3Client client; - private String bucket; - - S3Provider(CacheManager cacheManager) { - this(cacheManager, S3Client.create()); - } - - S3Provider(CacheManager cacheManager, S3Client client) { - super(cacheManager); - this.client = client; + import java.util.Map; + import software.amazon.lambda.powertools.parameters.BaseProvider; + import software.amazon.lambda.powertools.parameters.cache.CacheManager; + import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + + /** + * Our custom parameter provider itself. This does the heavy lifting of retrieving + * parameters from whatever our underlying parameter store might be. + **/ + public class CustomProvider extends BaseProvider { + + public CustomProvider(CacheManager cacheManager, TransformationManager transformationManager) { + super(cacheManager, transformationManager); } - - public S3Provider withBucket(String bucket) { - this.bucket = bucket; - return this; + + public CustomProviderBuilder builder() { + return new CustomProviderBuilder(); } @Override protected String getValue(String key) { - if (bucket == null) { - throw new IllegalStateException("A bucket must be specified, using withBucket() method"); - } - - GetObjectRequest request = GetObjectRequest.builder().bucket(bucket).key(key).build(); - ResponseBytes<GetObjectResponse> response = client.getObject(request, ResponseTransformer.toBytes()); - return response.asUtf8String(); + throw new RuntimeException("TODO - return a single value"); } @Override protected Map<String, String> getMultipleValues(String path) { - if (bucket == null) { - throw new IllegalStateException("A bucket must be specified, using withBucket() method"); - } - - ListObjectsV2Request listRequest = ListObjectsV2Request.builder().bucket(bucket).prefix(path).build(); - List<S3Object> s3Objects = client.listObjectsV2(listRequest).contents(); - - Map<String, String> result = new HashMap<>(); - s3Objects.forEach(s3Object -> { - result.put(s3Object.key(), getValue(s3Object.key())); - }); - - return result; + throw new RuntimeException("TODO - Optional - return multiple values"); } - - @Override - protected void resetToDefaults() { - super.resetToDefaults(); - bucket = null; - } - } ``` -=== "Using custom parameter store" - - ```java hl_lines="3" - S3Provider provider = new S3Provider(ParamManager.getCacheManager()); - - provider.setTransformationManager(ParamManager.getTransformationManager()); - - String value = provider.withBucket("myBucket").get("myKey"); - ``` - -## Annotation - -You can make use of the annotation `@Param` to inject a parameter value in a variable. +=== "CustomProviderBuilder.java" -By default, it will use `SSMProvider` to retrieve the value from AWS System Manager Parameter Store. -You could specify a different provider as long as it extends `BaseProvider` and/or a `Transformer`. + ```java + /** + * Provides a builder-style interface to configure our @{link CustomProvider}. + **/ + public class CustomProviderBuilder { + private CacheManager cacheManager; + private TransformationManager transformationManager; + + /** + * Create a {@link CustomProvider} instance. + * + * @return a {@link CustomProvider} + */ + public CustomProvider build() { + if (cacheManager == null) { + cacheManager = new CacheManager(); + } + return new CustomProvider(cacheManager, transformationManager); + } -=== "Param Annotation" + /** + * Provide a CacheManager to the {@link CustomProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) + */ + public CustomProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } - ```java hl_lines="3" - public class AppWithAnnotation implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Param(key = "/my/parameter/json") - ObjectToDeserialize value; - + /** + * Provide a transformationManager to the {@link CustomProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) + */ + public CustomProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } } ``` -=== "Custom Provider Usage" - - ```java hl_lines="3" - public class AppWithAnnotation implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Param(key = "/my/parameter/json" provider = SecretsProvider.class, transformer = JsonTransformer.class) - ObjectToDeserialize value; - +=== "CustomProviderParam.java" + + ```java + import java.lang.annotation.ElementType; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + import java.lang.annotation.Target; + import software.amazon.lambda.powertools.parameters.transform.Transformer; + + /** + * Aspect to inject a parameter from our custom provider. Note that if you + * want to implement a provider _without_ an Aspect and field injection, you can + * skip implementing both this and the {@link CustomProviderAspect} class. + **/ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface CustomProviderParam { + // The parameter key + String key(); + + // The transformer to use + Class<? extends Transformer> transformer() default Transformer.class; } ``` - In this case ```SecretsProvider``` will be used to retrieve a raw value that is then trasformed into the target Object by using ```JsonTransformer```. - To show the convenience of the annotation compare the following two code snippets. - - -### Install +=== "CustomProviderAspect.java" -If you want to use the ```@Param``` annotation in your project add configuration to compile-time weave (CTW) the powertools-parameters aspects into your project. - -=== "Maven" + ```java - ```xml - <build> - <plugins> - ... - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - ... - <aspectLibraries> - ... - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` + /** + * Aspect to inject a parameter from our custom provider where the {@link CustomProviderParam} + * annotation is used. + **/ + @Aspect + public class CustomProviderAspect extends BaseParamAspect { -=== "Gradle" - - ```groovy - plugins{ - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.3.0' - } - - repositories { - mavenCentral() - } + @Pointcut("get(* *) && @annotation(ddbConfigParam)") + public void getParam(CustomProviderParam customConfigParam) { + } + + @Around("getParam(customConfigParam)") + public Object injectParam(final ProceedingJoinPoint joinPoint, final CustomProviderParam customConfigParam) { + BaseProvider provider = CustomProvider.builder().build(); - dependencies { - ... - aspect 'software.amazon.lambda:powertools-parameters:{{ powertools.version }}' - implementation 'org.aspectj:aspectjrt:1.9.19' + return getAndTransform(customConfigParam.key(), ddbConfigParam.transformer(), provider, + (FieldSignature) joinPoint.getSignature()); + } + } - ``` \ No newline at end of file + ``` diff --git a/docs/utilities/sqs_batch.md b/docs/utilities/sqs_batch.md deleted file mode 100644 index 658f7b085..000000000 --- a/docs/utilities/sqs_batch.md +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: SQS Batch Processing (Deprecated) -description: Utility ---- - -!!! warning - The SQS batch module is now deprecated and will be removed in v2 of the library. Use the [batch module](batch.md), - and check out **[migrating to the batch library](#migrating-to-the-batch-library)** for migration instructions. - -The SQS batch processing utility provides a way to handle partial failures when processing batches of messages from SQS. -The utility handles batch processing for both -[standard](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/standard-queues.html) and -[FIFO](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html) SQS queues. - -**Key Features** - -* Prevent successfully processed messages from being returned to SQS -* A simple interface for individually processing messages from a batch - -**Background** - -When using SQS as a Lambda event source mapping, Lambda functions can be triggered with a batch of messages from SQS. -If your function fails to process any message from the batch, the entire batch returns to your SQS queue, and your -Lambda function will be triggered with the same batch again. With this utility, messages within a batch will be handled individually - only messages that were not successfully processed -are returned to the queue. - -!!! warning - While this utility lowers the chance of processing messages more than once, it is not guaranteed. We recommend implementing processing logic in an idempotent manner wherever possible. - More details on how Lambda works with SQS can be found in the [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html) - -## Install - -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. - -=== "Maven Java 11+" - - ```xml hl_lines="3-7 16 18 24-27"" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>11</source> <!-- or higher --> - <target>11</target> <!-- or higher --> - <complianceLevel>11</complianceLevel> <!-- or higher --> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Maven Java 1.8" - - ```xml hl_lines="3-7 16 18 24-27" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 11 // or higher - targetCompatibility = 11 // or higher - ``` - -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - -## IAM Permissions - -This utility requires additional permissions to work as expected. Lambda functions using this utility require the `sqs:DeleteMessageBatch` permission. - -If you are also using [nonRetryableExceptions](#move-non-retryable-messages-to-a-dead-letter-queue) attribute, utility will need additional permission of `sqs:GetQueueAttributes` on source SQS. -It also needs `sqs:SendMessage` and `sqs:SendMessageBatch` on configured dead letter queue. - -If source or dead letter queue is configured to use encryption at rest using [AWS Key Management Service (KMS)](https://aws.amazon.com/kms/), function will need additional permissions of -`kms:GenerateDataKey` and `kms:Decrypt` on the KMS key being used for encryption. Refer [docs](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-key-management.html#compatibility-with-aws-services) for more details. - -Refer [example project](https://github.com/aws-samples/aws-lambda-powertools-examples/blob/main/java/SqsBatchProcessing/template.yaml#L105) for policy details example. - - -## Processing messages from SQS - -You can use either **[SqsBatch annotation](#sqsbatch-annotation)**, or **[SqsUtils Utility API](#sqsutils-utility-api)** as a fluent API. - -Both have nearly the same behaviour when it comes to processing messages from the batch: - -* **Entire batch has been successfully processed**, where your Lambda handler returned successfully, we will let SQS delete the batch to optimize your cost -* **Entire Batch has been partially processed successfully**, where exceptions were raised within your `SqsMessageHandler` interface implementation, we will: - - **1)** Delete successfully processed messages from the queue by directly calling `sqs:DeleteMessageBatch` - - **2)** If a message with a [message group ID](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/using-messagegroupid-property.html) fails, - the processing of the batch will be stopped and the remainder of the messages will be returned to SQS. - This behaviour [is required to handle SQS FIFO queues](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting). - - **3)** if non retryable exceptions occur, messages resulting in configured exceptions during processing will be immediately moved to the dead letter queue associated to the source SQS queue or deleted from the source SQS queue if `deleteNonRetryableMessageFromQueue` is set to `true`. - - **4)** Raise `SQSBatchProcessingException` to ensure failed messages return to your SQS queue - -The only difference is that **SqsUtils Utility API** will give you access to return from the processed messages if you need. Exception `SQSBatchProcessingException` thrown from the -utility will have access to both successful and failed messaged along with failure exceptions. - -## Functional Interface SqsMessageHandler - -Both [annotation](#sqsbatch-annotation) and [SqsUtils Utility API](#sqsutils-utility-api) requires an implementation of functional interface `SqsMessageHandler`. - -This implementation is responsible for processing each individual message from the batch, and to raise an exception if unable to process any of the messages sent. - -**Any non-exception/successful return from your record handler function** will instruct utility to queue up each individual message for deletion. - -### SqsBatch annotation - -When using this annotation, you need provide a class implementation of `SqsMessageHandler` that will process individual messages from the batch - It should raise an exception if it is unable to process the record. - -All records in the batch will be passed to this handler for processing, even if exceptions are thrown - Here's the behaviour after completing the batch: - -* **Any successfully processed messages**, we will delete them from the queue via `sqs:DeleteMessageBatch`. -* **if, nonRetryableExceptions attribute is used**, messages resulting in configured exceptions during processing will be immediately moved to the dead letter queue associated to the source SQS queue or deleted from the source SQS queue if `deleteNonRetryableMessageFromQueue` is set to `true`. -* **Any unprocessed messages detected**, we will raise `SQSBatchProcessingException` to ensure failed messages return to your SQS queue. - -!!! warning - You will not have access to the **processed messages** within the Lambda Handler - all processing logic will and should be performed by the implemented `#!java SqsMessageHandler#process()` function. - -=== "AppSqsEvent.java" - - ```java hl_lines="7" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler<SQSEvent, String> { - @Override - @SqsBatch(SampleMessageHandler.class) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } - } - ``` - -=== "AppSqsEventWithNonRetryableExceptions.java" - - ```java hl_lines="7 21" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler<SQSEvent, String> { - @Override - @SqsBatch(value = SampleMessageHandler.class, nonRetryableExceptions = {IllegalArgumentException.class}) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - - if(/**Business validation failure**/) { - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - return returnVal; - } - } - } - ``` - - -### SqsUtils Utility API - -If you require access to the result of processed messages, you can use this utility. The result from calling **`#!java SqsUtils#batchProcessor()`** on the context manager will be a list of all the return values -from your **`#!java SqsMessageHandler#process()`** function. - -You can also use the utility in functional way by providing inline implementation of functional interface **`#!java SqsMessageHandler#process()`** - - -=== "Utility API" - - ```java hl_lines="4" - public class AppSqsEvent implements RequestHandler<SQSEvent, List<String>> { - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, SampleMessageHandler.class); - - return returnValues; - } - - public class SampleMessageHandler implements SqsMessageHandler<String> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } - } - ``` - -=== "Function implementation" - - ```java hl_lines="5 6 7 8 9 10" - public class AppSqsEvent implements RequestHandler<SQSEvent, List<String>> { - - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, (message) -> { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - }); - - return returnValues; - } - } - ``` - -## Passing custom SqsClient - -If you need to pass custom SqsClient such as region to the SDK, you can pass your own `SqsClient` to be used by utility either for -**[SqsBatch annotation](#sqsbatch-annotation)**, or **[SqsUtils Utility API](#sqsutils-utility-api)**. - -=== "App.java" - - ```java hl_lines="3 4" - public class AppSqsEvent implements RequestHandler<SQSEvent, List<String>> { - static { - SqsUtils.overrideSqsClient(SqsClient.builder() - .build()); - } - - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, SampleMessageHandler.class); - - return returnValues; - } - - public class SampleMessageHandler implements SqsMessageHandler<String> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } - } - ``` - -## Suppressing exceptions - -If you want to disable the default behavior where `SQSBatchProcessingException` is raised if there are any exception, you can pass the `suppressException` boolean argument. - -=== "Within SqsBatch annotation" - - ```java hl_lines="2" - @Override - @SqsBatch(value = SampleMessageHandler.class, suppressException = true) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - ``` - -=== "Within SqsUtils Utility API" - - ```java hl_lines="3" - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, true, SampleMessageHandler.class); - - return returnValues; - } - ``` - -## Move non retryable messages to a dead letter queue - -If you want certain exceptions to be treated as permanent failures during batch processing, i.e. exceptions where the result of retrying will -always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, you can use `SqsBatch#nonRetryableExceptions()` -to configure such exceptions. - -If you want such messages to be deleted instead, set `SqsBatch#deleteNonRetryableMessageFromQueue()` to `true`. By default, its value is `false`. - -Same capability is also provided by [SqsUtils Utility API](#sqsutils-utility-api). - -!!! info - Make sure the lambda function has required permissions needed by utility. Refer [this section](#iam-permissions). - -=== "SqsBatch annotation" - - ```java hl_lines="7 21" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler<SQSEvent, String> { - @Override - @SqsBatch(value = SampleMessageHandler.class, nonRetryableExceptions = {IllegalArgumentException.class}) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - - if(/**Business validation failure**/) { - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - return returnVal; - } - } - } - ``` - -=== "SqsBatch API" - - ```java hl_lines="9 23" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler<SQSEvent, String> { - @Override - public String handleRequest(SQSEvent input, Context context) { - - SqsUtils.batchProcessor(input, BatchProcessor.class, IllegalArgumentException.class); - - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - - if(/**Business validation failure**/) { - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - return returnVal; - } - } - } - ``` - -## Migrating to the Batch Library -The [batch processing library](batch.md) provides a way to process messages and gracefully handle partial failures for -SQS, Kinesis Streams, and DynamoDB Streams batch sources. In comparison the legacy SQS Batch library, it relies on -[Lambda partial batch responses](https://aws.amazon.com/about-aws/whats-new/2021/11/aws-lambda-partial-batch-response-sqs-event-source/), -which allows the library to provide a simpler, reliable interface for processing batches. - -In order to get started, check out the [processing messages from SQS](batch/#processing-messages-from-sqs) documentation. -In most cases, you will simply be able to retain your existing batch message handler function, and wrap it with the new -batch processing interface. Unlike this module, As the batch processor uses *partial batch responses* to communicate to -Lambda which messages have been processed and must be removed from the queue, the return of the handler's process function -must be returned to Lambda. - -The new library also no longer requires the `SQS:DeleteMessage` action on the Lambda function's role policy, as Lambda -itself now manages removal of messages from the queue. - -!!! info - Some tuneables from this library are no longer provided. - - * **Non-retryable Exceptions** - there is no mechanism to indicate in a partial batch response that a particular message - should not be retried and instead moved to DLQ - a message either succeeds, or fails and is retried. A message - will be moved to the DLQ once the normal retry process has expired. - * **Suppress Exception** - The new batch processor does not throw an exception on failure of a handler. Instead, - its result must be returned by your code from your message handler to Lambda, so that Lambda can manage - the completed messages and retry behaviour. \ No newline at end of file diff --git a/docs/utilities/sqs_large_message_handling.md b/docs/utilities/sqs_large_message_handling.md deleted file mode 100644 index 0924d01cf..000000000 --- a/docs/utilities/sqs_large_message_handling.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: SQS Large Message Handling (Deprecated) -description: Utility ---- - -!!! warning - This module is now deprecated and will be removed in version 2. - See [Large Message Handling](large_messages.md) and - [the migration guide](http://localhost:8000/lambda-java/utilities/large_messages/#migration-from-the-sqs-large-message-utility) - for the new module (`powertools-large-messages`) documentation - -The large message handling utility handles SQS messages which have had their payloads -offloaded to S3 due to them being larger than the SQS maximum. - -The utility automatically retrieves messages which have been offloaded to S3 using the -[amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) -client library. Once the message payloads have been processed successful the -utility can delete the message payloads from S3. - -This utility is compatible with versions *[1.1.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib)* of -amazon-sqs-java-extended-client-lib. - -=== "Maven" - -```xml - -<dependency> - <groupId>com.amazonaws</groupId> - <artifactId>amazon-sqs-java-extended-client-lib</artifactId> - <version>1.1.0</version> -</dependency> -``` -=== "Gradle" - - ```groovy - dependencies { - implementation 'com.amazonaws:amazon-sqs-java-extended-client-lib:1.1.0' - } - ``` - -## Install -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. - -=== "Maven Java 11+" - -```xml hl_lines="3-7 16 18 24-27" -<dependencies> -... -<dependency> -<groupId>software.amazon.lambda</groupId> -<artifactId>powertools-sqs</artifactId> -<version>{{ powertools.version }}</version> -</dependency> -... -</dependencies> -... -<!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> -<build> -<plugins> -... -<plugin> -<groupId>dev.aspectj</groupId> -<artifactId>aspectj-maven-plugin</artifactId> -<version>1.13.1</version> -<configuration> -<source>11</source> <!-- or higher --> -<target>11</target> <!-- or higher --> -<complianceLevel>11</complianceLevel> <!-- or higher --> -<aspectLibraries> -<aspectLibrary> -<groupId>software.amazon.lambda</groupId> -<artifactId>powertools-sqs</artifactId> -</aspectLibrary> -</aspectLibraries> -</configuration> -<executions> -<execution> -<goals> -<goal>compile</goal> -</goals> -</execution> -</executions> -</plugin> -... -</plugins> -</build> -``` - -=== "Maven Java 1.8" - - ```xml hl_lines="3-7 16 18 24-27" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 11 // or higher - targetCompatibility = 11 // or higher - ``` - -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - -## Lambda handler - -The annotation `@SqsLargeMessage` should be used with the handleRequest method of a class -which implements `com.amazonaws.services.lambda.runtime.RequestHandler` with -`com.amazonaws.services.lambda.runtime.events.SQSEvent` as the first parameter. - -=== "SqsMessageHandler.java" - - ```java hl_lines="6" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - - public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - - @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - // process messages - - return "ok"; - } - } - ``` - -`@SqsLargeMessage` creates a default S3 Client `AmazonS3 amazonS3 = AmazonS3ClientBuilder.defaultClient()`. - -!!! tip -When the Lambda function is invoked with an event from SQS, each received record -in the SQSEvent is checked to see to validate if it is offloaded to S3. -If it does then `getObject(bucket, key)` will be called, and the payload retrieved. -If there is an error during this process then the function will fail with a `FailedProcessingLargePayloadException` -exception. - - If the request handler method returns without error then each payload will be - deleted from S3 using `deleteObject(bucket, key)` - -To disable deletion of payloads setting the following annotation parameter: - -=== "Disable payload deletion" - - ```java hl_lines="3" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - - @SqsLargeMessage(deletePayloads=false) - public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - - } - ``` - -## Utility - -If you want to avoid using annotation and have control over error that can happen during payload enrichment use `SqsUtils.enrichedMessageFromS3()`. -It provides you access with a list of `SQSMessage` object enriched from S3 payload. - -Original `SQSEvent` object is never mutated. You can also control if the S3 payload should be deleted after successful -processing. - -=== "Functional API without annotation" - - ```java hl_lines="9 10 11 14 15 16 17 18 19 20 21 22 27 28 29" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - - @Override - public String handleRequest(SQSEvent sqsEvent, Context context) { - - Map<String, String> sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> { - // Some business logic - Map<String, String> someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - // Do not delete payload after processing. - Map<String, String> sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, false, sqsMessages -> { - // Some business logic - Map<String, String> someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - // Better control over exception during enrichment - try { - // Do not delete payload after processing. - SqsUtils.enrichedMessageFromS3(sqsEvent, false, sqsMessages -> { - // Some business logic - }); - } catch (FailedProcessingLargePayloadException e) { - // handle any exception. - } - - return "ok"; - } - } - ``` - -## Overriding the default S3Client - -If you require customisations to the default S3Client, you can create your own `S3Client` and pass it to be used by utility either for -**[SqsLargeMessage annotation](#lambda-handler)**, or **[SqsUtils Utility API](#utility)**. - -=== "App.java" - - ```java hl_lines="4 5 11" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - - static { - SqsUtils.overrideS3Client(S3Client.builder() - .build()); - } - - public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - - @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - // process messages - - return "ok"; - } - } - ``` \ No newline at end of file diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index 928ffb6c8..8e0d2c631 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -8,13 +8,12 @@ This utility provides JSON Schema validation for payloads held within events and **Key features** * Validate incoming events and responses -* Built-in validation for most common events (API Gateway, SNS, SQS, ...) +* Built-in validation for most common events (API Gateway, SNS, SQS, ...) and support for partial batch failures (SQS, Kinesis) * JMESPath support validate only a sub part of the event ## Install -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. -=== "Maven Java 11+" +=== "Maven" ```xml hl_lines="3-7 16 18 24-27" <dependencies> ... @@ -27,13 +26,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </dependencies> ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach with ValidationUtils.validate() --> <build> <plugins> ... <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>11</source> <!-- or higher --> <target>11</target> <!-- or higher --> @@ -45,6 +45,14 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -58,57 +66,12 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl </build> ``` -=== "Maven Java 1.8" +=== "Gradle" - ```xml hl_lines="3-7 16 18 24-27" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-validation</artifactId> - <version>{{ powertools.version }}</version> - </dependency> - ... - </dependencies> - ... - <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> - <build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-validation</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" - - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach with ValidationUtils.validate() } repositories { @@ -116,47 +79,38 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl } dependencies { - aspect 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' // Not needed when using the functional approach with ValidationUtils.validate() + implementation 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach } sourceCompatibility = 11 // or higher targetCompatibility = 11 // or higher ``` -=== "Gradle Java 1.8" +## Validating events - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` +You can validate inbound and outbound events using either the `@Validation` annotation or the functional approach with `ValidationUtils.validate()` methods: +- **@Validation annotation** - Simpler syntax with automatic validation, but requires AspectJ configuration +- **ValidationUtils.validate()** - No AspectJ required, provides more control over the validation process such as handling validation errors -## Validating events +We support JSON schema version 4, 6, 7, 2019-09 and 2020-12 using the [NetworkNT JSON Schema Validator](https://github.com/networknt/json-schema-validator) ([Compatibility with JSON Schema versions](https://github.com/networknt/json-schema-validator/blob/master/doc/compatibility.md)). -You can validate inbound and outbound events using `@Validation` annotation. +The validator is configured to enable format assertions by default even for 2019-09 and 2020-12. -You can also use the `Validator#validate()` methods, if you want more control over the validation process such as handling a validation error. +### Validation annotation -We support JSON schema version 4, 6, 7 and 201909 (from [jmespath-jackson library](https://github.com/burtcorp/jmespath-java)). +The `@Validation` annotation is used to validate either inbound events or functions' response. -### Validation annotation +It will fail fast if an event or response doesn't conform with given JSON Schema. For most type of events a `ValidationException` will be thrown. -`@Validation` annotation is used to validate either inbound events or functions' response. +For API gateway events associated with REST APIs and HTTP APIs - `APIGatewayProxyRequestEvent` and `APIGatewayV2HTTPEvent` - the `@Validation` +annotation will build and return a custom 400 / "Bad Request" response, with a body containing the validation errors. This saves you from having +to catch the validation exception and map it back to a meaningful user error yourself. -It will fail fast with `ValidationException` if an event or response doesn't conform with given JSON Schema. +For SQS and Kinesis events - `SQSEvent` and `KinesisEvent`- the `@Validation` annotation will add the invalid messages +to the batch item failures list in the response, respectively `SQSBatchResponse` and `StreamsEventResponse` +and removed from the event so that you do not process them within the handler. While it is easier to specify a json schema file in the classpath (using the notation `"classpath:/path/to/schema.json"`), you can also provide a JSON String containing the schema. @@ -178,11 +132,11 @@ While it is easier to specify a json schema file in the classpath (using the not **NOTE**: It's not a requirement to validate both inbound and outbound schemas - You can either use one, or both. -### Validate function +### Functional approach with ValidationUtils -Validate standalone function is used within the Lambda handler, or any other methods that perform data validation. +The `ValidationUtils.validate()` method provides a functional approach that can be used within the Lambda handler or any other methods that perform data validation. This approach does not require AspectJ configuration. -You can also gracefully handle schema validation errors by catching `ValidationException`. +With this approach, you can gracefully handle schema validation errors by catching `ValidationException`. === "MyFunctionHandler.java" @@ -212,33 +166,33 @@ You can also gracefully handle schema validation errors by catching `ValidationE For the following events and responses, the Validator will automatically perform validation on the content. -** Events ** - - Type of event | Class | Path to content | - ------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- - API Gateway REST | APIGatewayProxyRequestEvent | `body` - API Gateway HTTP | APIGatewayV2HTTPEvent | `body` - Application Load Balancer | ApplicationLoadBalancerRequestEvent | `body` - Cloudformation Custom Resource | CloudFormationCustomResourceEvent | `resourceProperties` - CloudWatch Logs | CloudWatchLogsEvent | `awslogs.powertools_base64_gzip(data)` - EventBridge / Cloudwatch | ScheduledEvent | `detail` - Kafka | KafkaEvent | `records[*][*].value` - Kinesis | KinesisEvent | `Records[*].kinesis.powertools_base64(data)` - Kinesis Firehose | KinesisFirehoseEvent | `Records[*].powertools_base64(data)` - Kinesis Analytics from Firehose | KinesisAnalyticsFirehoseInputPreprocessingEvent | `Records[*].powertools_base64(data)` - Kinesis Analytics from Streams | KinesisAnalyticsStreamsInputPreprocessingEvent | `Records[*].powertools_base64(data)` - SNS | SNSEvent | `Records[*].Sns.Message` - SQS | SQSEvent | `Records[*].body` - -** Responses ** - - Type of response | Class | Path to content (envelope) - ------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- - API Gateway REST | APIGatewayProxyResponseEvent} | `body` - API Gateway HTTP | APIGatewayV2HTTPResponse} | `body` - API Gateway WebSocket | APIGatewayV2WebSocketResponse} | `body` - Load Balancer | ApplicationLoadBalancerResponseEvent} | `body` - Kinesis Analytics | KinesisAnalyticsInputPreprocessingResponse} | `Records[*].powertools_base64(data)`` +**Events** + +| Type of event | Class | Path to content | +|---------------------------------|-------------------------------------------------|----------------------------------------------| +| API Gateway REST | APIGatewayProxyRequestEvent | `body` | +| API Gateway HTTP | APIGatewayV2HTTPEvent | `body` | +| Application Load Balancer | ApplicationLoadBalancerRequestEvent | `body` | +| Cloudformation Custom Resource | CloudFormationCustomResourceEvent | `resourceProperties` | +| CloudWatch Logs | CloudWatchLogsEvent | `awslogs.powertools_base64_gzip(data)` | +| EventBridge / Cloudwatch | ScheduledEvent | `detail` | +| Kafka | KafkaEvent | `records[*][*].value` | +| Kinesis | KinesisEvent | `Records[*].kinesis.powertools_base64(data)` | +| Kinesis Firehose | KinesisFirehoseEvent | `Records[*].powertools_base64(data)` | +| Kinesis Analytics from Firehose | KinesisAnalyticsFirehoseInputPreprocessingEvent | `Records[*].powertools_base64(data)` | +| Kinesis Analytics from Streams | KinesisAnalyticsStreamsInputPreprocessingEvent | `Records[*].powertools_base64(data)` | +| SNS | SNSEvent | `Records[*].Sns.Message` | +| SQS | SQSEvent | `Records[*].body` | + +**Responses** + +| Type of response | Class | Path to content (envelope) | +|-----------------------|---------------------------------------------|---------------------------------------| +| API Gateway REST | APIGatewayProxyResponseEvent} | `body` | +| API Gateway HTTP | APIGatewayV2HTTPResponse} | `body` | +| API Gateway WebSocket | APIGatewayV2WebSocketResponse} | `body` | +| Load Balancer | ApplicationLoadBalancerResponseEvent} | `body` | +| Kinesis Analytics | KinesisAnalyticsInputPreprocessingResponse} | `Records[*].powertools_base64(data)` | ## Custom events and responses @@ -292,7 +246,8 @@ and [function](https://jmespath.org/tutorial.html#functions) expressions, where ## Change the schema version -By default, powertools-validation is configured with [V7](https://json-schema.org/draft-07/json-schema-release-notes.html). +By default, powertools-validation is configured to use [V7](https://json-schema.org/draft-07/json-schema-release-notes.html) as the default dialect if [`$schema`](https://json-schema.org/understanding-json-schema/reference/schema#schema) is not explicitly specified within the schema. If [`$schema`](https://json-schema.org/understanding-json-schema/reference/schema#schema) is explicitly specified within the schema, the validator will use the specified dialect. + You can use the `ValidationConfig` to change that behaviour. === "Handler with custom schema version" @@ -340,3 +295,53 @@ If you need to configure the Jackson ObjectMapper, you can use the `ValidationCo } } ``` + +## Advanced + +### Lambda SnapStart priming + +The Validation utility integrates with AWS Lambda SnapStart to improve restore durations. To make sure the SnapStart priming logic of this utility runs correctly, you need an explicit reference to `ValidationConfig` in your code to allow the library to register before SnapStart takes a memory snapshot. Learn more about what priming is in this [blog post](https://aws.amazon.com/blogs/compute/optimizing-cold-start-performance-of-aws-lambda-using-advanced-priming-strategies-with-snapstart/){target="_blank"}. + +If you don't set a custom `ValidationConfig` in your code yet, make sure to reference `ValidationConfig` in your Lambda handler initialization code. This can be done by adding one of the following lines to your handler class: + +=== "Constructor" + + ```java hl_lines="7" + import software.amazon.lambda.powertools.validation.Validation; + import software.amazon.lambda.powertools.validation.ValidationConfig; + + public class MyFunctionHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public MyFunctionHandler() { + ValidationConfig.get(); // Ensure ValidationConfig is loaded for SnapStart + } + + @Override + @Validation(inboundSchema = "classpath:/schema_in.json", outboundSchema = "classpath:/schema_out.json") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + // ... + return something; + } + } + ``` + +=== "Static Initializer" + + ```java hl_lines="7" + import software.amazon.lambda.powertools.validation.Validation; + import software.amazon.lambda.powertools.validation.ValidationConfig; + + public class MyFunctionHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + static { + ValidationConfig.get(); // Ensure ValidationConfig is loaded for SnapStart + } + + @Override + @Validation(inboundSchema = "classpath:/schema_in.json", outboundSchema = "classpath:/schema_out.json") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + // ... + return something; + } + } + ``` diff --git a/examples/README.md b/examples/README.md index 0744c2bb1..727bc652e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,24 +1,35 @@ # Powertools for AWS Lambda (Java) Examples This directory holds example projects demoing different components of the Powertools for AWS Lambda (Java). + Each example can be copied from its subdirectory and used independently of the rest of this repository. ## Examples -* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools - * [SAM](./powertools-examples-core/sam) - * [CDK](./powertools-examples-core/cdk) +* [powertools-examples-core-utilities](powertools-examples-core-utilities) - Demonstrates the core logging, tracing, and metrics modules with different build tools and languages + * [CDK](./powertools-examples-core-utilities/cdk) + * [SAM](./powertools-examples-core-utilities/sam) + * [SAM GraalVM](./powertools-examples-core-utilities/sam-graalvm) + * [Serverless](./powertools-examples-core-utilities/serverless) + * [Terraform](./powertools-examples-core-utilities/terraform) + * [Gradle](./powertools-examples-core-utilities/gradle) + * [Kotlin](./powertools-examples-core-utilities/kotlin) * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API + * [SAM](./powertools-examples-idempotency/sam) + * [SAM GraalVM](./powertools-examples-idempotency/sam-graalvm) * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function + * [SAM](./powertools-examples-parameters/sam) + * [SAM GraalVM](./powertools-examples-parameters/sam-graalvm) * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads -* [powertools-examples-sqs](powertools-examples-sqs) - Processes SQS batch requests (**Deprecated** - will be replaced by `powertools-examples-batch` in version 2 of this library) + * [SAM](./powertools-examples-serialization/sam) + * [SAM GraalVM](./powertools-examples-serialization/sam-graalvm) * [powertools-examples-validation](powertools-examples-validation) - Uses the validation module to validate user requests received via API Gateway * [powertools-examples-cloudformation](powertools-examples-cloudformation) - Deploys a Cloudformation custom resource * [powertools-examples-batch](powertools-examples-batch) - Examples for each of the different batch processing deployments +* [powertools-examples-kafka](powertools-examples-kafka) - Examples for Kafka event processing ## Working with AWS Serverless Application Model (SAM) Examples -Many of the examples use [AWS Serverless Application Model](https://aws.amazon.com/serverless/sam/) (SAM). To get started -with them, you can use the SAM Command Line Interface (SAM CLI) to build it and deploy an example to AWS. +Many of the examples use [AWS Serverless Application Model](https://aws.amazon.com/serverless/sam/) (SAM). To get started with them, you can use the SAM Command Line Interface (SAM CLI) to build it and deploy an example to AWS. To use the SAM CLI, you need the following tools. @@ -27,17 +38,13 @@ To use the SAM CLI, you need the following tools. * Maven - [Install Maven](https://maven.apache.org/install.html) * Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) -To learn more about SAM, -[check out the developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli.html). -You can use the CLI to [test events locally](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-local-invoke.html), -and [run the application locally](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-local-start-api.html), -amongst other things. +To learn more about SAM, [check out the developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli.html). You can use the CLI to [test events locally](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-local-invoke.html), and [run the application locally](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-local-start-api.html), amongst other things. To build and deploy an example application for the first time, run the following in your shell: ```bash # Switch to the directory containing an example for the powertools-idempotency module -$ cd powertools-examples-idempotency +$ cd powertools-examples-idempotency/sam # Build and deploy the example $ sam build @@ -54,14 +61,12 @@ The first command will build the source of your application. The second command You can find your API Gateway Endpoint URL in the output values displayed after deployment. -If you're not using SAM, you can look for examples for other tools under [powertools-examples-core](./powertools-examples-core) +If you're not using SAM, you can look for examples for other tools under [powertools-examples-core-utilities](./powertools-examples-core-utilities) ### External examples You can find more examples in the https://github.com/aws/aws-sam-cli-app-templates project: -* [Java 8 + Maven](https://github.com/aws/aws-sam-cli-app-templates/tree/master/java8/hello-pt-maven) -* [Java 8 on Amazon Linux 2 + Maven](https://github.com/aws/aws-sam-cli-app-templates/tree/master/java8.al2/hello-pt-maven) * [Java 11 + Maven](https://github.com/aws/aws-sam-cli-app-templates/tree/master/java11/hello-pt-maven) * [Java 17 + Maven](https://github.com/aws/aws-sam-cli-app-templates/tree/master/java17/hello-pt-maven) * [Java 17 + Gradle](https://github.com/aws/aws-sam-cli-app-templates/tree/master/java17/hello-pt-gradle) @@ -69,10 +74,9 @@ You can find more examples in the https://github.com/aws/aws-sam-cli-app-templat ### SAM - Other Tools -If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. -The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started. +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started. * [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) * [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) * [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) -* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) \ No newline at end of file +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) diff --git a/examples/pom.xml b/examples/pom.xml index 9689e8d71..5d191063f 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,30 +20,45 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> <packaging>pom</packaging> - <name>Powertools for AWS Lambda (Java) library Examples</name> + <name>Powertools for AWS Lambda (Java) - Examples</name> <description> A suite of examples accompanying for Powertools for AWS Lambda (Java). </description> <modules> - <module>powertools-examples-core/sam</module> - <module>powertools-examples-core/cdk/app</module> - <module>powertools-examples-core/cdk/infra</module> - <module>powertools-examples-idempotency</module> - <module>powertools-examples-parameters</module> - <module>powertools-examples-serialization</module> - <module>powertools-examples-sqs</module> + <module>powertools-examples-core-utilities/sam</module> + <module>powertools-examples-core-utilities/sam-graalvm</module> + <module>powertools-examples-core-utilities/cdk/app</module> + <module>powertools-examples-core-utilities/cdk/infra</module> + <module>powertools-examples-core-utilities/serverless</module> + <module>powertools-examples-core-utilities/terraform</module> + <module>powertools-examples-idempotency/sam</module> + <module>powertools-examples-idempotency/sam-graalvm</module> + <module>powertools-examples-parameters/sam</module> + <module>powertools-examples-parameters/sam-graalvm</module> + <module>powertools-examples-serialization/sam</module> + <module>powertools-examples-serialization/sam-graalvm</module> + <module>powertools-examples-kafka</module> <module>powertools-examples-batch</module> <module>powertools-examples-validation</module> <module>powertools-examples-cloudformation</module> </modules> - <!-- Don't deploy the examples --> - <properties> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> + <build> + <plugins> + <!-- Don't deploy the examples --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> -</project> \ No newline at end of file +</project> diff --git a/examples/powertools-examples-batch/deploy/sqs/template.yml b/examples/powertools-examples-batch/deploy/sqs/template.yml index 764ba4863..1232e4d51 100644 --- a/examples/powertools-examples-batch/deploy/sqs/template.yml +++ b/examples/powertools-examples-batch/deploy/sqs/template.yml @@ -7,12 +7,10 @@ Globals: Function: Timeout: 20 Runtime: java11 - MemorySize: 512 - Tracing: Active + MemorySize: 5400 Environment: Variables: POWERTOOLS_LOG_LEVEL: INFO - POWERTOOLS_LOGGER_SAMPLE_RATE: 1.0 POWERTOOLS_LOGGER_LOG_EVENT: true Resources: @@ -45,6 +43,9 @@ Resources: AliasName: alias/powertools-batch-sqs-demo TargetKeyId: !Ref CustomerKey + Bucket: + Type: AWS::S3::Bucket + DemoDlqSqsQueue: Type: AWS::SQS::Queue Properties: @@ -64,6 +65,7 @@ Resources: DemoSQSSenderFunction: Type: AWS::Serverless::Function Properties: + Tracing: Active CodeUri: ../.. Handler: org.demo.batch.sqs.SqsBatchSender::handleRequest Environment: @@ -96,11 +98,13 @@ Resources: DemoSQSConsumerFunction: Type: AWS::Serverless::Function Properties: + Tracing: Active CodeUri: ../.. Handler: org.demo.batch.sqs.SqsBatchHandler::handleRequest Environment: Variables: POWERTOOLS_SERVICE_NAME: sqs-demo + BUCKET: !Ref Bucket Policies: - Statement: - Sid: SQSDeleteGetAttribute @@ -121,13 +125,62 @@ Resources: - kms:GenerateDataKey - kms:Decrypt Resource: !GetAtt CustomerKey.Arn + - Sid: WriteToS3 + Effect: Allow + Action: + - s3:PutObject + Resource: !Sub ${Bucket.Arn}/* + +# Events: +# MySQSEvent: +# Type: SQS +# Properties: +# Queue: !GetAtt DemoSqsQueue.Arn +# BatchSize: 100 +# MaximumBatchingWindowInSeconds: 60 + + DemoSQSParallelConsumerFunction: + Type: AWS::Serverless::Function + Properties: + Tracing: Active + CodeUri: ../.. + Handler: org.demo.batch.sqs.SqsParallelBatchHandler::handleRequest + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: sqs-demo + BUCKET: !Ref Bucket + Policies: + - Statement: + - Sid: SQSDeleteGetAttribute + Effect: Allow + Action: + - sqs:DeleteMessageBatch + - sqs:GetQueueAttributes + Resource: !GetAtt DemoSqsQueue.Arn + - Sid: SQSSendMessageBatch + Effect: Allow + Action: + - sqs:SendMessageBatch + - sqs:SendMessage + Resource: !GetAtt DemoDlqSqsQueue.Arn + - Sid: SQSKMSKey + Effect: Allow + Action: + - kms:GenerateDataKey + - kms:Decrypt + Resource: !GetAtt CustomerKey.Arn + - Sid: WriteToS3 + Effect: Allow + Action: + - s3:PutObject + Resource: !Sub ${Bucket.Arn}/* Events: MySQSEvent: Type: SQS Properties: Queue: !GetAtt DemoSqsQueue.Arn - BatchSize: 2 - MaximumBatchingWindowInSeconds: 300 + BatchSize: 100 + MaximumBatchingWindowInSeconds: 60 Outputs: DemoSqsQueue: diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index d3c4bc49b..0091fb5ca 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,17 +5,16 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Batch</name> + <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> <properties> - <log4j.version>2.20.0</log4j.version> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - <sdk.version>2.20.109</sdk.version> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + <sdk.version>2.39.3</sdk.version> </properties> <dependencies> @@ -26,7 +25,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -37,12 +36,23 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>sdk-core</artifactId> <version>${sdk.version}</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <version>${sdk.version}</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> @@ -64,6 +74,11 @@ <artifactId>kinesis</artifactId> <version>${sdk.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> </dependencies> <build> @@ -71,7 +86,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> @@ -94,11 +109,18 @@ </goals> </execution> </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> @@ -106,93 +128,30 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> - <transformer - implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> - </transformer> + <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> </transformers> </configuration> </execution> </executions> <dependencies> <dependency> - <groupId>com.github.edwgiz</groupId> - <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> - <version>2.15</version> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> </project> \ No newline at end of file diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandler.java index 988c49e86..4f27929f0 100644 --- a/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandler.java +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandler.java @@ -4,14 +4,14 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; public class DynamoDBStreamBatchHandler implements RequestHandler<DynamodbEvent, StreamsEventResponse> { - private final static Logger LOGGER = LogManager.getLogger(DynamoDBStreamBatchHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDBStreamBatchHandler.class); private final BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler; public DynamoDBStreamBatchHandler() { @@ -25,7 +25,7 @@ public StreamsEventResponse handleRequest(DynamodbEvent ddbEvent, Context contex return handler.processBatch(ddbEvent, context); } - private void processMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord, Context context) { + private void processMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord) { LOGGER.info("Processing DynamoDB Stream Record" + dynamodbStreamRecord); } diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandlerParallel.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandlerParallel.java new file mode 100644 index 000000000..2e784b795 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandlerParallel.java @@ -0,0 +1,37 @@ +package org.demo.batch.dynamo; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class DynamoDBStreamBatchHandlerParallel implements RequestHandler<DynamodbEvent, StreamsEventResponse> { + + private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDBStreamBatchHandlerParallel.class); + private final BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler; + private final ExecutorService executor; + + public DynamoDBStreamBatchHandlerParallel() { + handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + executor = Executors.newFixedThreadPool(2); + } + + @Override + public StreamsEventResponse handleRequest(DynamodbEvent ddbEvent, Context context) { + return handler.processBatchInParallel(ddbEvent, context, executor); + } + + private void processMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord) { + LOGGER.info("Processing DynamoDB Stream Record" + dynamodbStreamRecord); + } + +} diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBWriter.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBWriter.java index 953ba8f23..fc1b0747b 100644 --- a/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBWriter.java +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBWriter.java @@ -8,8 +8,8 @@ import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.demo.batch.model.DdbProduct; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; import software.amazon.awssdk.enhanced.dynamodb.TableSchema; @@ -21,7 +21,7 @@ public class DynamoDBWriter implements RequestHandler<ScheduledEvent, String> { - private static final Logger LOGGER = LogManager.getLogger(DynamoDBWriter.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDBWriter.class); private final DynamoDbEnhancedClient enhancedClient; diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java index d9339549b..b7b4f462e 100644 --- a/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java @@ -4,15 +4,15 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.KinesisEvent; import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.demo.batch.model.Product; import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; public class KinesisBatchHandler implements RequestHandler<KinesisEvent, StreamsEventResponse> { - private final static Logger LOGGER = LogManager.getLogger(org.demo.batch.sqs.SqsBatchHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(KinesisBatchHandler.class); private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler; public KinesisBatchHandler() { @@ -26,7 +26,7 @@ public StreamsEventResponse handleRequest(KinesisEvent kinesisEvent, Context con return handler.processBatch(kinesisEvent, context); } - private void processMessage(Product p, Context c) { + private void processMessage(Product p) { LOGGER.info("Processing product " + p); } diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandlerParallel.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandlerParallel.java new file mode 100644 index 000000000..d7e99e85a --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandlerParallel.java @@ -0,0 +1,39 @@ +package org.demo.batch.kinesis; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; +import org.demo.batch.model.Product; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class KinesisBatchHandlerParallel implements RequestHandler<KinesisEvent, StreamsEventResponse> { + + private static final Logger LOGGER = LoggerFactory.getLogger(KinesisBatchHandlerParallel.class); + private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler; + private final ExecutorService executor; + + + public KinesisBatchHandlerParallel() { + handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + executor = Executors.newFixedThreadPool(2); + } + + @Override + public StreamsEventResponse handleRequest(KinesisEvent kinesisEvent, Context context) { + return handler.processBatchInParallel(kinesisEvent, context, executor); + } + + private void processMessage(Product p) { + LOGGER.info("Processing product " + p); + } + +} diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchSender.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchSender.java index 0bc7dc42c..dadead1a2 100644 --- a/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchSender.java +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchSender.java @@ -10,8 +10,8 @@ import java.security.SecureRandom; import java.util.List; import java.util.stream.IntStream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.demo.batch.model.Product; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -28,7 +28,7 @@ */ public class KinesisBatchSender implements RequestHandler<ScheduledEvent, String> { - private static final Logger LOGGER = LogManager.getLogger(KinesisBatchSender.class); + private static final Logger LOGGER = LoggerFactory.getLogger(KinesisBatchSender.class); private final KinesisClient kinesisClient; private final SecureRandom random; diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/AbstractSqsBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/AbstractSqsBatchHandler.java new file mode 100644 index 000000000..1b3029d40 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/AbstractSqsBatchHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.demo.batch.sqs; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Random; + +import org.demo.batch.model.Product; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.tracing.TracingUtils; + +public class AbstractSqsBatchHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSqsBatchHandler.class); + private final ObjectMapper mapper = new ObjectMapper(); + private final String bucket = System.getenv("BUCKET"); + private final S3Client s3 = S3Client.builder().httpClient(UrlConnectionHttpClient.create()).build(); + private final Random r = new Random(); + + /** + * Simulate some processing (I/O + S3 put request) + * @param p deserialized product + */ + @Tracing + protected void processMessage(Product p) { + TracingUtils.putAnnotation("productId", p.getId()); + TracingUtils.putAnnotation("Thread", Thread.currentThread().getName()); + MDC.put("product", String.valueOf(p.getId())); + LOGGER.info("Processing product {}", p); + + char c = (char) (r.nextInt(26) + 'a'); + char[] chars = new char[1024 * 1000]; + Arrays.fill(chars, c); + p.setName(new String(chars)); + try { + File file = new File("/tmp/" + p.getId() + ".json"); + mapper.writeValue(file, p); + s3.putObject( + PutObjectRequest.builder().bucket(bucket).key(p.getId() + ".json").build(), + RequestBody.fromFile(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + MDC.remove("product"); + } + } +} diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandler.java index bb9d704d3..bc0f57cb8 100644 --- a/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandler.java +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandler.java @@ -4,14 +4,16 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.demo.batch.model.Product; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.tracing.Tracing; -public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { - private final static Logger LOGGER = LogManager.getLogger(SqsBatchHandler.class); +public class SqsBatchHandler extends AbstractSqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + private static final Logger LOGGER = LoggerFactory.getLogger(SqsBatchHandler.class); private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; public SqsBatchHandler() { @@ -20,14 +22,11 @@ public SqsBatchHandler() { .buildWithMessageHandler(this::processMessage, Product.class); } + @Logging + @Tracing @Override public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + LOGGER.info("Processing batch of {} messages", sqsEvent.getRecords().size()); return handler.processBatch(sqsEvent, context); } - - - private void processMessage(Product p, Context c) { - LOGGER.info("Processing product " + p); - } - } diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandlerParallel.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandlerParallel.java new file mode 100644 index 000000000..21294dd55 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandlerParallel.java @@ -0,0 +1,37 @@ +package org.demo.batch.sqs; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import org.demo.batch.model.Product; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.tracing.Tracing; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class SqsBatchHandlerParallel extends AbstractSqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + private static final Logger LOGGER = LoggerFactory.getLogger(SqsBatchHandlerParallel.class); + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + private final ExecutorService executor; + + public SqsBatchHandlerParallel() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + executor = Executors.newFixedThreadPool(2); + } + + @Logging + @Tracing + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + LOGGER.info("Processing batch of {} messages", sqsEvent.getRecords().size()); + return handler.processBatchInParallel(sqsEvent, context, executor); + } +} diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java index af78bed5a..58b24d735 100644 --- a/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java @@ -10,14 +10,13 @@ import java.security.SecureRandom; import java.util.List; import java.util.stream.IntStream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.demo.batch.model.Product; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; /** @@ -27,7 +26,7 @@ */ public class SqsBatchSender implements RequestHandler<ScheduledEvent, String> { - private static final Logger LOGGER = LogManager.getLogger(SqsBatchSender.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SqsBatchSender.class); private final SqsClient sqsClient; private final SecureRandom random; @@ -45,16 +44,12 @@ public SqsBatchSender() { public String handleRequest(ScheduledEvent scheduledEvent, Context context) { String queueUrl = System.getenv("QUEUE_URL"); - LOGGER.info("handleRequest"); - - // Push 5 messages on each invoke. - List<SendMessageBatchRequestEntry> batchRequestEntries = IntStream.range(0, 5) + List<SendMessageBatchRequestEntry> batchRequestEntries = IntStream.range(0, 50) .mapToObj(value -> { - long id = random.nextLong(); - float price = random.nextFloat(); + long id = Math.abs(random.nextLong()); + float price = Math.abs(random.nextFloat() * 3465); Product product = new Product(id, "product-" + id, price); try { - return SendMessageBatchRequestEntry.builder() .id(scheduledEvent.getId() + value) .messageBody(objectMapper.writeValueAsString(product)) @@ -65,12 +60,12 @@ public String handleRequest(ScheduledEvent scheduledEvent, Context context) { } }).collect(toList()); - SendMessageBatchResponse sendMessageBatchResponse = sqsClient.sendMessageBatch(SendMessageBatchRequest.builder() - .queueUrl(queueUrl) - .entries(batchRequestEntries) - .build()); - - LOGGER.info("Sent Message {}", sendMessageBatchResponse); + for (int i = 0; i < 50; i += 10) { + sqsClient.sendMessageBatch(SendMessageBatchRequest.builder() + .queueUrl(queueUrl) + .entries(batchRequestEntries.subList(i, i + 10)) + .build()); + } return "Success"; } diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsParallelBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsParallelBatchHandler.java new file mode 100644 index 000000000..0151c0a32 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsParallelBatchHandler.java @@ -0,0 +1,48 @@ +/* + * Copyright 2024 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.demo.batch.sqs; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import org.demo.batch.model.Product; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.tracing.Tracing; + +public class SqsParallelBatchHandler extends AbstractSqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + private static final Logger LOGGER = LoggerFactory.getLogger(SqsParallelBatchHandler.class); + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsParallelBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } + + @Logging + @Tracing + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + LOGGER.info("Processing batch of {} messages", sqsEvent.getRecords().size()); + MDC.put("requestId", context.getAwsRequestId()); // should be propagated to other threads + return handler.processBatchInParallel(sqsEvent, context); + } +} diff --git a/examples/powertools-examples-batch/src/main/resources/LogLayout.json b/examples/powertools-examples-batch/src/main/resources/LogLayout.json new file mode 100644 index 000000000..60f102e09 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/resources/LogLayout.json @@ -0,0 +1,75 @@ +{ + "level": { + "$resolver": "level", + "field": "name" + }, + "message": { + "$resolver": "message" + }, + "error": { + "message": { + "$resolver": "exception", + "field": "message" + }, + "name": { + "$resolver": "exception", + "field": "className" + }, + "stack": { + "$resolver": "exception", + "field": "stackTrace", + "stackTrace": { + "stringified": true + } + } + }, + "cold_start": { + "$resolver": "powertools", + "field": "cold_start" + }, + "thread": { + "$resolver": "thread", + "field": "name" + }, + "function_arn": { + "$resolver": "powertools", + "field": "function_arn" + }, + "function_memory_size": { + "$resolver": "powertools", + "field": "function_memory_size" + }, + "function_name": { + "$resolver": "powertools", + "field": "function_name" + }, + "function_request_id": { + "$resolver": "powertools", + "field": "function_request_id" + }, + "function_version": { + "$resolver": "powertools", + "field": "function_version" + }, + "sampling_rate": { + "$resolver": "powertools", + "field": "sampling_rate" + }, + "service": { + "$resolver": "powertools", + "field": "service" + }, + "timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + } + }, + "xray_trace_id": { + "$resolver": "powertools", + "field": "xray_trace_id" + }, + "": { + "$resolver": "powertools" + } +} \ No newline at end of file diff --git a/examples/powertools-examples-batch/src/main/resources/log4j2.xml b/examples/powertools-examples-batch/src/main/resources/log4j2.xml index ea3ecf474..c48ca3ef4 100644 --- a/examples/powertools-examples-batch/src/main/resources/log4j2.xml +++ b/examples/powertools-examples-batch/src/main/resources/log4j2.xml @@ -2,7 +2,8 @@ <Configuration> <Appenders> <Console name="JsonAppender" target="SYSTEM_OUT"> - <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json"/> + <!-- custom layout to show thread name in logs --> + <JsonTemplateLayout eventTemplateUri="classpath:LogLayout.json"/> </Console> </Appenders> <Loggers> diff --git a/examples/powertools-examples-cloudformation/Makefile b/examples/powertools-examples-cloudformation/Makefile new file mode 100644 index 000000000..b916d823c --- /dev/null +++ b/examples/powertools-examples-cloudformation/Makefile @@ -0,0 +1,5 @@ +build-HelloWorldFunction: + chmod +x target/hello-world + cp target/hello-world $(ARTIFACTS_DIR) # (ARTIFACTS_DIR --> https://github.com/aws/aws-lambda-builders/blob/develop/aws_lambda_builders/workflows/custom_make/DESIGN.md#implementation) + chmod +x src/main/config/bootstrap + cp src/main/config/bootstrap $(ARTIFACTS_DIR) diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 4b53ff0aa..27e564bf3 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -2,18 +2,27 @@ This project contains an example of Lambda function using the CloudFormation module of Powertools for AWS Lambda in Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/custom_resources/). +In this example you pass in a bucket name as a parameter and upon CloudFormation events a call is made to a lambda. That lambda attempts to create the bucket on CREATE events, create a new bucket if the name changes with an UPDATE event and delete the bucket upon DELETE events. + ## Deploy the sample application This sample can be used either with the Serverless Application Model (SAM) or with CDK. ### Deploy with SAM CLI To deploy it using the SAM CLI, check out the instructions for getting started in [the examples directory](../README.md) +Run the following in your shell: + +```bash +cd infra/sam +sam build +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.9.0718 +``` ### Deploy with CDK To use CDK you need the following tools. * CDK - [Install CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) -* Java 8 - [Install Java 8](https://docs.aws.amazon.com/corretto/latest/corretto-8-ug/downloads-list.html) +* Java 11 - [Install Java 11](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html) * Maven - [Install Maven](https://maven.apache.org/install.html) * Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) @@ -23,5 +32,5 @@ To build and deploy this application for the first time, run the following in yo cd infra/cdk mvn package cdk synth -cdk deploy -c BucketNameParam=my-unique-bucket-20230718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.9.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/Dockerfile b/examples/powertools-examples-cloudformation/infra/sam-graalvm/Dockerfile new file mode 100644 index 000000000..dac9390e5 --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/Dockerfile @@ -0,0 +1,14 @@ +# Use the official AWS SAM base image for Java 21 +FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461 + +# Install GraalVM dependencies +RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz +RUN mv graalvm-jdk-21.* /usr/lib/graalvm + +# Make native image and mvn available on CLI +RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image +RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn + +# Set GraalVM as default +ENV JAVA_HOME=/usr/lib/graalvm +ENV PATH=/usr/lib/graalvm/bin:$PATH diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md new file mode 100644 index 000000000..3aca1408a --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -0,0 +1,52 @@ +# Powertools for AWS Lambda (Java) - CloudFormation Custom Resource Example with SAM on GraalVM + +This project contains an example of a Lambda function using the CloudFormation module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://docs.powertools.aws.dev/lambda-java/utilities/custom_resources/). + +In this example you pass in a bucket name as a parameter and upon CloudFormation events a call is made to a lambda. That lambda attempts to create the bucket on CREATE events, create a new bucket if the name changes with an UPDATE event and delete the bucket upon DELETE events. + +Have a look at [App.java](../../src/main/java/helloworld/App.java) for the full details. + +## Build the sample application + +> [!NOTE] +> Building AWS Lambda packages on macOS (ARM64/Intel) for deployment on AWS Lambda (Linux x86_64 or ARM64) will result in incompatible binary dependencies that cause import errors at runtime. + +Choose the appropriate build method based on your operating system: + +### Build locally using Docker + +Recommended for macOS and Windows users: Cross-compile using Docker to match target platform of Lambda: + +```shell +docker build --platform linux/amd64 . -t powertools-examples-cloudformation-sam-graalvm +docker run --platform linux/amd64 -it -v `pwd`/../..:`pwd`/../.. -w `pwd`/../.. -v ~/.m2:/root/.m2 powertools-examples-cloudformation-sam-graalvm mvn clean -Pnative-image package -DskipTests +sam build --use-container --build-image powertools-examples-cloudformation-sam-graalvm +``` + +**Note**: The Docker run command mounts your local Maven cache (`~/.m2`) and builds the native binary with SNAPSHOT support, then SAM packages the pre-built binary. + +### Build on native OS + +For Linux users with GraalVM installed: + +```shell +export JAVA_HOME=<path to GraalVM> +cd ../.. +mvn clean -Pnative-image package -DskipTests +cd infra/sam-graalvm +sam build +``` + +## Deploy the sample application + +```shell +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.9.0718 +``` + +This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting started with SAM in [the examples directory](../../../README.md) + +## Test the application + +The CloudFormation custom resource will be triggered automatically during stack deployment. You can monitor the Lambda function execution in CloudWatch Logs to see the custom resource handling CREATE, UPDATE, and DELETE events for the S3 bucket. + +Check out [App.java](../../src/main/java/helloworld/App.java) to see how it works! \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/template.yaml b/examples/powertools-examples-cloudformation/infra/sam-graalvm/template.yaml new file mode 100644 index 000000000..4249aaed1 --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/template.yaml @@ -0,0 +1,49 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + powertools-examples-cloudformation-graalvm + + Sample SAM Template for powertools-examples-cloudformation with GraalVM native image + +Globals: + Function: + Timeout: 20 + +Parameters: + BucketNameParam: + Type: String + +Resources: + HelloWorldCustomResource: + Type: AWS::CloudFormation::CustomResource + Properties: + ServiceToken: !GetAtt HelloWorldFunction.Arn + BucketName: !Ref BucketNameParam + + HelloWorldFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ../../ + Handler: helloworld.App::handleRequest + Runtime: provided.al2023 + Architectures: + - x86_64 + MemorySize: 512 + Policies: + - Statement: + - Sid: bucketaccess1 + Effect: Allow + Action: + - s3:GetLifecycleConfiguration + - s3:PutLifecycleConfiguration + - s3:CreateBucket + - s3:ListBucket + - s3:DeleteBucket + Resource: '*' + Metadata: + BuildMethod: makefile + +Outputs: + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ad1c2eb51..212c0966b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,20 +3,20 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - CloudFormation</name> + <name>Powertools for AWS Lambda (Java) - Examples - CloudFormation</name> <properties> - <log4j.version>2.20.0</log4j.version> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - <lambda.core.version>1.2.2</lambda.core.version> - <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.126</aws.sdk.version> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <lambda.core.version>1.4.0</lambda.core.version> + <lambda.events.version>3.16.1</lambda.events.version> + <aws.sdk.version>2.40.9</aws.sdk.version> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> <dependencyManagement> <dependencies> @@ -37,9 +37,9 @@ <version>${lambda.core.version}</version> </dependency> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>${lambda.events.version}</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>${lambda.events.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> @@ -48,18 +48,13 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - <version>${log4j.version}</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - <version>${log4j.version}</version> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> @@ -78,96 +73,38 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>apache-client</artifactId> - <exclusions> - <exclusion> - <groupId>commons-logging</groupId> - <artifactId>commons-logging</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-jcl</artifactId> - <version>${log4j.version}</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.7</version> </dependency> - - - </dependencies> <build> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <transformers> - <transformer - implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> - </transformer> - </transformers> - </configuration> - </execution> - </executions> - <dependencies> - <dependency> - <groupId>com.github.edwgiz</groupId> - <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> - <version>2.15</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> <dependencies> <dependency> <groupId>org.aspectj</groupId> @@ -175,44 +112,74 @@ <version>${aspectj.version}</version> </dependency> </dependencies> - </dependencyManagement> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>native-image</id> <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>hello-world</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <arg>--enable-url-protocols=http</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> + </plugins> </build> </profile> </profiles> diff --git a/examples/powertools-examples-cloudformation/src/main/config/bootstrap b/examples/powertools-examples-cloudformation/src/main/config/bootstrap new file mode 100755 index 000000000..8e7928cd3 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/config/bootstrap @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +./hello-world $_HANDLER \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java index 54f13244c..c35ad8fb9 100644 --- a/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java @@ -3,8 +3,8 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; import java.util.Objects; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.awscore.exception.AwsServiceException; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.waiters.WaiterResponse; @@ -24,7 +24,7 @@ */ public class App extends AbstractCustomResourceHandler { - private final static Logger log = LogManager.getLogger(App.class); + private static final Logger log = LoggerFactory.getLogger(App.class); private final S3Client s3Client; public App() { @@ -47,7 +47,7 @@ protected Response create(CloudFormationCustomResourceEvent cloudFormationCustom Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); - log.info(cloudFormationCustomResourceEvent); + log.info(cloudFormationCustomResourceEvent.toString()); String bucketName = (String) cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"); log.info("Bucket Name {}", bucketName); try { @@ -57,7 +57,7 @@ protected Response create(CloudFormationCustomResourceEvent cloudFormationCustom return Response.success(bucketName); } catch (AwsServiceException | SdkClientException e) { // In case of error, return a failed response, with the bucketName as the physicalResourceId - log.error(e); + log.error("Unable to create bucket", e); return Response.failed(bucketName); } } @@ -77,7 +77,7 @@ protected Response update(CloudFormationCustomResourceEvent cloudFormationCustom Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); - log.info(cloudFormationCustomResourceEvent); + log.info(cloudFormationCustomResourceEvent.toString()); // Get the physicalResourceId. physicalResourceId is the value returned to CloudFormation in the Create request, and passed in on subsequent requests (e.g. UPDATE or DELETE) String physicalResourceId = cloudFormationCustomResourceEvent.getPhysicalResourceId(); log.info("Physical Resource ID {}", physicalResourceId); @@ -94,7 +94,7 @@ protected Response update(CloudFormationCustomResourceEvent cloudFormationCustom // Return a successful response with the newBucketName return Response.success(newBucketName); } catch (AwsServiceException | SdkClientException e) { - log.error(e); + log.error("Unable to create bucket", e); return Response.failed(newBucketName); } } else { @@ -120,7 +120,7 @@ protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustom Objects.requireNonNull(cloudFormationCustomResourceEvent.getPhysicalResourceId(), "PhysicalResourceId cannot be null."); - log.info(cloudFormationCustomResourceEvent); + log.info(cloudFormationCustomResourceEvent.toString()); // Get the physicalResourceId. physicalResourceId is the value provided to CloudFormation in the Create request. String bucketName = cloudFormationCustomResourceEvent.getPhysicalResourceId(); log.info("Bucket Name {}", bucketName); @@ -135,7 +135,7 @@ protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustom return Response.success(bucketName); } catch (AwsServiceException | SdkClientException e) { // Return a failed response in case of errors during the bucket deletion - log.error(e); + log.error("Unable to delete bucket", e); return Response.failed(bucketName); } } else { @@ -166,7 +166,7 @@ private void createBucket(String bucketName) { s3Client.createBucket(createBucketRequest); WaiterResponse<HeadBucketResponse> waiterResponse = waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(bucketName).build()); - waiterResponse.matched().response().ifPresent(log::info); + waiterResponse.matched().response().ifPresent(res -> log.info(res.toString())); log.info("Bucket Created {}", bucketName); } } \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..e97baa294 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,44 @@ +[ + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields":[{"name":"logger"}] + }, + { + "name":"java.lang.Void", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] + }, + { + "name":"jdk.internal.module.IllegalAccessLogger", + "fields":[{"name":"logger"}] + }, + { + "name":"sun.misc.Unsafe", + "fields":[{"name":"theUnsafe"}] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/native-image.properties b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/native-image.properties new file mode 100644 index 000000000..db5ebaa55 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/native-image.properties @@ -0,0 +1 @@ +Args = --enable-url-protocols=http,https \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/reflect-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/reflect-config.json new file mode 100644 index 000000000..06ea9ce2f --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/reflect-config.json @@ -0,0 +1,11 @@ +[ + { + "name": "helloworld.App", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/resource-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/software.amazon.awssdk/s3/reflect-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/software.amazon.awssdk/s3/reflect-config.json new file mode 100644 index 000000000..d685b7e20 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/software.amazon.awssdk/s3/reflect-config.json @@ -0,0 +1,27 @@ +[ + { + "name": "software.amazon.awssdk.services.s3.model.CreateBucketRequest", + "allPublicMethods": true, + "allPublicConstructors": true + }, + { + "name": "software.amazon.awssdk.services.s3.model.DeleteBucketRequest", + "allPublicMethods": true, + "allPublicConstructors": true + }, + { + "name": "software.amazon.awssdk.services.s3.model.HeadBucketRequest", + "allPublicMethods": true, + "allPublicConstructors": true + }, + { + "name": "software.amazon.awssdk.services.s3.model.HeadBucketResponse", + "allPublicMethods": true, + "allPublicConstructors": true + }, + { + "name": "software.amazon.awssdk.services.s3.model.NoSuchBucketException", + "allPublicMethods": true, + "allPublicConstructors": true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/.gitignore b/examples/powertools-examples-core-utilities/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/examples/powertools-examples-core-utilities/README.md b/examples/powertools-examples-core-utilities/README.md new file mode 100644 index 000000000..c9ca60f57 --- /dev/null +++ b/examples/powertools-examples-core-utilities/README.md @@ -0,0 +1,55 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example + +This project demonstrates the Lambda for Powertools Java module - including +[logging](https://docs.powertools.aws.dev/lambda/java/core/logging/), +[tracing](https://docs.powertools.aws.dev/lambda/java/core/tracing/), and +[metrics](https://docs.powertools.aws.dev/lambda/java/core/metrics/). + +The example application is the same, and you can now also use Kotlin! + +## Java +* [AWS SAM](sam/) +* [AWS CDK](cdk/) +* [Serverless framework](serverless/) +* [Terraform](terraform/) + +We also provide an example showing the integration of SAM, Powertools, and Gradle: + +* [AWS SAM with a Gradle build](gradle/) + +- App.java - Code for the application's Lambda function. +- AppTests.java - Unit tests for the application code. +- events - Invocation events that you can use to invoke the function. + +Configuration files and deployment process for each tool are described in corresponding README files. + +## Kotlin + +- [Gradle](kotlin/) + +Example application consists of the following files: + +- App.kt - Code for the application's Lambda function. +- AppTests.kt - Unit tests for the application code. +- events - Invocation events that you can use to invoke the function. + +Configuration files and deployment process for each tool are described in corresponding README files. + +## Test the application + +Once the app is deployed, you can invoke the endpoint like this: + +```bash + curl https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ +``` + +The response itself isn't particularly interesting - you will get back some information about your IP address. If +you go to the Lambda Console and locate the lambda you have deployed, then click the "Monitoring" tab you will +be able to find: + +- **View X-Ray traces** - Display the traces captured by the traces module. These include subsegments for the + different function calls within the example +- **View Cloudwatch logs** - Display the structured logging output of the example + +Likewise, from the CloudWatch dashboard, under **Metrics**, **all metrics**, you will find the namespaces `Another` +and `ServerlessAirline`. The values in each of these are published by the code in the respective application's Lambda function. diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core-utilities/cdk/README.md similarity index 84% rename from examples/powertools-examples-core/cdk/README.md rename to examples/powertools-examples-core-utilities/cdk/README.md index f15a24168..fbc558943 100644 --- a/examples/powertools-examples-core/cdk/README.md +++ b/examples/powertools-examples-core-utilities/cdk/README.md @@ -6,9 +6,9 @@ For general information on the deployed example itself, you can refer to the par ## Configuration CDK uses the following project structure: -- [app](./app) - stores the source code of your application, which is similar between all examples -- [infra](./infra) - stores the definition of your infrastructure - - [cdk.json](./infra/cdk.json) - tells the CDK Toolkit how to execute your app +- [app](app) - stores the source code of your application, which is similar between all examples +- [infra](infra) - stores the definition of your infrastructure + - [cdk.json](infra/cdk.json) - tells the CDK Toolkit how to execute your app - [CdkApp](./infra/src/main/java/cdk/CdkApp.java) - bootstraps your stack, taking AWS `account` and `region` as input - [CdkStack](./infra/src/main/java/cdk/CdkStack.java) - defines the Lambda function to be deployed as well as API Gateway for it. diff --git a/examples/powertools-examples-core/cdk/app/events/event.json b/examples/powertools-examples-core-utilities/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/cdk/app/events/event.json rename to examples/powertools-examples-core-utilities/cdk/app/events/event.json diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml new file mode 100644 index 000000000..c02b73026 --- /dev/null +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -0,0 +1,142 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with CDK</name> + <groupId>software.amazon.lambda.examples</groupId> + <!-- TODO TODO TODO this should build from SNAPSHOT, but it doesn't, because the snapshots + don't appear in the docker environment CDK builds it in in our CDK tests. How to procede? V2 blocker --> + <version>2.9.0</version> + <artifactId>powertools-examples-core-utilities-cdk</artifactId> + <packaging>jar</packaging> + + <properties> + <log4j.version>2.25.3</log4j.version> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + + <build> + <finalName>helloworld-lambda</finalName> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java similarity index 77% rename from examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java rename to examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java index 988da2a73..bb21f84d3 100644 --- a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java @@ -14,8 +14,6 @@ package helloworld; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; import com.amazonaws.services.lambda.runtime.Context; @@ -29,13 +27,15 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; import software.amazon.lambda.powertools.tracing.TracingUtils; @@ -44,26 +44,26 @@ * Handler for requests to Lambda function. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger log = LogManager.getLogger(App.class); + private static final Logger log = LoggerFactory.getLogger(App.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("X-Custom-Header", "application/json"); - metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> - { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); + DimensionSet dimensionSet = DimensionSet.of( + "AnotherService", "CustomService", + "AnotherService1", "CustomService1"); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); - LoggingUtils.appendKey("test", "willBeLogged"); + MDC.put("test", "willBeLogged"); APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() .withHeaders(headers); @@ -73,8 +73,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv TracingUtils.putAnnotation("Test", "New"); String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); - TracingUtils.withSubsegment("loggingResponse", subsegment -> - { + TracingUtils.withSubsegment("loggingResponse", subsegment -> { String sampled = "log something out"; log.info(sampled); log.info(output); diff --git a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..8bc57b201 --- /dev/null +++ b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/AppStream.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; + +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + private final static Logger log = LogManager.getLogger(AppStream.class); + + @Override + @Logging(logEvent = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + // RequestStreamHandler can be used instead of RequestHandler for cases when you'd like to deserialize request body or serialize response body yourself, instead of allowing that to happen automatically + // Note that you still need to return a proper JSON for API Gateway to handle + // See Lambda Response format for examples: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html + public void handleRequest(InputStream input, OutputStream output, Context context) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); + PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) { + + log.info("Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); + + writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} "); + } catch (IOException e) { + log.error("Something has gone wrong: ", e); + } + } +} + diff --git a/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/cdk/app/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml rename to examples/powertools-examples-core-utilities/cdk/app/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/cdk/infra/cdk.json b/examples/powertools-examples-core-utilities/cdk/infra/cdk.json similarity index 100% rename from examples/powertools-examples-core/cdk/infra/cdk.json rename to examples/powertools-examples-core-utilities/cdk/infra/cdk.json diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml similarity index 74% rename from examples/powertools-examples-core/cdk/infra/pom.xml rename to examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 3dc7adb6a..e3ceb7e65 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,32 +4,41 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.91.0</cdk.version> + <cdk.version>2.224.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> - <junit.version>5.10.0</junit.version> + <junit.version>5.14.1</junit.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.11.0</version> + <version>3.14.1</version> <configuration> - <source>1.8</source> - <target>1.8</target> + <source>11</source> + <target>11</target> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> - <version>3.1.0</version> + <version>3.6.2</version> <configuration> <mainClass>cdk.CdkApp</mainClass> </configuration> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <dependencies> diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core-utilities/cdk/infra/src/main/java/cdk/CdkApp.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java rename to examples/powertools-examples-core-utilities/cdk/infra/src/main/java/cdk/CdkApp.java diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core-utilities/cdk/infra/src/main/java/cdk/CdkStack.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java rename to examples/powertools-examples-core-utilities/cdk/infra/src/main/java/cdk/CdkStack.java diff --git a/examples/powertools-examples-core-utilities/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core-utilities/cdk/infra/src/test/java/cdk/CdkStackTest.java new file mode 100644 index 000000000..e69de29bb diff --git a/examples/powertools-examples-core-utilities/gradle/README.md b/examples/powertools-examples-core-utilities/gradle/README.md new file mode 100644 index 000000000..adc7fe635 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/README.md @@ -0,0 +1,38 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with Gradle + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with +[Gradle](https://gradle.org/) running the build. This example is configured for Java 11 only; in order to use a newer version, check out the Gradle +configuration guide [in the main project README](../../../README.md). + +You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, +and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. + + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## Configuration +SAM uses [template.yaml](template.yaml) to define the application's AWS resources. +This file defines the Lambda function to be deployed as well as API Gateway for it. + +The build of the project is managed by Gradle, and configured in [build.gradle](build.gradle). + +## Deploy the sample application +To get started, you can use the Gradle wrapper to bootstrap Gradle and run the build: + +```bash +./gradlew build +``` + +Once this is done to deploy the example, check out the instructions for getting started with SAM in +[the examples directory](../../README.md) + +## Additional notes + +You can watch the trace information or log information using the SAM CLI: +```bash +# Tail the logs +sam logs --tail $MY_STACK + +# Tail the traces +sam traces --tail +``` \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle new file mode 100644 index 000000000..b01fdcfaa --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -0,0 +1,36 @@ + +plugins { + id 'java' + id "io.freefair.aspectj.post-compile-weaving" version "8.2.2" +} + +wrapper { + gradleVersion = "8.5" +} + +compileJava { + sourceCompatibility = "11" + targetCompatibility = "11" + + ajc { + enabled = true + } +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation 'com.amazonaws:aws-lambda-java-core:1.2.2' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' + implementation 'com.amazonaws:aws-lambda-java-events:3.16.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' + implementation 'org.aspectj:aspectjrt:1.9.20.1' + aspect 'software.amazon.lambda:powertools-tracing:2.9.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.9.0' + aspect 'software.amazon.lambda:powertools-metrics:2.9.0' +} + diff --git a/examples/powertools-examples-core-utilities/gradle/events/event.json b/examples/powertools-examples-core-utilities/gradle/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/.gitignore new file mode 100644 index 000000000..59c09e205 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/.gitignore @@ -0,0 +1,2 @@ +!gradle-wrapper.jar + diff --git a/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..033e24c4c Binary files /dev/null and b/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..1af9e0930 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/powertools-examples-core-utilities/gradle/gradlew b/examples/powertools-examples-core-utilities/gradle/gradlew new file mode 100755 index 000000000..fcb6fca14 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/examples/powertools-examples-core-utilities/gradle/gradlew.bat b/examples/powertools-examples-core-utilities/gradle/gradlew.bat new file mode 100644 index 000000000..93e3f59f1 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/App.java new file mode 100644 index 000000000..39ed6f8d8 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/App.java @@ -0,0 +1,109 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; +import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.tracing.CaptureMode; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.tracing.TracingUtils; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(App.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map<String, String> headers = new HashMap<>(); + + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + + DimensionSet dimensionSet = DimensionSet.of( + "AnotherService", "CustomService", + "AnotherService1", "CustomService1" + ); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); + + MDC.put("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info("", entry("ip", pageContents)); + TracingUtils.putAnnotation("Test", "New"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + TracingUtils.withSubsegment("loggingResponse", subsegment -> + { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); + + log.info("After output"); + return response + .withStatusCode(200) + .withBody(output); + } catch (RuntimeException | IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + @Tracing + private void log() { + log.info("inside threaded logging for function"); + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + putMetadata("getPageContents", address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..8bc57b201 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/AppStream.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; + +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + private final static Logger log = LogManager.getLogger(AppStream.class); + + @Override + @Logging(logEvent = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + // RequestStreamHandler can be used instead of RequestHandler for cases when you'd like to deserialize request body or serialize response body yourself, instead of allowing that to happen automatically + // Note that you still need to return a proper JSON for API Gateway to handle + // See Lambda Response format for examples: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html + public void handleRequest(InputStream input, OutputStream output, Context context) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); + PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) { + + log.info("Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); + + writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} "); + } catch (IOException e) { + log.error("Something has gone wrong: ", e); + } + } +} + diff --git a/examples/powertools-examples-core/sam/template.yaml b/examples/powertools-examples-core-utilities/gradle/template.yaml similarity index 96% rename from examples/powertools-examples-core/sam/template.yaml rename to examples/powertools-examples-core-utilities/gradle/template.yaml index 9a51a1ba9..1a1572fca 100644 --- a/examples/powertools-examples-core/sam/template.yaml +++ b/examples/powertools-examples-core-utilities/gradle/template.yaml @@ -1,7 +1,9 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > - CoreUtilities + gradle + + Sample SAM Template for gradle # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: @@ -24,6 +26,8 @@ Resources: Properties: CodeUri: . Handler: helloworld.App::handleRequest + Runtime: java11 + MemorySize: 512 Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object Variables: POWERTOOLS_SERVICE_NAME: hello @@ -39,6 +43,7 @@ Resources: Properties: CodeUri: . Handler: helloworld.AppStream::handleRequest + Runtime: java11 MemorySize: 512 Tracing: Active Environment: diff --git a/examples/powertools-examples-core-utilities/kotlin/.gitignore b/examples/powertools-examples-core-utilities/kotlin/.gitignore new file mode 100644 index 000000000..e660fd93d --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/examples/powertools-examples-core-utilities/kotlin/README.md b/examples/powertools-examples-core-utilities/kotlin/README.md new file mode 100644 index 000000000..422e12742 --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/README.md @@ -0,0 +1,38 @@ +# Powertools for AWS Lambda (Kotlin) - Core Utilities Example + +This project demonstrates the Lambda for Powertools Kotlin module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with +[Gradle](https://gradle.org/) running the build. This example is configured for Java 11 only; in order to use a newer version, check out the Gradle +configuration guide [in the main project README](../../../README.md). + +You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, +and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## Configuration +SAM uses [template.yaml](template.yaml) to define the application's AWS resources. +This file defines the Lambda function to be deployed as well as API Gateway for it. + +The build of the project is managed by Gradle, and configured in [build.gradle.kts](build.gradle.kts) +. + +## Deploy the sample application +To get started, you can use the included template with SAM to run the build and deploy to your AWS environment: + +```bash +sam build && sam deploy --guided +``` + +Once this is done to deploy the example, check out the instructions for getting started with SAM in +[the examples directory](../../README.md) + +## Additional notes + +You can watch the trace information or log information using the SAM CLI: +```bash +# Tail the logs +sam logs --tail $MY_STACK + +# Tail the traces +sam traces --tail +``` \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts new file mode 100644 index 000000000..3dae5015e --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("io.freefair.aspectj.post-compile-weaving") version "8.2.2" + kotlin("jvm") version "1.9.10" +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation("com.amazonaws:aws-lambda-java-core:1.2.3") + implementation("com.fasterxml.jackson.core:jackson-annotations:2.15.1") + implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3") + implementation("com.amazonaws:aws-lambda-java-events:3.16.0") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2") + implementation("org.aspectj:aspectjrt:1.9.20.1") + aspect("software.amazon.lambda:powertools-tracing:2.9.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.9.0") + aspect("software.amazon.lambda:powertools-metrics:2.9.0") + implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") +} + +kotlin { + jvmToolchain(11) +} diff --git a/examples/powertools-examples-core-utilities/kotlin/events/event.json b/examples/powertools-examples-core-utilities/kotlin/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/.gitignore b/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/.gitignore new file mode 100644 index 000000000..59c09e205 --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/.gitignore @@ -0,0 +1,2 @@ +!gradle-wrapper.jar + diff --git a/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..033e24c4c Binary files /dev/null and b/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..1af9e0930 --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/powertools-examples-core-utilities/kotlin/gradlew b/examples/powertools-examples-core-utilities/kotlin/gradlew new file mode 100755 index 000000000..fcb6fca14 --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/examples/powertools-examples-core-utilities/kotlin/gradlew.bat b/examples/powertools-examples-core-utilities/kotlin/gradlew.bat new file mode 100644 index 000000000..93e3f59f1 --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/powertools-examples-core-utilities/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core-utilities/kotlin/src/main/kotlin/helloworld/App.kt new file mode 100644 index 000000000..b05e0d055 --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/src/main/kotlin/helloworld/App.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package helloworld + +import com.amazonaws.services.lambda.runtime.Context +import com.amazonaws.services.lambda.runtime.RequestHandler +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent +import com.amazonaws.xray.entities.Subsegment +import org.slf4j.LoggerFactory +import org.slf4j.MDC +import software.amazon.lambda.powertools.logging.Logging +import software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry +import software.amazon.lambda.powertools.metrics.Metrics +import software.amazon.lambda.powertools.metrics.Metrics +import software.amazon.lambda.powertools.metrics.MetricsFactory +import software.amazon.lambda.powertools.metrics.model.DimensionSet +import software.amazon.lambda.powertools.metrics.model.MetricUnit +import software.amazon.lambda.powertools.tracing.CaptureMode +import software.amazon.lambda.powertools.tracing.Tracing +import software.amazon.lambda.powertools.tracing.TracingUtils +import java.io.IOException +import java.io.InputStreamReader +import java.net.URL + +/** + * Handler for requests to Lambda function. + */ + +class App : RequestHandler<APIGatewayProxyRequestEvent?, APIGatewayProxyResponseEvent> { + private val log = LoggerFactory.getLogger(this::class.java) + private val metrics: Metrics = MetricsFactory.getMetricsInstance() + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { + val headers = mapOf("Content-Type" to "application/json", "X-Custom-Header" to "application/json") + + metrics.addMetric("CustomMetric1", 1.0, MetricUnit.COUNT) + + val dimensionSet = DimensionSet.of( + "AnotherService", "CustomService", + "AnotherService1", "CustomService1" + ) + metrics.flushSingleMetric("CustomMetric2", 1.0, MetricUnit.COUNT, "Another", dimensionSet) + + MDC.put("test", "willBeLogged") + val response = APIGatewayProxyResponseEvent().withHeaders(headers) + return try { + val pageContents = getPageContents("https://checkip.amazonaws.com") + log.info("", entry("ip", pageContents)); + TracingUtils.putAnnotation("Test", "New") + val output = """ + { + "message": "hello world", + "location": "$pageContents" + } + """.trimIndent() + TracingUtils.withSubsegment("loggingResponse") { _: Subsegment? -> + val sampled = "log something out" + log.info(sampled) + log.info(output) + } + log.info("After output") + response.withStatusCode(200).withBody(output) + } catch (e: RuntimeException) { + response.withBody("{}").withStatusCode(500) + } catch (e: IOException) { + response.withBody("{}").withStatusCode(500) + } + } + + @Tracing + private fun log() { + log.info("inside threaded logging for function") + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + @Throws(IOException::class) + private fun getPageContents(address: String): String { + val url = URL(address) + TracingUtils.putMetadata("getPageContents", address) + return InputStreamReader(url.openStream()).use { reader -> + reader.readText().trim() + } + } +} diff --git a/examples/powertools-examples-core-utilities/kotlin/src/main/kotlin/helloworld/AppStream.kt b/examples/powertools-examples-core-utilities/kotlin/src/main/kotlin/helloworld/AppStream.kt new file mode 100644 index 000000000..88f578e3f --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/src/main/kotlin/helloworld/AppStream.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package helloworld + +import com.amazonaws.services.lambda.runtime.Context +import com.amazonaws.services.lambda.runtime.RequestStreamHandler +import com.fasterxml.jackson.databind.ObjectMapper +import software.amazon.lambda.powertools.logging.Logging +import software.amazon.lambda.powertools.metrics.Metrics +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream + +class AppStream : RequestStreamHandler { + @Logging(logEvent = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + @Throws(IOException::class) + override fun handleRequest(input: InputStream, output: OutputStream, context: Context) { + val map: Map<*, *> = mapper.readValue(input, MutableMap::class.java) + println(map.size) + } + + companion object { + private val mapper = ObjectMapper() + } +} diff --git a/examples/powertools-examples-core-utilities/kotlin/template.yaml b/examples/powertools-examples-core-utilities/kotlin/template.yaml new file mode 100644 index 000000000..1a1572fca --- /dev/null +++ b/examples/powertools-examples-core-utilities/kotlin/template.yaml @@ -0,0 +1,71 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + gradle + + Sample SAM Template for gradle + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Runtime: java11 + MemorySize: 512 + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + + HelloWorldStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: helloworld.AppStream::handleRequest + Runtime: java11 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/examples/powertools-examples-core-utilities/sam-bazel/.bazelrc b/examples/powertools-examples-core-utilities/sam-bazel/.bazelrc new file mode 100644 index 000000000..6cdf014a1 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/.bazelrc @@ -0,0 +1,3 @@ +# Java compilation settings +build --java_language_version=11 +build --java_runtime_version=11 diff --git a/examples/powertools-examples-core-utilities/sam-bazel/.gitignore b/examples/powertools-examples-core-utilities/sam-bazel/.gitignore new file mode 100644 index 000000000..4975e987e --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/.gitignore @@ -0,0 +1,20 @@ +# Bazel build outputs +bazel-* +MODULE.bazel.lock + +# SAM build outputs +.aws-sam/ + +# JAR files +*.jar + +# Maven lock file (generated) +maven_install.json + +# IDE files +.idea/ +.vscode/ +*.iml + +# OS files +.DS_Store diff --git a/examples/powertools-examples-core-utilities/sam-bazel/BUILD.bazel b/examples/powertools-examples-core-utilities/sam-bazel/BUILD.bazel new file mode 100644 index 000000000..97fa37394 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/BUILD.bazel @@ -0,0 +1,78 @@ +load("@rules_java//java:defs.bzl", "java_binary", "java_library") + +java_library( + name = "powertools_sam_lib_base", + srcs = glob(["src/main/java/**/*.java"]), + resources = glob(["src/main/resources/**"]), + deps = [ + "@maven//:software_amazon_lambda_powertools_tracing", + "@maven//:software_amazon_lambda_powertools_logging", + "@maven//:software_amazon_lambda_powertools_logging_log4j", + "@maven//:software_amazon_lambda_powertools_metrics", + "@maven//:com_amazonaws_aws_lambda_java_core", + "@maven//:com_amazonaws_aws_lambda_java_events", + "@maven//:org_aspectj_aspectjrt", + "@maven//:org_slf4j_slf4j_api", + "@maven//:com_fasterxml_jackson_core_jackson_databind", + "@maven//:org_apache_logging_log4j_log4j_api", + ], +) + +genrule( + name = "aspectj_weave", + srcs = [ + ":powertools_sam_lib_base", + "@maven//:software_amazon_lambda_powertools_tracing", + "@maven//:software_amazon_lambda_powertools_logging", + "@maven//:software_amazon_lambda_powertools_metrics", + "@maven//:com_amazonaws_aws_lambda_java_core", + "@maven//:com_amazonaws_aws_lambda_java_events", + "@maven//:org_aspectj_aspectjrt", + ], + outs = ["powertools_sam_lib_woven.jar"], + tools = [ + "@maven//:org_aspectj_aspectjweaver", + "@maven//:org_aspectj_aspectjtools", + ], + cmd = """ + # Get dependency JARs for classpath + ASPECTJ_TOOLS="$$(echo $(locations @maven//:org_aspectj_aspectjtools) $(locations @maven//:org_aspectj_aspectjweaver) | tr ' ' ':')" + ASPECTJ_RT="$(locations @maven//:org_aspectj_aspectjrt)" + LAMBDA_JARS="$$(echo $(locations @maven//:com_amazonaws_aws_lambda_java_core) $(locations @maven//:com_amazonaws_aws_lambda_java_events) | tr ' ' ':')" + ALL_DEPS="$$ASPECTJ_TOOLS:$$ASPECTJ_RT:$$LAMBDA_JARS" + + BASE_JAR=$$(echo $(locations :powertools_sam_lib_base) | cut -d' ' -f1) + POWERTOOLS_JARS="$$(echo $(locations @maven//:software_amazon_lambda_powertools_tracing) $(locations @maven//:software_amazon_lambda_powertools_logging) $(locations @maven//:software_amazon_lambda_powertools_metrics) | tr ' ' ':')" + + java -cp "$$ALL_DEPS" \ + org.aspectj.tools.ajc.Main \ + -inpath "$$BASE_JAR" \ + -aspectpath "$$POWERTOOLS_JARS" \ + -outjar $(location powertools_sam_lib_woven.jar) \ + -source 11 -target 11 -nowarn + """, +) + +java_import( + name = "powertools_sam_lib", + jars = [":powertools_sam_lib_woven.jar"], + deps = [ + "@maven//:software_amazon_lambda_powertools_tracing", + "@maven//:software_amazon_lambda_powertools_logging", + "@maven//:software_amazon_lambda_powertools_logging_log4j", + "@maven//:software_amazon_lambda_powertools_metrics", + "@maven//:com_amazonaws_aws_lambda_java_core", + "@maven//:com_amazonaws_aws_lambda_java_events", + "@maven//:org_aspectj_aspectjrt", + ], +) + +java_binary( + name = "powertools_sam", + main_class = "helloworld.App", + runtime_deps = [":powertools_sam_lib"], + deploy_manifest_lines = [ + "Main-Class: helloworld.App", + ], + create_executable = False, +) diff --git a/examples/powertools-examples-core-utilities/sam-bazel/MODULE.bazel b/examples/powertools-examples-core-utilities/sam-bazel/MODULE.bazel new file mode 100644 index 000000000..704b0cf1a --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/MODULE.bazel @@ -0,0 +1,27 @@ +module(name = "powertools_sam_bazel", version = "1.0.0") + +bazel_dep(name = "rules_java", version = "8.12.0") +bazel_dep(name = "rules_jvm_external", version = "6.3") + +maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") + +maven.install( + artifacts = [ + "software.amazon.lambda:powertools-tracing:2.2.1", + "software.amazon.lambda:powertools-logging:2.2.1", + "software.amazon.lambda:powertools-logging-log4j:2.2.1", + "software.amazon.lambda:powertools-metrics:2.2.1", + "com.amazonaws:aws-lambda-java-core:1.3.0", + "com.amazonaws:aws-lambda-java-events:3.16.1", + "org.aspectj:aspectjrt:1.9.22", + "org.aspectj:aspectjweaver:1.9.22", + "org.aspectj:aspectjtools:1.9.22", + "org.slf4j:slf4j-api:2.0.16", + ], + repositories = [ + "https://repo1.maven.org/maven2", + ], + lock_file = "//:maven_install.json", +) + +use_repo(maven, "maven") diff --git a/examples/powertools-examples-core-utilities/sam-bazel/README.md b/examples/powertools-examples-core-utilities/sam-bazel/README.md new file mode 100644 index 000000000..a18af2dd5 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/README.md @@ -0,0 +1,63 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with Bazel + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) and built with Bazel. + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## Configuration + +SAM uses [template.yaml](template.yaml) to define the application's AWS resources. This file defines the Lambda function to be deployed as well as API Gateway for it. + +## Deploy the sample application + +To deploy the example, check out the instructions for getting started with SAM in [the examples directory](../../README.md) + +## Build and deploy + +```bash +# Build the application +bazel build //:powertools_sam_deploy.jar + +# Deploy the application +sam deploy --guided +``` + +## Local testing + +```bash +# Build the application +bazel build //:powertools_sam_deploy.jar + +# Test a single function locally +sam local invoke HelloWorldFunction --event events/event.json + +# Start the local API +sam local start-api + +# Test the API endpoints +curl http://127.0.0.1:3000/hello +curl http://127.0.0.1:3000/hellostream +``` + +## Additional notes + +You can watch the trace information or log information using the SAM CLI: + +```bash +# Tail the logs +sam logs --tail $MY_STACK + +# Tail the traces +sam traces --tail +``` + +### Pinning Maven versions + +To ensure reproducible builds, you can pin Maven dependency versions: + +```bash +# Generate lock file for reproducible builds +bazel run @maven//:pin +``` + +This creates `maven_install.json` which locks dependency versions and should be committed to version control. diff --git a/examples/powertools-examples-core/sam/events/event.json b/examples/powertools-examples-core-utilities/sam-bazel/events/event.json similarity index 100% rename from examples/powertools-examples-core/sam/events/event.json rename to examples/powertools-examples-core-utilities/sam-bazel/events/event.json diff --git a/examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/App.java new file mode 100644 index 000000000..2844e50fe --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/App.java @@ -0,0 +1,108 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; +import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.tracing.CaptureMode; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.tracing.TracingUtils; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(App.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map<String, String> headers = new HashMap<>(); + + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + + DimensionSet dimensionSet = new DimensionSet(); + dimensionSet.addDimension("AnotherService", "CustomService"); + dimensionSet.addDimension("AnotherService1", "CustomService1"); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); + + metrics.addMetric("CustomMetric3", 1, MetricUnit.COUNT, MetricResolution.HIGH); + + MDC.put("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info("", entry("ip", pageContents)); + TracingUtils.putAnnotation("Test", "New"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + TracingUtils.withSubsegment("loggingResponse", subsegment -> { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); + + log.info("After output"); + return response + .withStatusCode(200) + .withBody(output); + } catch (RuntimeException | IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + putMetadata("getPageContents", address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..e04641ddc --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/AppStream.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; + +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + private final static Logger log = LogManager.getLogger(AppStream.class); + + @Override + @Logging(logEvent = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + // RequestStreamHandler can be used instead of RequestHandler for cases when you'd like to deserialize request body or serialize response body yourself, instead of allowing that to happen automatically + // Note that you still need to return a proper JSON for API Gateway to handle + // See Lambda Response format for examples: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html + public void handleRequest(InputStream input, OutputStream output, Context context) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); + PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) { + + log.info("Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); + + writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} "); + } catch (IOException e) { + log.error("Something has gone wrong: ", e); + } + } +} \ No newline at end of file diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/sam-bazel/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/sam/src/main/resources/log4j2.xml rename to examples/powertools-examples-core-utilities/sam-bazel/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core-utilities/sam-bazel/template.yaml b/examples/powertools-examples-core-utilities/sam-bazel/template.yaml new file mode 100644 index 000000000..1e489b8eb --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/template.yaml @@ -0,0 +1,66 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: > + CoreUtilities + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: bazel-bin/powertools_sam_deploy.jar + Handler: helloworld.App::handleRequest + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + + HelloWorldStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: bazel-bin/powertools_sam_deploy.jar + Handler: helloworld.AppStream::handleRequest + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile b/examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile new file mode 100644 index 000000000..8377d6dc7 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile @@ -0,0 +1,14 @@ +#Use the official AWS SAM base image for Java 21 +FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461 + +#Install GraalVM dependencies +RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz +RUN mv graalvm-jdk-21.* /usr/lib/graalvm + +#Make native image and mvn available on CLI +RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image +RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn + +#Set GraalVM as default +ENV JAVA_HOME=/usr/lib/graalvm +ENV PATH=/usr/lib/graalvm/bin:$PATH diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/Makefile b/examples/powertools-examples-core-utilities/sam-graalvm/Makefile new file mode 100644 index 000000000..f8abe6870 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/Makefile @@ -0,0 +1,6 @@ +build-HelloWorldFunction: + mvn clean package -P native-image + chmod +x target/hello-world + cp target/hello-world $(ARTIFACTS_DIR) # (ARTIFACTS_DIR --> https://github.com/aws/aws-lambda-builders/blob/develop/aws_lambda_builders/workflows/custom_make/DESIGN.md#implementation) + chmod +x src/main/config/bootstrap + cp src/main/config/bootstrap $(ARTIFACTS_DIR) diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/README.md b/examples/powertools-examples-core-utilities/sam-graalvm/README.md new file mode 100644 index 000000000..5572684ad --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/README.md @@ -0,0 +1,64 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with SAM on GraalVM + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) running as a GraalVM native image. + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## Configuration + +- SAM uses [template.yaml](template.yaml) to define the application's AWS resources. + This file defines the Lambda function to be deployed as well as API Gateway for it. + +- Set the environment to use GraalVM + +```shell +export JAVA_HOME=<path to GraalVM> +``` + +## Build the sample application + +- Build the Docker image that will be used as the environment for SAM build: + +```shell +docker build --platform linux/amd64 . -t powertools-examples-core-sam-graalvm +``` + +- Build the SAM project using the docker image + +```shell +sam build --use-container --build-image powertools-examples-core-sam-graalvm +``` + +#### [Optional] Building with -SNAPSHOT versions of PowerTools + +- If you are testing the example with a -SNAPSHOT version of PowerTools, the maven build inside the docker image will fail. This is because the -SNAPSHOT version of the PowerTools library that you are working on is still not available in maven central/snapshot repository. + To get around this, follow these steps: + - Create the native image using the `docker` command below on your development machine. The native image is created in the `target` directory. + - `` docker run --platform linux/amd64 -it -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 powertools-examples-core-sam-graalvm mvn clean -Pnative-image package -DskipTests `` + - Edit the [`Makefile`](Makefile) remove this line + - `mvn clean package -P native-image` + - Build the SAM project using the docker image + - `sam build --use-container --build-image powertools-examples-core-sam-graalvm` + +## Deploy the sample application + +- SAM deploy + +```shell +sam deploy +``` + +To deploy the example, check out the instructions for getting +started with SAM in [the examples directory](../../README.md) + +## Additional notes + +You can watch the trace information or log information using the SAM CLI: + +```bash +# Tail the logs +sam logs --tail $MY_STACK + +# Tail the traces +sam traces --tail +``` diff --git a/examples/powertools-examples-sqs/events/event.json b/examples/powertools-examples-core-utilities/sam-graalvm/events/event.json similarity index 100% rename from examples/powertools-examples-sqs/events/event.json rename to examples/powertools-examples-core-utilities/sam-graalvm/events/event.json diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml new file mode 100644 index 000000000..eea0357e9 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -0,0 +1,186 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> + <packaging>jar</packaging> + + <properties> + <log4j.version>2.25.3</log4j.version> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.7</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-layout-template-json</artifactId> + <version>${log4j.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.15.0</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>hello-world</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <!-- required for AWS Lambda Runtime Interface Client --> + <arg>--enable-url-protocols=http</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/config/bootstrap b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/config/bootstrap new file mode 100644 index 000000000..8e7928cd3 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/config/bootstrap @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +./hello-world $_HANDLER \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/java/helloworld/App.java new file mode 100644 index 000000000..68664feec --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/java/helloworld/App.java @@ -0,0 +1,110 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; +import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.tracing.CaptureMode; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.tracing.TracingUtils; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(App.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map<String, String> headers = new HashMap<>(); + + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + + DimensionSet dimensionSet = DimensionSet.of( + "AnotherService", "CustomService", + "AnotherService1", "CustomService1"); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); + + metrics.addMetric("CustomMetric3", 1, MetricUnit.COUNT, MetricResolution.HIGH); + + MDC.put("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info("", entry("ip", pageContents)); + TracingUtils.putAnnotation("Test", "New"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + TracingUtils.withSubsegment("loggingResponse", subsegment -> { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); + + log.info("After output"); + return response + .withStatusCode(200) + .withBody(output); + } catch (RuntimeException | IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + @Tracing + private void log() { + log.info("inside threaded logging for function"); + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + putMetadata("getPageContents", address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..e97baa294 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,44 @@ +[ + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields":[{"name":"logger"}] + }, + { + "name":"java.lang.Void", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] + }, + { + "name":"jdk.internal.module.IllegalAccessLogger", + "fields":[{"name":"logger"}] + }, + { + "name":"sun.misc.Unsafe", + "fields":[{"name":"theUnsafe"}] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties new file mode 100644 index 000000000..db5ebaa55 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties @@ -0,0 +1 @@ +Args = --enable-url-protocols=http,https \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json new file mode 100644 index 000000000..06ea9ce2f --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json @@ -0,0 +1,11 @@ +[ + { + "name": "helloworld.App", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-logging/src/test/resources/log4j2.xml b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/log4j2.xml similarity index 84% rename from powertools-logging/src/test/resources/log4j2.xml rename to examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/log4j2.xml index 22a44ee8b..60975d487 100644 --- a/powertools-logging/src/test/resources/log4j2.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/log4j2.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> - <File name="JsonAppender" fileName="target/logfile.json"> + <Console name="JsonAppender"> <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> - </File> + </Console> </Appenders> <Loggers> <Logger name="JsonLogger" level="INFO" additivity="false"> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/template.yaml b/examples/powertools-examples-core-utilities/sam-graalvm/template.yaml new file mode 100644 index 000000000..feb55743b --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/template.yaml @@ -0,0 +1,52 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + CoreUtilities + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Runtime: provided.al2023 + MemorySize: 512 + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + Metadata: + BuildMethod: makefile + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/examples/powertools-examples-core/sam/README.md b/examples/powertools-examples-core-utilities/sam/README.md similarity index 100% rename from examples/powertools-examples-core/sam/README.md rename to examples/powertools-examples-core-utilities/sam/README.md diff --git a/examples/powertools-examples-idempotency/src/test/resources/event.json b/examples/powertools-examples-core-utilities/sam/events/event.json similarity index 97% rename from examples/powertools-examples-idempotency/src/test/resources/event.json rename to examples/powertools-examples-core-utilities/sam/events/event.json index fd7f5ace7..3822fadaa 100644 --- a/examples/powertools-examples-idempotency/src/test/resources/event.json +++ b/examples/powertools-examples-core-utilities/sam/events/event.json @@ -1,5 +1,5 @@ { - "body": "{\"address\": \"https://checkip.amazonaws.com\"}", + "body": "{\"message\": \"hello world\"}", "resource": "/{proxy+}", "path": "/path/to/resource", "httpMethod": "POST", diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml new file mode 100644 index 000000000..2d6a00161 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -0,0 +1,127 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-core-utilities-sam</artifactId> + <packaging>jar</packaging> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/App.java new file mode 100644 index 000000000..2844e50fe --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/App.java @@ -0,0 +1,108 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; +import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.tracing.CaptureMode; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.tracing.TracingUtils; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(App.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map<String, String> headers = new HashMap<>(); + + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + + DimensionSet dimensionSet = new DimensionSet(); + dimensionSet.addDimension("AnotherService", "CustomService"); + dimensionSet.addDimension("AnotherService1", "CustomService1"); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); + + metrics.addMetric("CustomMetric3", 1, MetricUnit.COUNT, MetricResolution.HIGH); + + MDC.put("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info("", entry("ip", pageContents)); + TracingUtils.putAnnotation("Test", "New"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + TracingUtils.withSubsegment("loggingResponse", subsegment -> { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); + + log.info("After output"); + return response + .withStatusCode(200) + .withBody(output); + } catch (RuntimeException | IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + putMetadata("getPageContents", address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..d3ebbef5d --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + private static final Logger log = LoggerFactory.getLogger(AppStream.class); + + @Override + @Logging(logEvent = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + // RequestStreamHandler can be used instead of RequestHandler for cases when you'd like to deserialize request body + // or serialize response body yourself, instead of allowing that to happen automatically + // Note that you still need to return a proper JSON for API Gateway to handle + // See Lambda Response format for examples: + // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html + public void handleRequest(InputStream input, OutputStream output, Context context) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); + PrintWriter writer = new PrintWriter( + new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) { + + log.info( + "Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); + + writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} "); + } catch (IOException e) { + log.error("Something has gone wrong: ", e); + } + } +} diff --git a/examples/powertools-examples-core-utilities/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/sam/src/main/resources/log4j2.xml new file mode 100644 index 000000000..e140022e4 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <BufferingAppender name="BufferedAppender" bufferAtVerbosity="DEBUG"> + <AppenderRef ref="JsonAppender" /> + </BufferingAppender> + </Appenders> + <Loggers> + <Root level="debug"> + <AppenderRef ref="BufferedAppender" /> + </Root> + </Loggers> +</Configuration> diff --git a/examples/powertools-examples-core-utilities/sam/template.yaml b/examples/powertools-examples-core-utilities/sam/template.yaml new file mode 100644 index 000000000..6b1814dce --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam/template.yaml @@ -0,0 +1,66 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: > + CoreUtilities + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: DEBUG # We use log buffering to buffer DEBUG logs (see log4j2.xml) + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + + HelloWorldStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: helloworld.AppStream::handleRequest + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/examples/powertools-examples-core-utilities/serverless/README.md b/examples/powertools-examples-core-utilities/serverless/README.md new file mode 100644 index 000000000..9e33aa9ff --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/README.md @@ -0,0 +1,26 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with Serverless Framework + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Framework](https://www.serverless.com/framework). +For general information on the deployed example itself, you can refer to the parent [README](../README.md). +To install Serverless Framework if you don't have it yet, you can follow the [Getting Started Guide](https://www.serverless.com/framework/docs/getting-started). + +## Configuration +Serverless Framework uses [serverless.yml](serverless.yml) to define the application's AWS resources. +This file defines the Lambda function to be deployed as well as API Gateway for it. + +It is a [Maven](https://maven.apache.org/) based project, so you can open this project with any Maven compatible Java IDE to build and run tests. + + +## Deploy the sample application + +To deploy the app, simply run the following commands: +```bash +mvn package && sls deploy +``` + +## Useful commands + +Deploy a single function +```bash +sls deploy function -f hello +``` \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml new file mode 100644 index 000000000..26e647dad --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -0,0 +1,128 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-core-utilities-serverless</artifactId> + <packaging>jar</packaging> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + + <build> + <finalName>helloworld-lambda</finalName> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-core-utilities/serverless/serverless.yml b/examples/powertools-examples-core-utilities/serverless/serverless.yml new file mode 100644 index 000000000..d8dec8080 --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/serverless.yml @@ -0,0 +1,40 @@ +service: hello +# app and org for use with dashboard.serverless.com +#app: your-app-name +#org: your-org-name + +# You can pin your service to only deploy with a specific Serverless version +# Check out our docs for more details +frameworkVersion: '3' + +provider: + name: aws + runtime: java11 + +# you can overwrite defaults here +# stage: dev +# region: us-east-1 + +# you can define service wide environment variables here + environment: + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +# you can add packaging information here +package: + artifact: target/helloworld-lambda.jar + +functions: + hello: + handler: helloworld.App + memorySize: 512 + timeout: 20 + tracing: "Active" + events: + - httpApi: + path: /hello + method: get +# Define function environment variables here + environment: + POWERTOOLS_SERVICE_NAME: hello diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java similarity index 76% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/App.java rename to examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java index fccc63b9a..771f5c1f1 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java @@ -14,8 +14,7 @@ package helloworld; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; import com.amazonaws.services.lambda.runtime.Context; @@ -31,11 +30,13 @@ import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; +import org.slf4j.MDC; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; import software.amazon.lambda.powertools.tracing.TracingUtils; @@ -44,37 +45,36 @@ * Handler for requests to Lambda function. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger log = LogManager.getLogger(App.class); + private static final Logger log = LogManager.getLogger(App.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("X-Custom-Header", "application/json"); - metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> - { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); + DimensionSet dimensionSet = DimensionSet.of( + "AnotherService", "CustomService", + "AnotherService1", "CustomService1"); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); - LoggingUtils.appendKey("test", "willBeLogged"); + MDC.put("test", "willBeLogged"); APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() .withHeaders(headers); try { final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); - log.info(pageContents); + log.info("", entry("ip", pageContents)); TracingUtils.putAnnotation("Test", "New"); String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); - TracingUtils.withSubsegment("loggingResponse", subsegment -> - { + TracingUtils.withSubsegment("loggingResponse", subsegment -> { String sampled = "log something out"; log.info(sampled); log.info(output); @@ -91,11 +91,6 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv } } - @Tracing - private void log() { - log.info("inside threaded logging for function"); - } - @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) private String getPageContents(String address) throws IOException { URL url = new URL(address); diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/AppStream.java similarity index 89% rename from examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/AppStream.java index 401ef8c48..c13ab9f2e 100644 --- a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/AppStream.java @@ -22,14 +22,14 @@ import java.io.OutputStream; import java.util.Map; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.FlushMetrics; public class AppStream implements RequestStreamHandler { private static final ObjectMapper mapper = new ObjectMapper(); @Override @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { Map map = mapper.readValue(input, Map.class); diff --git a/examples/powertools-examples-sqs/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/serverless/src/main/resources/log4j2.xml similarity index 96% rename from examples/powertools-examples-sqs/src/main/resources/log4j2.xml rename to examples/powertools-examples-core-utilities/serverless/src/main/resources/log4j2.xml index e1fd14cea..0cc0953f0 100644 --- a/examples/powertools-examples-sqs/src/main/resources/log4j2.xml +++ b/examples/powertools-examples-core-utilities/serverless/src/main/resources/log4j2.xml @@ -13,4 +13,4 @@ <AppenderRef ref="JsonAppender"/> </Root> </Loggers> -</Configuration> \ No newline at end of file +</Configuration> diff --git a/examples/powertools-examples-core-utilities/terraform/.tflint.hcl b/examples/powertools-examples-core-utilities/terraform/.tflint.hcl new file mode 100644 index 000000000..18e69352b --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/.tflint.hcl @@ -0,0 +1,3 @@ +rule "terraform_required_version" { + enabled = false +} \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/terraform/README.md b/examples/powertools-examples-core-utilities/terraform/README.md new file mode 100644 index 000000000..71c78d437 --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/README.md @@ -0,0 +1,27 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with Terraform + +This project demonstrates the Lambda for Powertools Java module deployed using [Terraform](https://www.terraform.io/). +For general information on the deployed example itself, you can refer to the parent [README](../README.md). +To install Terraform if you don't have it yet, you can follow the [Install Terraform Guide](https://developer.hashicorp.com/terraform/downloads?product_intent=terraform). + +## Configuration +Terraform uses [main.tf](./main.tf) to define the application's AWS resources. +This file defines the Lambda function to be deployed as well as API Gateway for it. + +It is a [Maven](https://maven.apache.org/) based project, so you can open this project with any Maven compatible Java IDE to build and run tests. + + +## Deploy the sample application + +To deploy the app, simply run the following commands: +```bash +terraform init +mvn package && terraform apply +``` + +## Useful commands + +To destroy the app +```bash +terraform destroy +``` diff --git a/examples/powertools-examples-core-utilities/terraform/infra/api-gateway.tf b/examples/powertools-examples-core-utilities/terraform/infra/api-gateway.tf new file mode 100644 index 000000000..dba1c3616 --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/infra/api-gateway.tf @@ -0,0 +1,94 @@ +resource "aws_api_gateway_rest_api" "hello_world_api" { + name = "hello_world_api" + description = "API Gateway endpoint URL for Prod stage for Hello World function" +} + +resource "aws_api_gateway_resource" "hello_resource" { + rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}" + parent_id = "${aws_api_gateway_rest_api.hello_world_api.root_resource_id}" + path_part = "hello" +} + +resource "aws_api_gateway_resource" "hello_stream_resource" { + rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}" + parent_id = "${aws_api_gateway_rest_api.hello_world_api.root_resource_id}" + path_part = "hellostream" +} + +resource "aws_api_gateway_method" "hello_get_method" { + rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}" + resource_id = "${aws_api_gateway_resource.hello_resource.id}" + http_method = "GET" + authorization = "NONE" +} + +resource "aws_api_gateway_method" "hello_stream_get_method" { + rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}" + resource_id = "${aws_api_gateway_resource.hello_stream_resource.id}" + http_method = "GET" + authorization = "NONE" +} + +resource "aws_api_gateway_integration" "java_lambda_integration" { + rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}" + resource_id = "${aws_api_gateway_resource.hello_resource.id}" + http_method = "${aws_api_gateway_method.hello_get_method.http_method}" + + integration_http_method = "POST" + type = "AWS_PROXY" + uri = "${aws_lambda_function.hello_world_lambda.invoke_arn}" +} + +resource "aws_api_gateway_integration" "java_stream_lambda_integration" { + rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}" + resource_id = "${aws_api_gateway_resource.hello_stream_resource.id}" + http_method = "${aws_api_gateway_method.hello_stream_get_method.http_method}" + + integration_http_method = "POST" + type = "AWS_PROXY" + uri = "${aws_lambda_function.hello_world_stream_lambda.invoke_arn}" +} + +resource "aws_api_gateway_deployment" "prod_deployment" { + depends_on = [aws_api_gateway_integration.java_lambda_integration, aws_api_gateway_integration.java_stream_lambda_integration] + rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}" + stage_name = "prod" +} + +# Allows API gateway to invoke lambda +resource "aws_lambda_permission" "hello_world_lambda_invoke" { + statement_id = "AllowAPIGatewayInvoke" + action = "lambda:InvokeFunction" + function_name = "${aws_lambda_function.hello_world_lambda.function_name}" + principal = "apigateway.amazonaws.com" + source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/${aws_api_gateway_deployment.prod_deployment.stage_name}/GET/hello" +} + +# Allows API gateway to invoke lambda +resource "aws_lambda_permission" "hello_world_lambda_testinvoke" { + statement_id = "AllowAPIGatewayTestInvoke" + action = "lambda:InvokeFunction" + function_name = "${aws_lambda_function.hello_world_lambda.function_name}" + principal = "apigateway.amazonaws.com" + source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/test-invoke-stage/GET/hello" +} + +# Allows API gateway to invoke lambda +resource "aws_lambda_permission" "hello_world_stream_lambda_invoke" { + statement_id = "AllowAPIGatewayInvoke" + action = "lambda:InvokeFunction" + function_name = "${aws_lambda_function.hello_world_stream_lambda.function_name}" + principal = "apigateway.amazonaws.com" + source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/${aws_api_gateway_deployment.prod_deployment.stage_name}/GET/hellostream" +} + +# Allows API gateway to invoke lambda +resource "aws_lambda_permission" "hello_world_stream_lambda_testinvoke" { + statement_id = "AllowAPIGatewayTestInvoke" + action = "lambda:InvokeFunction" + function_name = "${aws_lambda_function.hello_world_stream_lambda.function_name}" + principal = "apigateway.amazonaws.com" + source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/test-invoke-stage/GET/hellostream" +} + +output "invoke" {value=aws_api_gateway_deployment.prod_deployment.invoke_url} \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/terraform/infra/lambda.tf b/examples/powertools-examples-core-utilities/terraform/infra/lambda.tf new file mode 100644 index 000000000..abebccf54 --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/infra/lambda.tf @@ -0,0 +1,95 @@ +resource "aws_lambda_function" "hello_world_lambda" { + runtime = "java11" + filename = "target/helloworld-lambda.jar" + source_code_hash = filebase64sha256("target/helloworld-lambda.jar") + function_name = "hello_world_lambda" + + handler = "helloworld.App" + description = "Powertools example, deployed by Terraform" + timeout = 20 + memory_size = 512 + role = "${aws_iam_role.iam_role_for_lambda.arn}" + tracing_config { + mode = "Active" + } + depends_on = [aws_cloudwatch_log_group.log_group] +} + +resource "aws_lambda_function" "hello_world_stream_lambda" { + runtime = "java11" + filename = "target/helloworld-lambda.jar" + source_code_hash = filebase64sha256("target/helloworld-lambda.jar") + function_name = "hello_world_stream_lambda" + + handler = "helloworld.AppStream" + description = "Powertools example, deployed by Terraform" + timeout = 20 + memory_size = 512 + role = "${aws_iam_role.iam_role_for_lambda.arn}" + tracing_config { + mode = "Active" + } + depends_on = [aws_cloudwatch_log_group.log_group] +} + +# Create a log group for the lambda +resource "aws_cloudwatch_log_group" "log_group" { + name = "/aws/lambda/hello_world_lambda" +} + +# Create a log group for the lambda +resource "aws_cloudwatch_log_group" "log_group_stream" { + name = "/aws/lambda/hello_world_stream_lambda" +} + +# lambda role +resource "aws_iam_role" "iam_role_for_lambda" { + name = "lambda-invoke-role" + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] + } +EOF +} + +# lambda policy, allow logs to be published to CloudWatch, and traces to Xray +resource "aws_iam_policy" "iam_policy_for_lambda" { + name = "lambda-invoke-policy" + path = "/" + + policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "LambdaPolicy", + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + "xray:PutTelemetryRecords", + "xray:PutTraceSegments" + ], + "Resource": "*" + } + ] + } +EOF +} + +# Attach the policy to the role +resource "aws_iam_role_policy_attachment" "aws_iam_role_policy_attachment" { + role = "${aws_iam_role.iam_role_for_lambda.name}" + policy_arn = "${aws_iam_policy.iam_policy_for_lambda.arn}" +} diff --git a/examples/powertools-examples-core-utilities/terraform/main.tf b/examples/powertools-examples-core-utilities/terraform/main.tf new file mode 100644 index 000000000..10504088a --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/main.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +# terraform modules +module "powertools_for_java_lambda" { + source = "./infra/" +} + +output "api_url" { + value = module.powertools_for_java_lambda.invoke + description = "URL where the API gateway can be invoked" +} + +# Configure the AWS Provider +provider "aws" { +} \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml new file mode 100644 index 000000000..4de1e415c --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -0,0 +1,138 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-core-utilities-terraform</artifactId> + <packaging>jar</packaging> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + + <build> + <finalName>helloworld-lambda</finalName> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <LAMBDA_TASK_ROOT>handler</LAMBDA_TASK_ROOT> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/App.java new file mode 100644 index 000000000..771f5c1f1 --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/App.java @@ -0,0 +1,102 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; +import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.FlushMetrics; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.tracing.CaptureMode; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.tracing.TracingUtils; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LogManager.getLogger(App.class); + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map<String, String> headers = new HashMap<>(); + + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + + DimensionSet dimensionSet = DimensionSet.of( + "AnotherService", "CustomService", + "AnotherService1", "CustomService1"); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); + + MDC.put("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info("", entry("ip", pageContents)); + TracingUtils.putAnnotation("Test", "New"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + TracingUtils.withSubsegment("loggingResponse", subsegment -> { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); + + log.info("After output"); + return response + .withStatusCode(200) + .withBody(output); + } catch (RuntimeException | IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + putMetadata("getPageContents", address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/AppStream.java similarity index 89% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/AppStream.java index 401ef8c48..c13ab9f2e 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/AppStream.java @@ -22,14 +22,14 @@ import java.io.OutputStream; import java.util.Map; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.FlushMetrics; public class AppStream implements RequestStreamHandler { private static final ObjectMapper mapper = new ObjectMapper(); @Override @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + @FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { Map map = mapper.readValue(input, Map.class); diff --git a/examples/powertools-examples-core-utilities/terraform/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/terraform/src/main/resources/log4j2.xml new file mode 100644 index 000000000..0cc0953f0 --- /dev/null +++ b/examples/powertools-examples-core-utilities/terraform/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender"/> + </Root> + </Loggers> +</Configuration> diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md deleted file mode 100644 index f11982477..000000000 --- a/examples/powertools-examples-core/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example - -This project demonstrates the Lambda for Powertools Java module - including -[logging](https://docs.powertools.aws.dev/lambda/java/core/logging/), -[tracing](https://docs.powertools.aws.dev/lambda/java/core/tracing/), and -[metrics](https://docs.powertools.aws.dev/lambda/java/core/metrics/). - -We provide examples for the following infrastructure-as-code tools: -* [AWS SAM](sam/) -* [AWS CDK](cdk/) - -For each of the tools, the example application is the same, and consists of the following files: - -- [App.java](sam/src/main/java/helloworld/App.java) - Code for the application's Lambda function. -- [AppTests.java](sam/src/test/java/helloworld/AppTest.java) - Unit tests for the application code. -- [events](sam/events/event.json) - Invocation events that you can use to invoke the function. - -Configuration files and deployment process for each tool are described in corresponding README files. - -## Test the application - -Once the app is deployed, you can invoke the endpoint like this: - -```bash - curl https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ -``` - -The response itself isn't particularly interesting - you will get back some information about your IP address. If -you go to the Lambda Console and locate the lambda you have deployed, then click the "Monitoring" tab you will -be able to find: - -* **View X-Ray traces** - Display the traces captured by the traces module. These include subsegments for the -different function calls within the example -* **View Cloudwatch logs** - Display the structured logging output of the example - -Likewise, from the CloudWatch dashboard, under **Metrics**, **all metrics**, you will find the namespaces `Another` -and `ServerlessAirline`. The values in each of these are published by the code in -[App.java](sam/src/main/java/helloworld/App.java). diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml deleted file mode 100644 index a4359e223..000000000 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ /dev/null @@ -1,202 +0,0 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.1</version> - <artifactId>powertools-examples-core-cdk</artifactId> - <packaging>jar</packaging> - - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> - - <properties> - <log4j.version>2.20.0</log4j.version> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - <version>${log4j.version}</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - <version>${log4j.version}</version> - </dependency> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <finalName>helloworld-lambda</finalName> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <transformers> - <transformer - implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> - </transformer> - </transformers> - </configuration> - </execution> - </executions> - <dependencies> - <dependency> - <groupId>com.github.edwgiz</groupId> - <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> - <version>2.15</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> -</project> diff --git a/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java deleted file mode 100644 index 70dad8d71..000000000 --- a/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package helloworld; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.amazonaws.xray.AWSXRay; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -public class AppTest { - - @Before - public void setup() { - if (null == System.getenv("LAMBDA_TASK_ROOT")) { - AWSXRay.beginSegment("test"); - } - } - - @After - public void tearDown() { - if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { - AWSXRay.endSubsegment(); - } - - if (null == System.getenv("LAMBDA_TASK_ROOT")) { - AWSXRay.endSegment(); - } - } - - @Test - public void successfulResponse() { - App app = new App(); - APIGatewayProxyResponseEvent result = app.handleRequest(null, null); - assertEquals(result.getStatusCode().intValue(), 200); - assertEquals(result.getHeaders().get("Content-Type"), "application/json"); - String content = result.getBody(); - assertNotNull(content); - assertTrue(content.contains("\"message\"")); - assertTrue(content.contains("\"hello world\"")); - assertTrue(content.contains("\"location\"")); - } -} diff --git a/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java deleted file mode 100644 index 29cb15545..000000000 --- a/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cdk; - -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.Test; -import software.amazon.awscdk.App; -import software.amazon.awscdk.assertions.Template; - -public class CdkStackTest { - - @Test - public void testStack() { - App app = new App(); - CdkStack stack = new CdkStack(app, "test"); - - Template template = Template.fromStack(stack); - - // There should be 2 lambda functions, one to handle regular input, and another for streaming - template.resourceCountIs("AWS::Lambda::Function", 2); - - // API Gateway should exist - template.resourceCountIs("AWS::ApiGateway::RestApi", 1); - - // API Gateway should have a path pointing to the regular Lambda - Map<String, String> resourceProperties = new HashMap<>(); - resourceProperties.put("PathPart", "hello"); - template.hasResourceProperties("AWS::ApiGateway::Resource", resourceProperties); - - // API Gateway should have a path pointing to the streaming Lambda - resourceProperties = new HashMap<>(); - resourceProperties.put("PathPart", "hellostream"); - template.hasResourceProperties("AWS::ApiGateway::Resource", resourceProperties); - } -} diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml deleted file mode 100644 index bc3667b4f..000000000 --- a/examples/powertools-examples-core/sam/pom.xml +++ /dev/null @@ -1,201 +0,0 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> - <artifactId>powertools-examples-core-sam</artifactId> - <packaging>jar</packaging> - - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> - - <properties> - <log4j.version>2.20.0</log4j.version> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - <version>${log4j.version}</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - <version>${log4j.version}</version> - </dependency> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <transformers> - <transformer - implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> - </transformer> - </transformers> - </configuration> - </execution> - </executions> - <dependencies> - <dependency> - <groupId>com.github.edwgiz</groupId> - <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> - <version>2.15</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> -</project> diff --git a/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java deleted file mode 100644 index 70dad8d71..000000000 --- a/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package helloworld; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.amazonaws.xray.AWSXRay; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -public class AppTest { - - @Before - public void setup() { - if (null == System.getenv("LAMBDA_TASK_ROOT")) { - AWSXRay.beginSegment("test"); - } - } - - @After - public void tearDown() { - if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { - AWSXRay.endSubsegment(); - } - - if (null == System.getenv("LAMBDA_TASK_ROOT")) { - AWSXRay.endSegment(); - } - } - - @Test - public void successfulResponse() { - App app = new App(); - APIGatewayProxyResponseEvent result = app.handleRequest(null, null); - assertEquals(result.getStatusCode().intValue(), 200); - assertEquals(result.getHeaders().get("Content-Type"), "application/json"); - String content = result.getBody(); - assertNotNull(content); - assertTrue(content.contains("\"message\"")); - assertTrue(content.contains("\"hello world\"")); - assertTrue(content.contains("\"location\"")); - } -} diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml deleted file mode 100644 index f24b2ffc4..000000000 --- a/examples/powertools-examples-idempotency/pom.xml +++ /dev/null @@ -1,271 +0,0 @@ -<!-- - ~ Copyright 2023 Amazon.com, Inc. or its affiliates. - ~ Licensed under the Apache License, Version 2.0 (the - ~ "License"); you may not use this file except in compliance - ~ with the License. You may obtain a copy of the License at - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - ~ - --> - -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> - <artifactId>powertools-examples-idempotency</artifactId> - <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> - - <properties> - <log4j.version>2.20.0</log4j.version> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - <version>${log4j.version}</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - <version>${log4j.version}</version> - </dependency> - - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>4.11.0</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <version>5.9.3</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>DynamoDBLocal</artifactId> - <version>1.22.0</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-tests</artifactId> - <version>1.1.1</version> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>copy</id> - <phase>test-compile</phase> - <goals> - <goal>copy-dependencies</goal> - </goals> - <configuration> - <includeScope>test</includeScope> - <includeTypes>so,dll,dylib</includeTypes> - <outputDirectory>${project.build.directory}/native-libs</outputDirectory> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> - <configuration> - <systemPropertyVariables> - <sqlite4java.library.path>${project.build.directory}/native-libs</sqlite4java.library.path> - </systemPropertyVariables> - <environmentVariables> - <IDEMPOTENCY_TABLE>idempotency</IDEMPOTENCY_TABLE> - <AWS_REGION>eu-central-1</AWS_REGION> - <AWS_XRAY_CONTEXT_MISSING>LOG_ERROR</AWS_XRAY_CONTEXT_MISSING> - </environmentVariables> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <transformers> - <transformer - implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> - </transformer> - </transformers> - </configuration> - </execution> - </executions> - <dependencies> - <dependency> - <groupId>com.github.edwgiz</groupId> - <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> - <version>2.15</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> - <repositories> - <repository> - <id>dynamodb-local-oregon</id> - <name>DynamoDB Local Release Repository</name> - <url>https://s3.eu-central-1.amazonaws.com/dynamodb-local-frankfurt/release</url> - </repository> - </repositories> -</project> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/Dockerfile b/examples/powertools-examples-idempotency/sam-graalvm/Dockerfile new file mode 100644 index 000000000..dac9390e5 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/Dockerfile @@ -0,0 +1,14 @@ +# Use the official AWS SAM base image for Java 21 +FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461 + +# Install GraalVM dependencies +RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz +RUN mv graalvm-jdk-21.* /usr/lib/graalvm + +# Make native image and mvn available on CLI +RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image +RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn + +# Set GraalVM as default +ENV JAVA_HOME=/usr/lib/graalvm +ENV PATH=/usr/lib/graalvm/bin:$PATH diff --git a/examples/powertools-examples-idempotency/sam-graalvm/Makefile b/examples/powertools-examples-idempotency/sam-graalvm/Makefile new file mode 100644 index 000000000..6fb4e537c --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/Makefile @@ -0,0 +1,5 @@ +build-IdempotencyFunction: + chmod +x target/hello-world + cp target/hello-world $(ARTIFACTS_DIR) # (ARTIFACTS_DIR --> https://github.com/aws/aws-lambda-builders/blob/develop/aws_lambda_builders/workflows/custom_make/DESIGN.md#implementation) + chmod +x src/main/config/bootstrap + cp src/main/config/bootstrap $(ARTIFACTS_DIR) diff --git a/examples/powertools-examples-idempotency/sam-graalvm/README.md b/examples/powertools-examples-idempotency/sam-graalvm/README.md new file mode 100644 index 000000000..1c44bd791 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/README.md @@ -0,0 +1,61 @@ +# Powertools for AWS Lambda (Java) - Idempotency Example with SAM on GraalVM + +This project contains an example of a Lambda function using the idempotency module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://docs.powertools.aws.dev/lambda-java/utilities/idempotency/). + +The example exposes a HTTP POST endpoint. When the user sends the address of a webpage to it, the endpoint fetches the contents of the URL and returns them to the user. + +Have a look at [App.java](src/main/java/helloworld/App.java) for the full details. + +## Build the sample application + +> [!NOTE] +> Building AWS Lambda packages on macOS (ARM64/Intel) for deployment on AWS Lambda (Linux x86_64 or ARM64) will result in incompatible binary dependencies that cause import errors at runtime. + +Choose the appropriate build method based on your operating system: + +### Build locally using Docker + +Recommended for macOS and Windows users: Cross-compile using Docker to match target platform of Lambda: + +```shell +docker build --platform linux/amd64 . -t powertools-examples-idempotency-sam-graalvm +docker run --platform linux/amd64 -it -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 powertools-examples-idempotency-sam-graalvm mvn clean -Pnative-image package -DskipTests +sam build --use-container --build-image powertools-examples-idempotency-sam-graalvm +``` + +**Note**: The Docker run command mounts your local Maven cache (`~/.m2`) and builds the native binary with SNAPSHOT support, then SAM packages the pre-built binary. + +### Build on native OS + +For Linux users with GraalVM installed: + +```shell +export JAVA_HOME=<path to GraalVM> +mvn clean -Pnative-image package -DskipTests +sam build +``` + +## Deploy the sample application + +```shell +sam deploy +``` + +This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting started with SAM in [the examples directory](../../README.md) + +## Test the application + +```bash +curl -X POST https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/helloidem/ -H "Content-Type: application/json" -d '{"address": "https://checkip.amazonaws.com"}' +``` + +this should return the contents of the webpage, for instance: + +```json +{ "message": "hello world", "location": "123.123.123.1" } +``` + +- First call will execute the handleRequest normally, and store the response in the idempotency table (Look into DynamoDB) +- Second call (and next ones) will retrieve from the cache (if cache is enabled, which is by default) or from the store, the handler won't be called. Until the expiration happens (by default 1 hour). + +Check out [App.java](src/main/java/helloworld/App.java) to see how it works! diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml new file mode 100644 index 000000000..0536951aa --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -0,0 +1,167 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-idempotency-sam-graalvm</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM</name> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-dynamodb</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.7</version> + </dependency> + + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-core</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>hello-world</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <!-- required for AWS Lambda Runtime Interface Client --> + <arg>--enable-url-protocols=http,https</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/config/bootstrap b/examples/powertools-examples-idempotency/sam-graalvm/src/main/config/bootstrap new file mode 100644 index 000000000..8e7928cd3 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/config/bootstrap @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +./hello-world $_HANDLER \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/sam-graalvm/src/main/java/helloworld/App.java similarity index 70% rename from examples/powertools-examples-idempotency/src/main/java/helloworld/App.java rename to examples/powertools-examples-idempotency/sam-graalvm/src/main/java/helloworld/App.java index 72fa621ad..ebe9fac22 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/java/helloworld/App.java @@ -26,18 +26,19 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.Idempotent; -import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.tracing.Tracing; import software.amazon.lambda.powertools.utilities.JsonConfig; public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger log = LogManager.getLogger(App.class); + private static final Logger log = LoggerFactory.getLogger(App.class); public App() { this(null); @@ -45,20 +46,36 @@ public App() { public App(DynamoDbClient client) { Idempotency.config().withConfig( - IdempotencyConfig.builder() - .withEventKeyJMESPath( - "powertools_json(body).address") // will retrieve the address field in the body which is a string transformed to json with `powertools_json` - .build()) + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).address") + .withResponseHook((responseData, dataRecord) -> { + if (responseData instanceof APIGatewayProxyResponseEvent) { + APIGatewayProxyResponseEvent proxyResponse = (APIGatewayProxyResponseEvent) responseData; + final Map<String, String> headers = new HashMap<>(); + headers.putAll(proxyResponse.getHeaders()); + headers.put("x-idempotency-response", "true"); + headers.put("x-idempotency-expiration", + String.valueOf(dataRecord.getExpiryTimestamp())); + + proxyResponse.setHeaders(headers); + + return proxyResponse; + } + + return responseData; + }) + .build()) .withPersistenceStore( DynamoDBPersistenceStore.builder() .withDynamoDbClient(client) .withTableName(System.getenv("IDEMPOTENCY_TABLE")) - .build() - ).configure(); + .build()) + .configure(); } /** - * This is our Lambda event handler. It accepts HTTP POST requests from API gateway and returns the contents of the given URL. Requests are made idempotent + * This is your Lambda event handler. It accepts HTTP POST requests from API gateway and returns the contents of the + * given URL. Requests are made idempotent * by the idempotency library, and results are cached for the default 1h expiry time. * <p> * You can test the endpoint like this: @@ -67,12 +84,15 @@ public App(DynamoDbClient client) { * curl -X POST https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/helloidem/ -H "Content-Type: application/json" -d '{"address": "https://checkip.amazonaws.com"}' * </pre> * <ul> - * <li>First call will execute the handleRequest normally, and store the response in the idempotency table (Look into DynamoDB)</li> - * <li>Second call (and next ones) will retrieve from the cache (if cache is enabled, which is by default) or from the store, the handler won't be called. Until the expiration happens (by default 1 hour).</li> + * <li>First call will execute the handleRequest normally, and store the response in the idempotency table (Look + * into DynamoDB)</li> + * <li>Second call (and next ones) will retrieve from the cache (if cache is enabled, which is by default) or from + * the store, the handler won't be called. Until the expiration happens (by default 1 hour).</li> * </ul> */ @Idempotent // The magic is here! @Logging(logEvent = true) + @Tracing public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); @@ -101,21 +121,22 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv } } - /** * Helper to retrieve the contents of the given URL and return them as a string. * <p> * We could also put the @Idempotent annotation here if we only wanted this sub-operation to be idempotent. Putting * it on the handler, however, reduces total execution time and saves us time! * - * @param address The URL to fetch + * @param address + * The URL to fetch * @return The contents of the given URL * @throws IOException */ + @Tracing private String getPageContents(String address) throws IOException { URL url = new URL(address); try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { return br.lines().collect(Collectors.joining(System.lineSeparator())); } } -} \ No newline at end of file +} diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..b60cce0ea --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,30 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..e97baa294 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,44 @@ +[ + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields":[{"name":"logger"}] + }, + { + "name":"java.lang.Void", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] + }, + { + "name":"jdk.internal.module.IllegalAccessLogger", + "fields":[{"name":"logger"}] + }, + { + "name":"sun.misc.Unsafe", + "fields":[{"name":"theUnsafe"}] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json new file mode 100644 index 000000000..d01f93780 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json @@ -0,0 +1,11 @@ +[ + { + "name": "helloworld.App", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/log4j2.xml b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/log4j2.xml new file mode 100644 index 000000000..5dede7b58 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender" /> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender" /> + </Root> + </Loggers> +</Configuration> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/template.yaml b/examples/powertools-examples-idempotency/sam-graalvm/template.yaml new file mode 100644 index 000000000..5ffd70255 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam-graalvm/template.yaml @@ -0,0 +1,60 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: > + Idempotency demo with GraalVM + +Globals: + Function: + Timeout: 20 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + +Resources: + IdempotencyTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: id + AttributeType: S + KeySchema: + - AttributeName: id + KeyType: HASH + TimeToLiveSpecification: + AttributeName: expiration + Enabled: true + BillingMode: PAY_PER_REQUEST + + IdempotencyFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Runtime: provided.al2023 + MemorySize: 512 + Tracing: Active + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref IdempotencyTable + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: idempotency + IDEMPOTENCY_TABLE: !Ref IdempotencyTable + Events: + HelloWorld: + Type: Api + Properties: + Path: /helloidem + Method: post + +Outputs: + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Idempotent function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/helloidem/" + HelloWorldFunction: + Description: "Idempotent Lambda Function ARN" + Value: !GetAtt IdempotencyFunction.Arn diff --git a/examples/powertools-examples-idempotency/README.md b/examples/powertools-examples-idempotency/sam/README.md similarity index 100% rename from examples/powertools-examples-idempotency/README.md rename to examples/powertools-examples-idempotency/sam/README.md diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml new file mode 100644 index 000000000..22d6a9c81 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -0,0 +1,144 @@ +<!-- + ~ Copyright 2023 Amazon.com, Inc. or its affiliates. + ~ Licensed under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-idempotency</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-dynamodb</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-core</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-idempotency/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/sam/src/main/java/helloworld/App.java new file mode 100644 index 000000000..ebe9fac22 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam/src/main/java/helloworld/App.java @@ -0,0 +1,142 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package helloworld; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +import software.amazon.lambda.powertools.idempotency.Idempotent; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(App.class); + + public App() { + this(null); + } + + public App(DynamoDbClient client) { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).address") + .withResponseHook((responseData, dataRecord) -> { + if (responseData instanceof APIGatewayProxyResponseEvent) { + APIGatewayProxyResponseEvent proxyResponse = (APIGatewayProxyResponseEvent) responseData; + final Map<String, String> headers = new HashMap<>(); + headers.putAll(proxyResponse.getHeaders()); + headers.put("x-idempotency-response", "true"); + headers.put("x-idempotency-expiration", + String.valueOf(dataRecord.getExpiryTimestamp())); + + proxyResponse.setHeaders(headers); + + return proxyResponse; + } + + return responseData; + }) + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withDynamoDbClient(client) + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build()) + .configure(); + } + + /** + * This is your Lambda event handler. It accepts HTTP POST requests from API gateway and returns the contents of the + * given URL. Requests are made idempotent + * by the idempotency library, and results are cached for the default 1h expiry time. + * <p> + * You can test the endpoint like this: + * + * <pre> + * curl -X POST https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/helloidem/ -H "Content-Type: application/json" -d '{"address": "https://checkip.amazonaws.com"}' + * </pre> + * <ul> + * <li>First call will execute the handleRequest normally, and store the response in the idempotency table (Look + * into DynamoDB)</li> + * <li>Second call (and next ones) will retrieve from the cache (if cache is enabled, which is by default) or from + * the store, the handler won't be called. Until the expiration happens (by default 1 hour).</li> + * </ul> + */ + @Idempotent // The magic is here! + @Logging(logEvent = true) + @Tracing + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map<String, String> headers = new HashMap<>(); + + headers.put("Content-Type", "application/json"); + headers.put("Access-Control-Allow-Origin", "*"); + headers.put("Access-Control-Allow-Methods", "GET, OPTIONS"); + headers.put("Access-Control-Allow-Headers", "*"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + // Read the 'address' field from the JSON post body + String address = JsonConfig.get().getObjectMapper().readTree(input.getBody()).get("address").asText(); + final String pageContents = this.getPageContents(address); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + log.info("ip is {}", pageContents); + return response + .withStatusCode(200) + .withBody(output); + + } catch (IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + /** + * Helper to retrieve the contents of the given URL and return them as a string. + * <p> + * We could also put the @Idempotent annotation here if we only wanted this sub-operation to be idempotent. Putting + * it on the handler, however, reduces total execution time and saves us time! + * + * @param address + * The URL to fetch + * @return The contents of the given URL + * @throws IOException + */ + @Tracing + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-idempotency/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-idempotency/sam/src/main/resources/log4j2.xml new file mode 100644 index 000000000..5dede7b58 --- /dev/null +++ b/examples/powertools-examples-idempotency/sam/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender" /> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender" /> + </Root> + </Loggers> +</Configuration> diff --git a/examples/powertools-examples-idempotency/template.yaml b/examples/powertools-examples-idempotency/sam/template.yaml similarity index 100% rename from examples/powertools-examples-idempotency/template.yaml rename to examples/powertools-examples-idempotency/sam/template.yaml diff --git a/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java deleted file mode 100644 index 7f097906a..000000000 --- a/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package helloworld; - -import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; -import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.amazonaws.services.lambda.runtime.tests.EventLoader; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.URI; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.BillingMode; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -public class AppTest { - private static DynamoDbClient client; - @Mock - private Context context; - private App app; - - @BeforeAll - public static void setupDynamoLocal() { - int port = getFreePort(); - try { - DynamoDBProxyServer dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[] { - "-inMemory", - "-port", - Integer.toString(port) - }); - dynamoProxy.start(); - } catch (Exception e) { - throw new RuntimeException(); - } - - client = DynamoDbClient.builder() - .httpClient(UrlConnectionHttpClient.builder().build()) - .region(Region.EU_WEST_1) - .endpointOverride(URI.create("http://localhost:" + port)) - .credentialsProvider(StaticCredentialsProvider.create( - AwsBasicCredentials.create("FAKE", "FAKE"))) - .build(); - - client.createTable(CreateTableRequest.builder() - .tableName("idempotency") - .keySchema(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("id").build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S).build() - ) - .billingMode(BillingMode.PAY_PER_REQUEST) - .build()); - } - - private static int getFreePort() { - try { - ServerSocket socket = new ServerSocket(0); - int port = socket.getLocalPort(); - socket.close(); - return port; - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - app = new App(client); - } - - @Test - public void testApp() { - APIGatewayProxyResponseEvent response = - app.handleRequest(EventLoader.loadApiGatewayRestEvent("event.json"), context); - Assertions.assertNotNull(response); - Assertions.assertTrue(response.getBody().contains("hello world")); - } -} diff --git a/examples/powertools-examples-kafka/README.md b/examples/powertools-examples-kafka/README.md new file mode 100644 index 000000000..76cd81cb9 --- /dev/null +++ b/examples/powertools-examples-kafka/README.md @@ -0,0 +1,77 @@ +# Powertools for AWS Lambda (Java) - Kafka Example + +This project demonstrates how to use Powertools for AWS Lambda (Java) to deserialize Kafka Lambda events directly into strongly typed Kafka ConsumerRecords<K, V> using different serialization formats. + +## Overview + +The example showcases automatic deserialization of Kafka Lambda events into ConsumerRecords using three formats: +- JSON - Using standard JSON serialization +- Avro - Using Apache Avro schema-based serialization +- Protobuf - Using Google Protocol Buffers serialization + +Each format has its own Lambda function handler that demonstrates how to use the `@Deserialization` annotation with the appropriate `DeserializationType`, eliminating the need to handle complex deserialization logic manually. + +## Build and Deploy + +### Prerequisites +- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Java 11+ +- Maven + +### Build + +```bash +# Build the application +sam build +``` + +### Deploy + +```bash +# Deploy the application to AWS +sam deploy --guided +``` + +During the guided deployment, you'll be prompted to provide values for required parameters. After deployment, SAM will output the ARNs of the deployed Lambda functions. + +### Build with Different Serialization Formats + +The project includes Maven profiles to build with different serialization formats: + +```bash +# Build with JSON only (no Avro or Protobuf) +mvn clean package -P base + +# Build with Avro only +mvn clean package -P avro-only + +# Build with Protobuf only +mvn clean package -P protobuf-only + +# Build with all formats (default) +mvn clean package -P full +``` + +## Testing + +The `events` directory contains sample events for each serialization format: +- `kafka-json-event.json` - Sample event with JSON-serialized products +- `kafka-avro-event.json` - Sample event with Avro-serialized products +- `kafka-protobuf-event.json` - Sample event with Protobuf-serialized products + +You can use these events to test the Lambda functions: + +```bash +# Test the JSON deserialization function +sam local invoke JsonDeserializationFunction --event events/kafka-json-event.json + +# Test the Avro deserialization function +sam local invoke AvroDeserializationFunction --event events/kafka-avro-event.json + +# Test the Protobuf deserialization function +sam local invoke ProtobufDeserializationFunction --event events/kafka-protobuf-event.json +``` + +## Sample Generator Tool + +The project includes a tool to generate sample JSON, Avro, and Protobuf serialized data. See the [tools/README.md](tools/README.md) for more information. \ No newline at end of file diff --git a/examples/powertools-examples-kafka/events/kafka-avro-event.json b/examples/powertools-examples-kafka/events/kafka-avro-event.json new file mode 100644 index 000000000..8d6ef2210 --- /dev/null +++ b/examples/powertools-examples-kafka/events/kafka-avro-event.json @@ -0,0 +1,51 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4", + "bootstrapServers": "b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092", + "records": { + "mytopic-0": [ + { + "topic": "mytopic", + "partition": 0, + "offset": 15, + "timestamp": 1545084650987, + "timestampType": "CREATE_TIME", + "key": "NDI=", + "value": "0g8MTGFwdG9wUrgehes/j0A=", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + }, + { + "topic": "mytopic", + "partition": 0, + "offset": 16, + "timestamp": 1545084650988, + "timestampType": "CREATE_TIME", + "key": "NDI=", + "value": "1A8UU21hcnRwaG9uZVK4HoXrv4JA", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + }, + { + "topic": "mytopic", + "partition": 0, + "offset": 17, + "timestamp": 1545084650989, + "timestampType": "CREATE_TIME", + "key": null, + "value": "1g8USGVhZHBob25lc0jhehSuv2JA", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + } + ] + } +} diff --git a/examples/powertools-examples-kafka/events/kafka-json-event.json b/examples/powertools-examples-kafka/events/kafka-json-event.json new file mode 100644 index 000000000..7ffb9a3a6 --- /dev/null +++ b/examples/powertools-examples-kafka/events/kafka-json-event.json @@ -0,0 +1,51 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4", + "bootstrapServers": "b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092", + "records": { + "mytopic-0": [ + { + "topic": "mytopic", + "partition": 0, + "offset": 15, + "timestamp": 1545084650987, + "timestampType": "CREATE_TIME", + "key": "NDI=", + "value": "eyJwcmljZSI6OTk5Ljk5LCJuYW1lIjoiTGFwdG9wIiwiaWQiOjEwMDF9", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + }, + { + "topic": "mytopic", + "partition": 0, + "offset": 15, + "timestamp": 1545084650987, + "timestampType": "CREATE_TIME", + "key": "NDI=", + "value": "eyJwcmljZSI6NTk5Ljk5LCJuYW1lIjoiU21hcnRwaG9uZSIsImlkIjoxMDAyfQ==", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + }, + { + "topic": "mytopic", + "partition": 0, + "offset": 15, + "timestamp": 1545084650987, + "timestampType": "CREATE_TIME", + "key": null, + "value": "eyJwcmljZSI6MTQ5Ljk5LCJuYW1lIjoiSGVhZHBob25lcyIsImlkIjoxMDAzfQ==", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + } + ] + } +} diff --git a/examples/powertools-examples-kafka/events/kafka-protobuf-event.json b/examples/powertools-examples-kafka/events/kafka-protobuf-event.json new file mode 100644 index 000000000..6f5ec58ae --- /dev/null +++ b/examples/powertools-examples-kafka/events/kafka-protobuf-event.json @@ -0,0 +1,77 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4", + "bootstrapServers": "b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092", + "records": { + "mytopic-0": [ + { + "topic": "mytopic", + "partition": 0, + "offset": 15, + "timestamp": 1545084650987, + "timestampType": "CREATE_TIME", + "key": "NDI=", + "value": "COkHEgZMYXB0b3AZUrgehes/j0A=", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + }, + { + "topic": "mytopic", + "partition": 0, + "offset": 16, + "timestamp": 1545084650988, + "timestampType": "CREATE_TIME", + "key": "NDI=", + "value": "AAjpBxIGTGFwdG9wGVK4HoXrP49A", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ], + "valueSchemaMetadata": { + "schemaId": "123", + "dataFormat": "PROTOBUF" + } + }, + { + "topic": "mytopic", + "partition": 0, + "offset": 17, + "timestamp": 1545084650989, + "timestampType": "CREATE_TIME", + "key": null, + "value": "BAIACOkHEgZMYXB0b3AZUrgehes/j0A=", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ], + "valueSchemaMetadata": { + "schemaId": "456", + "dataFormat": "PROTOBUF" + } + }, + { + "topic": "mytopic", + "partition": 0, + "offset": 18, + "timestamp": 1545084650990, + "timestampType": "CREATE_TIME", + "key": "NDI=", + "value": "AQjpBxIGTGFwdG9wGVK4HoXrP49A", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ], + "valueSchemaMetadata": { + "schemaId": "12345678-1234-1234-1234-123456789012", + "dataFormat": "PROTOBUF" + } + } + ] + } +} diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml new file mode 100644 index 000000000..d152f46c0 --- /dev/null +++ b/examples/powertools-examples-kafka/pom.xml @@ -0,0 +1,232 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-kafka</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + <avro.version>1.12.1</avro.version> + <protobuf.version>4.33.1</protobuf.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-kafka</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.kafka</groupId> + <artifactId>kafka-clients</artifactId> + <version>4.1.1</version> <!-- Supports >= 3.0.0 --> + </dependency> + <dependency> + <groupId>org.apache.avro</groupId> + <artifactId>avro</artifactId> + <version>${avro.version}</version> + </dependency> + <dependency> + <groupId>com.google.protobuf</groupId> + <artifactId>protobuf-java</artifactId> + <version>${protobuf.version}</version> + </dependency> + + <!-- Basic logging setup --> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <!-- Generate Avro classes from schema --> + <plugin> + <groupId>org.apache.avro</groupId> + <artifactId>avro-maven-plugin</artifactId> + <version>${avro.version}</version> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>schema</goal> + </goals> + <configuration> + <sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory> + <outputDirectory>${project.basedir}/src/main/java/</outputDirectory> + <stringType>String</stringType> + </configuration> + </execution> + </executions> + </plugin> + <!-- Generate Protobuf classes from schema --> + <plugin> + <groupId>io.github.ascopes</groupId> + <artifactId>protobuf-maven-plugin</artifactId> + <version>3.10.3</version> + <executions> + <execution> + <goals> + <goal>generate</goal> + </goals> + <phase>generate-sources</phase> + <configuration> + <protocVersion>${protobuf.version}</protocVersion> + <sourceDirectories> + <sourceDirectory>${project.basedir}/src/main/proto</sourceDirectory> + </sourceDirectories> + <outputDirectory>${project.basedir}/src/main/java</outputDirectory> + <clearOutputDirectory>false</clearOutputDirectory> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <profiles> + <!-- Base profile without Avro or Protobuf (compatible with JSON only) --> + <profile> + <id>base</id> + <properties> + <active.profile>base</active.profile> + </properties> + <dependencies> + <!-- Exclude both Avro and Protobuf --> + <dependency> + <groupId>org.apache.avro</groupId> + <artifactId>avro</artifactId> + <version>${avro.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.google.protobuf</groupId> + <artifactId>protobuf-java</artifactId> + <version>${protobuf.version}</version> + <scope>provided</scope> + </dependency> + </dependencies> + </profile> + + <!-- Profile with only Avro --> + <profile> + <id>avro-only</id> + <properties> + <active.profile>avro-only</active.profile> + </properties> + <dependencies> + <dependency> + <groupId>com.google.protobuf</groupId> + <artifactId>protobuf-java</artifactId> + <version>${protobuf.version}</version> + <scope>provided</scope> + </dependency> + </dependencies> + </profile> + + <!-- Profile with only Protobuf --> + <profile> + <id>protobuf-only</id> + <properties> + <active.profile>protobuf-only</active.profile> + </properties> + <dependencies> + <dependency> + <groupId>org.apache.avro</groupId> + <artifactId>avro</artifactId> + <version>${avro.version}</version> + <scope>provided</scope> + </dependency> + </dependencies> + </profile> + + <!-- Profile with both Avro and Protobuf (default) --> + <profile> + <id>full</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <properties> + <active.profile>full</active.profile> + </properties> + </profile> + </profiles> +</project> diff --git a/examples/powertools-examples-kafka/src/main/avro/AvroProduct.avsc b/examples/powertools-examples-kafka/src/main/avro/AvroProduct.avsc new file mode 100644 index 000000000..7155857ea --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/avro/AvroProduct.avsc @@ -0,0 +1,10 @@ +{ + "namespace": "org.demo.kafka.avro", + "type": "record", + "name": "AvroProduct", + "fields": [ + {"name": "id", "type": "int"}, + {"name": "name", "type": "string"}, + {"name": "price", "type": "double"} + ] +} diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/AvroDeserializationFunction.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/AvroDeserializationFunction.java new file mode 100644 index 000000000..72f383eef --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/AvroDeserializationFunction.java @@ -0,0 +1,37 @@ +package org.demo.kafka; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.demo.kafka.avro.AvroProduct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; +import software.amazon.lambda.powertools.logging.Logging; + +public class AvroDeserializationFunction implements RequestHandler<ConsumerRecords<String, AvroProduct>, String> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AvroDeserializationFunction.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_AVRO) + public String handleRequest(ConsumerRecords<String, AvroProduct> records, Context context) { + for (ConsumerRecord<String, AvroProduct> consumerRecord : records) { + LOGGER.info("ConsumerRecord: {}", consumerRecord); + + AvroProduct product = consumerRecord.value(); + LOGGER.info("AvroProduct: {}", product); + + String key = consumerRecord.key(); + LOGGER.info("Key: {}", key); + } + + return "OK"; + } + +} diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/JsonDeserializationFunction.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/JsonDeserializationFunction.java new file mode 100644 index 000000000..c1d7f13ae --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/JsonDeserializationFunction.java @@ -0,0 +1,35 @@ +package org.demo.kafka; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; +import software.amazon.lambda.powertools.logging.Logging; + +public class JsonDeserializationFunction implements RequestHandler<ConsumerRecords<String, Product>, String> { + + private static final Logger LOGGER = LoggerFactory.getLogger(JsonDeserializationFunction.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, Product> consumerRecords, Context context) { + for (ConsumerRecord<String, Product> consumerRecord : consumerRecords) { + LOGGER.info("ConsumerRecord: {}", consumerRecord); + + Product product = consumerRecord.value(); + LOGGER.info("Product: {}", product); + + String key = consumerRecord.key(); + LOGGER.info("Key: {}", key); + } + + return "OK"; + } +} diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/Product.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/Product.java new file mode 100644 index 000000000..c6166090c --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/Product.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.demo.kafka; + +public class Product { + private long id; + private String name; + private double price; + + public Product() { + } + + public Product(long id, String name, double price) { + this.id = id; + this.name = name; + this.price = price; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + @Override + public String toString() { + return "Product{" + + "id=" + id + + ", name='" + name + '\'' + + ", price=" + price + + '}'; + } +} diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/ProtobufDeserializationFunction.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/ProtobufDeserializationFunction.java new file mode 100644 index 000000000..1978e8890 --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/ProtobufDeserializationFunction.java @@ -0,0 +1,38 @@ +package org.demo.kafka; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.demo.kafka.protobuf.ProtobufProduct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; +import software.amazon.lambda.powertools.logging.Logging; + +public class ProtobufDeserializationFunction + implements RequestHandler<ConsumerRecords<String, ProtobufProduct>, String> { + + private static final Logger LOGGER = LoggerFactory.getLogger(ProtobufDeserializationFunction.class); + + @Override + @Logging + @Deserialization(type = DeserializationType.KAFKA_PROTOBUF) + public String handleRequest(ConsumerRecords<String, ProtobufProduct> records, Context context) { + for (ConsumerRecord<String, ProtobufProduct> consumerRecord : records) { + LOGGER.info("ConsumerRecord: {}", consumerRecord); + + ProtobufProduct product = consumerRecord.value(); + LOGGER.info("ProtobufProduct: {}", product); + + String key = consumerRecord.key(); + LOGGER.info("Key: {}", key); + } + + return "OK"; + } + +} diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/avro/AvroProduct.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/avro/AvroProduct.java new file mode 100644 index 000000000..fad7e2fbf --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/avro/AvroProduct.java @@ -0,0 +1,476 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package org.demo.kafka.avro; + +import org.apache.avro.specific.SpecificData; +import org.apache.avro.util.Utf8; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@org.apache.avro.specific.AvroGenerated +public class AvroProduct extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = -2929699301240218341L; + + + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"AvroProduct\",\"namespace\":\"org.demo.kafka.avro\",\"fields\":[{\"name\":\"id\",\"type\":\"int\"},{\"name\":\"name\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"}},{\"name\":\"price\",\"type\":\"double\"}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static final SpecificData MODEL$ = new SpecificData(); + + private static final BinaryMessageEncoder<AvroProduct> ENCODER = + new BinaryMessageEncoder<>(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder<AvroProduct> DECODER = + new BinaryMessageDecoder<>(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageEncoder instance used by this class. + * @return the message encoder used by this class + */ + public static BinaryMessageEncoder<AvroProduct> getEncoder() { + return ENCODER; + } + + /** + * Return the BinaryMessageDecoder instance used by this class. + * @return the message decoder used by this class + */ + public static BinaryMessageDecoder<AvroProduct> getDecoder() { + return DECODER; + } + + /** + * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. + * @param resolver a {@link SchemaStore} used to find schemas by fingerprint + * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore + */ + public static BinaryMessageDecoder<AvroProduct> createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder<>(MODEL$, SCHEMA$, resolver); + } + + /** + * Serializes this AvroProduct to a ByteBuffer. + * @return a buffer holding the serialized data for this instance + * @throws java.io.IOException if this instance could not be serialized + */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** + * Deserializes a AvroProduct from a ByteBuffer. + * @param b a byte buffer holding serialized data for an instance of this class + * @return a AvroProduct instance decoded from the given buffer + * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class + */ + public static AvroProduct fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + private int id; + private java.lang.String name; + private double price; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use <code>newBuilder()</code>. + */ + public AvroProduct() {} + + /** + * All-args constructor. + * @param id The new value for id + * @param name The new value for name + * @param price The new value for price + */ + public AvroProduct(java.lang.Integer id, java.lang.String name, java.lang.Double price) { + this.id = id; + this.name = name; + this.price = price; + } + + @Override + public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; } + + @Override + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + + // Used by DatumWriter. Applications should not call. + @Override + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return id; + case 1: return name; + case 2: return price; + default: throw new IndexOutOfBoundsException("Invalid index: " + field$); + } + } + + // Used by DatumReader. Applications should not call. + @Override + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: id = (java.lang.Integer)value$; break; + case 1: name = value$ != null ? value$.toString() : null; break; + case 2: price = (java.lang.Double)value$; break; + default: throw new IndexOutOfBoundsException("Invalid index: " + field$); + } + } + + /** + * Gets the value of the 'id' field. + * @return The value of the 'id' field. + */ + public int getId() { + return id; + } + + + /** + * Sets the value of the 'id' field. + * @param value the value to set. + */ + public void setId(int value) { + this.id = value; + } + + /** + * Gets the value of the 'name' field. + * @return The value of the 'name' field. + */ + public java.lang.String getName() { + return name; + } + + + /** + * Sets the value of the 'name' field. + * @param value the value to set. + */ + public void setName(java.lang.String value) { + this.name = value; + } + + /** + * Gets the value of the 'price' field. + * @return The value of the 'price' field. + */ + public double getPrice() { + return price; + } + + + /** + * Sets the value of the 'price' field. + * @param value the value to set. + */ + public void setPrice(double value) { + this.price = value; + } + + /** + * Creates a new AvroProduct RecordBuilder. + * @return A new AvroProduct RecordBuilder + */ + public static org.demo.kafka.avro.AvroProduct.Builder newBuilder() { + return new org.demo.kafka.avro.AvroProduct.Builder(); + } + + /** + * Creates a new AvroProduct RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new AvroProduct RecordBuilder + */ + public static org.demo.kafka.avro.AvroProduct.Builder newBuilder(org.demo.kafka.avro.AvroProduct.Builder other) { + if (other == null) { + return new org.demo.kafka.avro.AvroProduct.Builder(); + } else { + return new org.demo.kafka.avro.AvroProduct.Builder(other); + } + } + + /** + * Creates a new AvroProduct RecordBuilder by copying an existing AvroProduct instance. + * @param other The existing instance to copy. + * @return A new AvroProduct RecordBuilder + */ + public static org.demo.kafka.avro.AvroProduct.Builder newBuilder(org.demo.kafka.avro.AvroProduct other) { + if (other == null) { + return new org.demo.kafka.avro.AvroProduct.Builder(); + } else { + return new org.demo.kafka.avro.AvroProduct.Builder(other); + } + } + + /** + * RecordBuilder for AvroProduct instances. + */ + @org.apache.avro.specific.AvroGenerated + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<AvroProduct> + implements org.apache.avro.data.RecordBuilder<AvroProduct> { + + private int id; + private java.lang.String name; + private double price; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$, MODEL$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(org.demo.kafka.avro.AvroProduct.Builder other) { + super(other); + if (isValidValue(fields()[0], other.id)) { + this.id = data().deepCopy(fields()[0].schema(), other.id); + fieldSetFlags()[0] = other.fieldSetFlags()[0]; + } + if (isValidValue(fields()[1], other.name)) { + this.name = data().deepCopy(fields()[1].schema(), other.name); + fieldSetFlags()[1] = other.fieldSetFlags()[1]; + } + if (isValidValue(fields()[2], other.price)) { + this.price = data().deepCopy(fields()[2].schema(), other.price); + fieldSetFlags()[2] = other.fieldSetFlags()[2]; + } + } + + /** + * Creates a Builder by copying an existing AvroProduct instance + * @param other The existing instance to copy. + */ + private Builder(org.demo.kafka.avro.AvroProduct other) { + super(SCHEMA$, MODEL$); + if (isValidValue(fields()[0], other.id)) { + this.id = data().deepCopy(fields()[0].schema(), other.id); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.name)) { + this.name = data().deepCopy(fields()[1].schema(), other.name); + fieldSetFlags()[1] = true; + } + if (isValidValue(fields()[2], other.price)) { + this.price = data().deepCopy(fields()[2].schema(), other.price); + fieldSetFlags()[2] = true; + } + } + + /** + * Gets the value of the 'id' field. + * @return The value. + */ + public int getId() { + return id; + } + + + /** + * Sets the value of the 'id' field. + * @param value The value of 'id'. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder setId(int value) { + validate(fields()[0], value); + this.id = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'id' field has been set. + * @return True if the 'id' field has been set, false otherwise. + */ + public boolean hasId() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'id' field. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder clearId() { + fieldSetFlags()[0] = false; + return this; + } + + /** + * Gets the value of the 'name' field. + * @return The value. + */ + public java.lang.String getName() { + return name; + } + + + /** + * Sets the value of the 'name' field. + * @param value The value of 'name'. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder setName(java.lang.String value) { + validate(fields()[1], value); + this.name = value; + fieldSetFlags()[1] = true; + return this; + } + + /** + * Checks whether the 'name' field has been set. + * @return True if the 'name' field has been set, false otherwise. + */ + public boolean hasName() { + return fieldSetFlags()[1]; + } + + + /** + * Clears the value of the 'name' field. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder clearName() { + name = null; + fieldSetFlags()[1] = false; + return this; + } + + /** + * Gets the value of the 'price' field. + * @return The value. + */ + public double getPrice() { + return price; + } + + + /** + * Sets the value of the 'price' field. + * @param value The value of 'price'. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder setPrice(double value) { + validate(fields()[2], value); + this.price = value; + fieldSetFlags()[2] = true; + return this; + } + + /** + * Checks whether the 'price' field has been set. + * @return True if the 'price' field has been set, false otherwise. + */ + public boolean hasPrice() { + return fieldSetFlags()[2]; + } + + + /** + * Clears the value of the 'price' field. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder clearPrice() { + fieldSetFlags()[2] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public AvroProduct build() { + try { + AvroProduct record = new AvroProduct(); + record.id = fieldSetFlags()[0] ? this.id : (java.lang.Integer) defaultValue(fields()[0]); + record.name = fieldSetFlags()[1] ? this.name : (java.lang.String) defaultValue(fields()[1]); + record.price = fieldSetFlags()[2] ? this.price : (java.lang.Double) defaultValue(fields()[2]); + return record; + } catch (org.apache.avro.AvroMissingFieldException e) { + throw e; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter<AvroProduct> + WRITER$ = (org.apache.avro.io.DatumWriter<AvroProduct>)MODEL$.createDatumWriter(SCHEMA$); + + @Override public void writeExternal(java.io.ObjectOutput out) + throws java.io.IOException { + WRITER$.write(this, SpecificData.getEncoder(out)); + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumReader<AvroProduct> + READER$ = (org.apache.avro.io.DatumReader<AvroProduct>)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + + @Override protected boolean hasCustomCoders() { return true; } + + @Override public void customEncode(org.apache.avro.io.Encoder out) + throws java.io.IOException + { + out.writeInt(this.id); + + out.writeString(this.name); + + out.writeDouble(this.price); + + } + + @Override public void customDecode(org.apache.avro.io.ResolvingDecoder in) + throws java.io.IOException + { + org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff(); + if (fieldOrder == null) { + this.id = in.readInt(); + + this.name = in.readString(); + + this.price = in.readDouble(); + + } else { + for (int i = 0; i < 3; i++) { + switch (fieldOrder[i].pos()) { + case 0: + this.id = in.readInt(); + break; + + case 1: + this.name = in.readString(); + break; + + case 2: + this.price = in.readDouble(); + break; + + default: + throw new java.io.IOException("Corrupt ResolvingDecoder."); + } + } + } + } +} + + + + + + + + + + diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java new file mode 100644 index 000000000..2bf5db844 --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java @@ -0,0 +1,636 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: ProtobufProduct.proto +// Protobuf Java Version: 4.33.1 + +package org.demo.kafka.protobuf; + +/** + * Protobuf type {@code org.demo.kafka.protobuf.ProtobufProduct} + */ +@com.google.protobuf.Generated +public final class ProtobufProduct extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:org.demo.kafka.protobuf.ProtobufProduct) + ProtobufProductOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 33, + /* patch= */ 1, + /* suffix= */ "", + "ProtobufProduct"); + } + // Use ProtobufProduct.newBuilder() to construct. + private ProtobufProduct(com.google.protobuf.GeneratedMessage.Builder<?> builder) { + super(builder); + } + private ProtobufProduct() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.demo.kafka.protobuf.ProtobufProduct.class, org.demo.kafka.protobuf.ProtobufProduct.Builder.class); + } + + public static final int ID_FIELD_NUMBER = 1; + private int id_ = 0; + /** + * <code>int32 id = 1;</code> + * @return The id. + */ + @java.lang.Override + public int getId() { + return id_; + } + + public static final int NAME_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + * <code>string name = 2;</code> + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + * <code>string name = 2;</code> + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PRICE_FIELD_NUMBER = 3; + private double price_ = 0D; + /** + * <code>double price = 3;</code> + * @return The price. + */ + @java.lang.Override + public double getPrice() { + return price_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (id_ != 0) { + output.writeInt32(1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, name_); + } + if (java.lang.Double.doubleToRawLongBits(price_) != 0) { + output.writeDouble(3, price_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (id_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, name_); + } + if (java.lang.Double.doubleToRawLongBits(price_) != 0) { + size += com.google.protobuf.CodedOutputStream + .computeDoubleSize(3, price_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.demo.kafka.protobuf.ProtobufProduct)) { + return super.equals(obj); + } + org.demo.kafka.protobuf.ProtobufProduct other = (org.demo.kafka.protobuf.ProtobufProduct) obj; + + if (getId() + != other.getId()) return false; + if (!getName() + .equals(other.getName())) return false; + if (java.lang.Double.doubleToLongBits(getPrice()) + != java.lang.Double.doubleToLongBits( + other.getPrice())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + PRICE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + java.lang.Double.doubleToLongBits(getPrice())); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static org.demo.kafka.protobuf.ProtobufProduct parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static org.demo.kafka.protobuf.ProtobufProduct parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.demo.kafka.protobuf.ProtobufProduct prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code org.demo.kafka.protobuf.ProtobufProduct} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:org.demo.kafka.protobuf.ProtobufProduct) + org.demo.kafka.protobuf.ProtobufProductOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.demo.kafka.protobuf.ProtobufProduct.class, org.demo.kafka.protobuf.ProtobufProduct.Builder.class); + } + + // Construct using org.demo.kafka.protobuf.ProtobufProduct.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + id_ = 0; + name_ = ""; + price_ = 0D; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct getDefaultInstanceForType() { + return org.demo.kafka.protobuf.ProtobufProduct.getDefaultInstance(); + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct build() { + org.demo.kafka.protobuf.ProtobufProduct result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct buildPartial() { + org.demo.kafka.protobuf.ProtobufProduct result = new org.demo.kafka.protobuf.ProtobufProduct(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(org.demo.kafka.protobuf.ProtobufProduct result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.name_ = name_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.price_ = price_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.demo.kafka.protobuf.ProtobufProduct) { + return mergeFrom((org.demo.kafka.protobuf.ProtobufProduct)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.demo.kafka.protobuf.ProtobufProduct other) { + if (other == org.demo.kafka.protobuf.ProtobufProduct.getDefaultInstance()) return this; + if (other.getId() != 0) { + setId(other.getId()); + } + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (java.lang.Double.doubleToRawLongBits(other.getPrice()) != 0) { + setPrice(other.getPrice()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + id_ = input.readInt32(); + bitField0_ |= 0x00000001; + break; + } // case 8 + case 18: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 25: { + price_ = input.readDouble(); + bitField0_ |= 0x00000004; + break; + } // case 25 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private int id_ ; + /** + * <code>int32 id = 1;</code> + * @return The id. + */ + @java.lang.Override + public int getId() { + return id_; + } + /** + * <code>int32 id = 1;</code> + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId(int value) { + + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * <code>int32 id = 1;</code> + * @return This builder for chaining. + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + /** + * <code>string name = 2;</code> + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * <code>string name = 2;</code> + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * <code>string name = 2;</code> + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * <code>string name = 2;</code> + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + * <code>string name = 2;</code> + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private double price_ ; + /** + * <code>double price = 3;</code> + * @return The price. + */ + @java.lang.Override + public double getPrice() { + return price_; + } + /** + * <code>double price = 3;</code> + * @param value The price to set. + * @return This builder for chaining. + */ + public Builder setPrice(double value) { + + price_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * <code>double price = 3;</code> + * @return This builder for chaining. + */ + public Builder clearPrice() { + bitField0_ = (bitField0_ & ~0x00000004); + price_ = 0D; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:org.demo.kafka.protobuf.ProtobufProduct) + } + + // @@protoc_insertion_point(class_scope:org.demo.kafka.protobuf.ProtobufProduct) + private static final org.demo.kafka.protobuf.ProtobufProduct DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.demo.kafka.protobuf.ProtobufProduct(); + } + + public static org.demo.kafka.protobuf.ProtobufProduct getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<ProtobufProduct> + PARSER = new com.google.protobuf.AbstractParser<ProtobufProduct>() { + @java.lang.Override + public ProtobufProduct parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser<ProtobufProduct> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<ProtobufProduct> getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java new file mode 100644 index 000000000..caf17ad50 --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java @@ -0,0 +1,36 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: ProtobufProduct.proto +// Protobuf Java Version: 4.33.1 + +package org.demo.kafka.protobuf; + +@com.google.protobuf.Generated +public interface ProtobufProductOrBuilder extends + // @@protoc_insertion_point(interface_extends:org.demo.kafka.protobuf.ProtobufProduct) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 id = 1;</code> + * @return The id. + */ + int getId(); + + /** + * <code>string name = 2;</code> + * @return The name. + */ + java.lang.String getName(); + /** + * <code>string name = 2;</code> + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + * <code>double price = 3;</code> + * @return The price. + */ + double getPrice(); +} diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java new file mode 100644 index 000000000..ce3214777 --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java @@ -0,0 +1,63 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: ProtobufProduct.proto +// Protobuf Java Version: 4.33.1 + +package org.demo.kafka.protobuf; + +@com.google.protobuf.Generated +public final class ProtobufProductOuterClass extends com.google.protobuf.GeneratedFile { + private ProtobufProductOuterClass() {} + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 33, + /* patch= */ 1, + /* suffix= */ "", + "ProtobufProductOuterClass"); + } + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + static final com.google.protobuf.Descriptors.Descriptor + internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\025ProtobufProduct.proto\022\027org.demo.kafka." + + "protobuf\":\n\017ProtobufProduct\022\n\n\002id\030\001 \001(\005\022" + + "\014\n\004name\030\002 \001(\t\022\r\n\005price\030\003 \001(\001B6\n\027org.demo" + + ".kafka.protobufB\031ProtobufProductOuterCla" + + "ssP\001b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor = + getDescriptor().getMessageType(0); + internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor, + new java.lang.String[] { "Id", "Name", "Price", }); + descriptor.resolveAllFeaturesImmutable(); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/examples/powertools-examples-kafka/src/main/proto/ProtobufProduct.proto b/examples/powertools-examples-kafka/src/main/proto/ProtobufProduct.proto new file mode 100644 index 000000000..4d3338a6f --- /dev/null +++ b/examples/powertools-examples-kafka/src/main/proto/ProtobufProduct.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package org.demo.kafka.protobuf; + +option java_package = "org.demo.kafka.protobuf"; +option java_outer_classname = "ProtobufProductOuterClass"; +option java_multiple_files = true; + +message ProtobufProduct { + int32 id = 1; + string name = 2; + double price = 3; +} \ No newline at end of file diff --git a/examples/powertools-examples-parameters/src/main/resources/log4j2.xml b/examples/powertools-examples-kafka/src/main/resources/log4j2.xml similarity index 84% rename from examples/powertools-examples-parameters/src/main/resources/log4j2.xml rename to examples/powertools-examples-kafka/src/main/resources/log4j2.xml index 033da8a11..fe943d707 100644 --- a/examples/powertools-examples-parameters/src/main/resources/log4j2.xml +++ b/examples/powertools-examples-kafka/src/main/resources/log4j2.xml @@ -2,7 +2,7 @@ <Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> <Appenders> <Console name="JsonAppender" target="SYSTEM_OUT"> - <LambdaJsonLayout compact="true" eventEol="true"/> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> </Console> </Appenders> <Loggers> diff --git a/examples/powertools-examples-kafka/template.yaml b/examples/powertools-examples-kafka/template.yaml new file mode 100644 index 000000000..509b13ca3 --- /dev/null +++ b/examples/powertools-examples-kafka/template.yaml @@ -0,0 +1,59 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: > + Kafka Deserialization example with Kafka Lambda ESM + +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active + +Resources: + JsonDeserializationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.kafka.JsonDeserializationFunction::handleRequest + Environment: + Variables: + JAVA_TOOL_OPTIONS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" + POWERTOOLS_LOG_LEVEL: DEBUG + POWERTOOLS_SERVICE_NAME: JsonDeserialization + POWERTOOLS_METRICS_NAMESPACE: JsonDeserializationFunction + + AvroDeserializationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.kafka.AvroDeserializationFunction::handleRequest + Environment: + Variables: + JAVA_TOOL_OPTIONS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" + POWERTOOLS_LOG_LEVEL: DEBUG + POWERTOOLS_SERVICE_NAME: AvroDeserialization + POWERTOOLS_METRICS_NAMESPACE: AvroDeserializationFunction + + ProtobufDeserializationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.kafka.ProtobufDeserializationFunction::handleRequest + Environment: + Variables: + JAVA_TOOL_OPTIONS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" + POWERTOOLS_LOG_LEVEL: DEBUG + POWERTOOLS_SERVICE_NAME: ProtobufDeserialization + POWERTOOLS_METRICS_NAMESPACE: ProtobufDeserializationFunction + +Outputs: + JsonFunction: + Description: "Kafka JSON Lambda Function ARN" + Value: !GetAtt JsonDeserializationFunction.Arn + AvroFunction: + Description: "Kafka Avro Lambda Function ARN" + Value: !GetAtt AvroDeserializationFunction.Arn + ProtobufFunction: + Description: "Kafka Protobuf Lambda Function ARN" + Value: !GetAtt ProtobufDeserializationFunction.Arn diff --git a/examples/powertools-examples-kafka/tools/README.md b/examples/powertools-examples-kafka/tools/README.md new file mode 100644 index 000000000..02e8dde9b --- /dev/null +++ b/examples/powertools-examples-kafka/tools/README.md @@ -0,0 +1,73 @@ +# Kafka Sample Generator Tool + +This tool generates base64-encoded serialized products for testing the Kafka consumer functions with different serialization formats. + +## Supported Formats + +- **JSON**: Generates base64-encoded JSON serialized products +- **Avro**: Generates base64-encoded Avro serialized products +- **Protobuf**: Generates base64-encoded Protobuf serialized products + +## Usage + +Run the following Maven commands from this directory: + +```bash +# Generate Avro and Protobuf classes from schemas +mvn generate-sources + +# Compile the code +mvn compile +``` + +### Generate JSON Samples + +```bash +# Run the JSON sample generator +mvn exec:java -Dexec.mainClass="org.demo.kafka.tools.GenerateJsonSamples" +``` + +The tool will output base64-encoded values for JSON products that can be used in `../events/kafka-json-event.json`. + +### Generate Avro Samples + +```bash +# Run the Avro sample generator +mvn exec:java -Dexec.mainClass="org.demo.kafka.tools.GenerateAvroSamples" +``` + +The tool will output base64-encoded values for Avro products that can be used in `../events/kafka-avro-event.json`. + +### Generate Protobuf Samples + +```bash +# Run the Protobuf sample generator +mvn exec:java -Dexec.mainClass="org.demo.kafka.tools.GenerateProtobufSamples" +``` + +The tool will output base64-encoded values for Protobuf products that can be used in `../events/kafka-protobuf-event.json`. This generator creates samples with and without Confluent message-indexes to test different serialization scenarios. + +## Output + +Each generator produces: + +1. Three different products (Laptop, Smartphone, Headphones) +2. An integer key (42) and one entry with a nullish key to test for edge-cases +3. A complete sample event structure that can be used directly for testing + +The Protobuf generators additionally create samples with different Confluent message-index formats: +- Standard protobuf (no message indexes) +- Simple message index (single 0 byte) +- Complex message index (length-prefixed array) + +For more information about Confluent Schema Registry serialization formats and wire format specifications, see the [Confluent documentation](https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format). + +## Example + +After generating the samples, you can copy the output into the respective event files: + +- `../events/kafka-json-event.json` for JSON samples +- `../events/kafka-avro-event.json` for Avro samples +- `../events/kafka-protobuf-event.json` for Protobuf samples + +These event files can then be used to test the Lambda functions with the appropriate deserializer. diff --git a/examples/powertools-examples-kafka/tools/pom.xml b/examples/powertools-examples-kafka/tools/pom.xml new file mode 100644 index 000000000..e6f2654d1 --- /dev/null +++ b/examples/powertools-examples-kafka/tools/pom.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>software.amazon.lambda.examples</groupId> + <artifactId>powertools-examples-kafka-tools</artifactId> + <version>2.0.0</version> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <avro.version>1.12.0</avro.version> + <protobuf.version>4.31.0</protobuf.version> + <kafka-clients.version>4.0.0</kafka-clients.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.avro</groupId> + <artifactId>avro</artifactId> + <version>${avro.version}</version> + </dependency> + <dependency> + <groupId>com.google.protobuf</groupId> + <artifactId>protobuf-java</artifactId> + <version>${protobuf.version}</version> + </dependency> + <dependency> + <groupId>org.apache.kafka</groupId> + <artifactId>kafka-clients</artifactId> + <version>${kafka-clients.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>2.19.0</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.avro</groupId> + <artifactId>avro-maven-plugin</artifactId> + <version>${avro.version}</version> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>schema</goal> + </goals> + <configuration> + <sourceDirectory>${project.basedir}/../src/main/avro/</sourceDirectory> + <outputDirectory>${project.basedir}/src/main/java/</outputDirectory> + <stringType>String</stringType> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>io.github.ascopes</groupId> + <artifactId>protobuf-maven-plugin</artifactId> + <version>3.10.2</version> + <executions> + <execution> + <goals> + <goal>generate</goal> + </goals> + <phase>generate-sources</phase> + <configuration> + <protocVersion>${protobuf.version}</protocVersion> + <sourceDirectories> + <sourceDirectory>${project.basedir}/../src/main/proto</sourceDirectory> + </sourceDirectories> + <outputDirectory>${project.basedir}/src/main/java</outputDirectory> + <clearOutputDirectory>false</clearOutputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <id>generate-json-samples</id> + <configuration> + <mainClass>org.demo.kafka.tools.GenerateJsonSamples</mainClass> + </configuration> + </execution> + <execution> + <id>generate-avro-samples</id> + <configuration> + <mainClass>org.demo.kafka.tools.GenerateAvroSamples</mainClass> + </configuration> + </execution> + <execution> + <id>generate-protobuf-samples</id> + <configuration> + <mainClass>org.demo.kafka.tools.GenerateProtobufSamples</mainClass> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/avro/AvroProduct.java b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/avro/AvroProduct.java new file mode 100644 index 000000000..fad7e2fbf --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/avro/AvroProduct.java @@ -0,0 +1,476 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package org.demo.kafka.avro; + +import org.apache.avro.specific.SpecificData; +import org.apache.avro.util.Utf8; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@org.apache.avro.specific.AvroGenerated +public class AvroProduct extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = -2929699301240218341L; + + + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"AvroProduct\",\"namespace\":\"org.demo.kafka.avro\",\"fields\":[{\"name\":\"id\",\"type\":\"int\"},{\"name\":\"name\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"}},{\"name\":\"price\",\"type\":\"double\"}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static final SpecificData MODEL$ = new SpecificData(); + + private static final BinaryMessageEncoder<AvroProduct> ENCODER = + new BinaryMessageEncoder<>(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder<AvroProduct> DECODER = + new BinaryMessageDecoder<>(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageEncoder instance used by this class. + * @return the message encoder used by this class + */ + public static BinaryMessageEncoder<AvroProduct> getEncoder() { + return ENCODER; + } + + /** + * Return the BinaryMessageDecoder instance used by this class. + * @return the message decoder used by this class + */ + public static BinaryMessageDecoder<AvroProduct> getDecoder() { + return DECODER; + } + + /** + * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. + * @param resolver a {@link SchemaStore} used to find schemas by fingerprint + * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore + */ + public static BinaryMessageDecoder<AvroProduct> createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder<>(MODEL$, SCHEMA$, resolver); + } + + /** + * Serializes this AvroProduct to a ByteBuffer. + * @return a buffer holding the serialized data for this instance + * @throws java.io.IOException if this instance could not be serialized + */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** + * Deserializes a AvroProduct from a ByteBuffer. + * @param b a byte buffer holding serialized data for an instance of this class + * @return a AvroProduct instance decoded from the given buffer + * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class + */ + public static AvroProduct fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + private int id; + private java.lang.String name; + private double price; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use <code>newBuilder()</code>. + */ + public AvroProduct() {} + + /** + * All-args constructor. + * @param id The new value for id + * @param name The new value for name + * @param price The new value for price + */ + public AvroProduct(java.lang.Integer id, java.lang.String name, java.lang.Double price) { + this.id = id; + this.name = name; + this.price = price; + } + + @Override + public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; } + + @Override + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + + // Used by DatumWriter. Applications should not call. + @Override + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return id; + case 1: return name; + case 2: return price; + default: throw new IndexOutOfBoundsException("Invalid index: " + field$); + } + } + + // Used by DatumReader. Applications should not call. + @Override + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: id = (java.lang.Integer)value$; break; + case 1: name = value$ != null ? value$.toString() : null; break; + case 2: price = (java.lang.Double)value$; break; + default: throw new IndexOutOfBoundsException("Invalid index: " + field$); + } + } + + /** + * Gets the value of the 'id' field. + * @return The value of the 'id' field. + */ + public int getId() { + return id; + } + + + /** + * Sets the value of the 'id' field. + * @param value the value to set. + */ + public void setId(int value) { + this.id = value; + } + + /** + * Gets the value of the 'name' field. + * @return The value of the 'name' field. + */ + public java.lang.String getName() { + return name; + } + + + /** + * Sets the value of the 'name' field. + * @param value the value to set. + */ + public void setName(java.lang.String value) { + this.name = value; + } + + /** + * Gets the value of the 'price' field. + * @return The value of the 'price' field. + */ + public double getPrice() { + return price; + } + + + /** + * Sets the value of the 'price' field. + * @param value the value to set. + */ + public void setPrice(double value) { + this.price = value; + } + + /** + * Creates a new AvroProduct RecordBuilder. + * @return A new AvroProduct RecordBuilder + */ + public static org.demo.kafka.avro.AvroProduct.Builder newBuilder() { + return new org.demo.kafka.avro.AvroProduct.Builder(); + } + + /** + * Creates a new AvroProduct RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new AvroProduct RecordBuilder + */ + public static org.demo.kafka.avro.AvroProduct.Builder newBuilder(org.demo.kafka.avro.AvroProduct.Builder other) { + if (other == null) { + return new org.demo.kafka.avro.AvroProduct.Builder(); + } else { + return new org.demo.kafka.avro.AvroProduct.Builder(other); + } + } + + /** + * Creates a new AvroProduct RecordBuilder by copying an existing AvroProduct instance. + * @param other The existing instance to copy. + * @return A new AvroProduct RecordBuilder + */ + public static org.demo.kafka.avro.AvroProduct.Builder newBuilder(org.demo.kafka.avro.AvroProduct other) { + if (other == null) { + return new org.demo.kafka.avro.AvroProduct.Builder(); + } else { + return new org.demo.kafka.avro.AvroProduct.Builder(other); + } + } + + /** + * RecordBuilder for AvroProduct instances. + */ + @org.apache.avro.specific.AvroGenerated + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<AvroProduct> + implements org.apache.avro.data.RecordBuilder<AvroProduct> { + + private int id; + private java.lang.String name; + private double price; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$, MODEL$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(org.demo.kafka.avro.AvroProduct.Builder other) { + super(other); + if (isValidValue(fields()[0], other.id)) { + this.id = data().deepCopy(fields()[0].schema(), other.id); + fieldSetFlags()[0] = other.fieldSetFlags()[0]; + } + if (isValidValue(fields()[1], other.name)) { + this.name = data().deepCopy(fields()[1].schema(), other.name); + fieldSetFlags()[1] = other.fieldSetFlags()[1]; + } + if (isValidValue(fields()[2], other.price)) { + this.price = data().deepCopy(fields()[2].schema(), other.price); + fieldSetFlags()[2] = other.fieldSetFlags()[2]; + } + } + + /** + * Creates a Builder by copying an existing AvroProduct instance + * @param other The existing instance to copy. + */ + private Builder(org.demo.kafka.avro.AvroProduct other) { + super(SCHEMA$, MODEL$); + if (isValidValue(fields()[0], other.id)) { + this.id = data().deepCopy(fields()[0].schema(), other.id); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.name)) { + this.name = data().deepCopy(fields()[1].schema(), other.name); + fieldSetFlags()[1] = true; + } + if (isValidValue(fields()[2], other.price)) { + this.price = data().deepCopy(fields()[2].schema(), other.price); + fieldSetFlags()[2] = true; + } + } + + /** + * Gets the value of the 'id' field. + * @return The value. + */ + public int getId() { + return id; + } + + + /** + * Sets the value of the 'id' field. + * @param value The value of 'id'. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder setId(int value) { + validate(fields()[0], value); + this.id = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'id' field has been set. + * @return True if the 'id' field has been set, false otherwise. + */ + public boolean hasId() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'id' field. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder clearId() { + fieldSetFlags()[0] = false; + return this; + } + + /** + * Gets the value of the 'name' field. + * @return The value. + */ + public java.lang.String getName() { + return name; + } + + + /** + * Sets the value of the 'name' field. + * @param value The value of 'name'. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder setName(java.lang.String value) { + validate(fields()[1], value); + this.name = value; + fieldSetFlags()[1] = true; + return this; + } + + /** + * Checks whether the 'name' field has been set. + * @return True if the 'name' field has been set, false otherwise. + */ + public boolean hasName() { + return fieldSetFlags()[1]; + } + + + /** + * Clears the value of the 'name' field. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder clearName() { + name = null; + fieldSetFlags()[1] = false; + return this; + } + + /** + * Gets the value of the 'price' field. + * @return The value. + */ + public double getPrice() { + return price; + } + + + /** + * Sets the value of the 'price' field. + * @param value The value of 'price'. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder setPrice(double value) { + validate(fields()[2], value); + this.price = value; + fieldSetFlags()[2] = true; + return this; + } + + /** + * Checks whether the 'price' field has been set. + * @return True if the 'price' field has been set, false otherwise. + */ + public boolean hasPrice() { + return fieldSetFlags()[2]; + } + + + /** + * Clears the value of the 'price' field. + * @return This builder. + */ + public org.demo.kafka.avro.AvroProduct.Builder clearPrice() { + fieldSetFlags()[2] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public AvroProduct build() { + try { + AvroProduct record = new AvroProduct(); + record.id = fieldSetFlags()[0] ? this.id : (java.lang.Integer) defaultValue(fields()[0]); + record.name = fieldSetFlags()[1] ? this.name : (java.lang.String) defaultValue(fields()[1]); + record.price = fieldSetFlags()[2] ? this.price : (java.lang.Double) defaultValue(fields()[2]); + return record; + } catch (org.apache.avro.AvroMissingFieldException e) { + throw e; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter<AvroProduct> + WRITER$ = (org.apache.avro.io.DatumWriter<AvroProduct>)MODEL$.createDatumWriter(SCHEMA$); + + @Override public void writeExternal(java.io.ObjectOutput out) + throws java.io.IOException { + WRITER$.write(this, SpecificData.getEncoder(out)); + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumReader<AvroProduct> + READER$ = (org.apache.avro.io.DatumReader<AvroProduct>)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + + @Override protected boolean hasCustomCoders() { return true; } + + @Override public void customEncode(org.apache.avro.io.Encoder out) + throws java.io.IOException + { + out.writeInt(this.id); + + out.writeString(this.name); + + out.writeDouble(this.price); + + } + + @Override public void customDecode(org.apache.avro.io.ResolvingDecoder in) + throws java.io.IOException + { + org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff(); + if (fieldOrder == null) { + this.id = in.readInt(); + + this.name = in.readString(); + + this.price = in.readDouble(); + + } else { + for (int i = 0; i < 3; i++) { + switch (fieldOrder[i].pos()) { + case 0: + this.id = in.readInt(); + break; + + case 1: + this.name = in.readString(); + break; + + case 2: + this.price = in.readDouble(); + break; + + default: + throw new java.io.IOException("Corrupt ResolvingDecoder."); + } + } + } + } +} + + + + + + + + + + diff --git a/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java new file mode 100644 index 000000000..6da9113fc --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java @@ -0,0 +1,636 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: ProtobufProduct.proto +// Protobuf Java Version: 4.31.0 + +package org.demo.kafka.protobuf; + +/** + * Protobuf type {@code org.demo.kafka.protobuf.ProtobufProduct} + */ +@com.google.protobuf.Generated +public final class ProtobufProduct extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:org.demo.kafka.protobuf.ProtobufProduct) + ProtobufProductOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 0, + /* suffix= */ "", + ProtobufProduct.class.getName()); + } + // Use ProtobufProduct.newBuilder() to construct. + private ProtobufProduct(com.google.protobuf.GeneratedMessage.Builder<?> builder) { + super(builder); + } + private ProtobufProduct() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.demo.kafka.protobuf.ProtobufProduct.class, org.demo.kafka.protobuf.ProtobufProduct.Builder.class); + } + + public static final int ID_FIELD_NUMBER = 1; + private int id_ = 0; + /** + * <code>int32 id = 1;</code> + * @return The id. + */ + @java.lang.Override + public int getId() { + return id_; + } + + public static final int NAME_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + * <code>string name = 2;</code> + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + * <code>string name = 2;</code> + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PRICE_FIELD_NUMBER = 3; + private double price_ = 0D; + /** + * <code>double price = 3;</code> + * @return The price. + */ + @java.lang.Override + public double getPrice() { + return price_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (id_ != 0) { + output.writeInt32(1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, name_); + } + if (java.lang.Double.doubleToRawLongBits(price_) != 0) { + output.writeDouble(3, price_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (id_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, name_); + } + if (java.lang.Double.doubleToRawLongBits(price_) != 0) { + size += com.google.protobuf.CodedOutputStream + .computeDoubleSize(3, price_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.demo.kafka.protobuf.ProtobufProduct)) { + return super.equals(obj); + } + org.demo.kafka.protobuf.ProtobufProduct other = (org.demo.kafka.protobuf.ProtobufProduct) obj; + + if (getId() + != other.getId()) return false; + if (!getName() + .equals(other.getName())) return false; + if (java.lang.Double.doubleToLongBits(getPrice()) + != java.lang.Double.doubleToLongBits( + other.getPrice())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + PRICE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + java.lang.Double.doubleToLongBits(getPrice())); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static org.demo.kafka.protobuf.ProtobufProduct parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static org.demo.kafka.protobuf.ProtobufProduct parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static org.demo.kafka.protobuf.ProtobufProduct parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.demo.kafka.protobuf.ProtobufProduct prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code org.demo.kafka.protobuf.ProtobufProduct} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:org.demo.kafka.protobuf.ProtobufProduct) + org.demo.kafka.protobuf.ProtobufProductOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.demo.kafka.protobuf.ProtobufProduct.class, org.demo.kafka.protobuf.ProtobufProduct.Builder.class); + } + + // Construct using org.demo.kafka.protobuf.ProtobufProduct.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + id_ = 0; + name_ = ""; + price_ = 0D; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.demo.kafka.protobuf.ProtobufProductOuterClass.internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct getDefaultInstanceForType() { + return org.demo.kafka.protobuf.ProtobufProduct.getDefaultInstance(); + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct build() { + org.demo.kafka.protobuf.ProtobufProduct result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct buildPartial() { + org.demo.kafka.protobuf.ProtobufProduct result = new org.demo.kafka.protobuf.ProtobufProduct(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(org.demo.kafka.protobuf.ProtobufProduct result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.name_ = name_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.price_ = price_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.demo.kafka.protobuf.ProtobufProduct) { + return mergeFrom((org.demo.kafka.protobuf.ProtobufProduct)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.demo.kafka.protobuf.ProtobufProduct other) { + if (other == org.demo.kafka.protobuf.ProtobufProduct.getDefaultInstance()) return this; + if (other.getId() != 0) { + setId(other.getId()); + } + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (java.lang.Double.doubleToRawLongBits(other.getPrice()) != 0) { + setPrice(other.getPrice()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + id_ = input.readInt32(); + bitField0_ |= 0x00000001; + break; + } // case 8 + case 18: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 25: { + price_ = input.readDouble(); + bitField0_ |= 0x00000004; + break; + } // case 25 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private int id_ ; + /** + * <code>int32 id = 1;</code> + * @return The id. + */ + @java.lang.Override + public int getId() { + return id_; + } + /** + * <code>int32 id = 1;</code> + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId(int value) { + + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * <code>int32 id = 1;</code> + * @return This builder for chaining. + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + /** + * <code>string name = 2;</code> + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * <code>string name = 2;</code> + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * <code>string name = 2;</code> + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * <code>string name = 2;</code> + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + * <code>string name = 2;</code> + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private double price_ ; + /** + * <code>double price = 3;</code> + * @return The price. + */ + @java.lang.Override + public double getPrice() { + return price_; + } + /** + * <code>double price = 3;</code> + * @param value The price to set. + * @return This builder for chaining. + */ + public Builder setPrice(double value) { + + price_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * <code>double price = 3;</code> + * @return This builder for chaining. + */ + public Builder clearPrice() { + bitField0_ = (bitField0_ & ~0x00000004); + price_ = 0D; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:org.demo.kafka.protobuf.ProtobufProduct) + } + + // @@protoc_insertion_point(class_scope:org.demo.kafka.protobuf.ProtobufProduct) + private static final org.demo.kafka.protobuf.ProtobufProduct DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.demo.kafka.protobuf.ProtobufProduct(); + } + + public static org.demo.kafka.protobuf.ProtobufProduct getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<ProtobufProduct> + PARSER = new com.google.protobuf.AbstractParser<ProtobufProduct>() { + @java.lang.Override + public ProtobufProduct parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser<ProtobufProduct> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<ProtobufProduct> getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.demo.kafka.protobuf.ProtobufProduct getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java new file mode 100644 index 000000000..9c1518db3 --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java @@ -0,0 +1,36 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: ProtobufProduct.proto +// Protobuf Java Version: 4.31.0 + +package org.demo.kafka.protobuf; + +@com.google.protobuf.Generated +public interface ProtobufProductOrBuilder extends + // @@protoc_insertion_point(interface_extends:org.demo.kafka.protobuf.ProtobufProduct) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 id = 1;</code> + * @return The id. + */ + int getId(); + + /** + * <code>string name = 2;</code> + * @return The name. + */ + java.lang.String getName(); + /** + * <code>string name = 2;</code> + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + * <code>double price = 3;</code> + * @return The price. + */ + double getPrice(); +} diff --git a/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java new file mode 100644 index 000000000..6a99f35ec --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java @@ -0,0 +1,63 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: ProtobufProduct.proto +// Protobuf Java Version: 4.31.0 + +package org.demo.kafka.protobuf; + +@com.google.protobuf.Generated +public final class ProtobufProductOuterClass { + private ProtobufProductOuterClass() {} + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 0, + /* suffix= */ "", + ProtobufProductOuterClass.class.getName()); + } + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + static final com.google.protobuf.Descriptors.Descriptor + internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\025ProtobufProduct.proto\022\027org.demo.kafka." + + "protobuf\":\n\017ProtobufProduct\022\n\n\002id\030\001 \001(\005\022" + + "\014\n\004name\030\002 \001(\t\022\r\n\005price\030\003 \001(\001B6\n\027org.demo" + + ".kafka.protobufB\031ProtobufProductOuterCla" + + "ssP\001b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_org_demo_kafka_protobuf_ProtobufProduct_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor, + new java.lang.String[] { "Id", "Name", "Price", }); + descriptor.resolveAllFeaturesImmutable(); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateAvroSamples.java b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateAvroSamples.java new file mode 100644 index 000000000..e6f4d38fd --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateAvroSamples.java @@ -0,0 +1,127 @@ +package org.demo.kafka.tools; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Base64; + +import org.apache.avro.io.BinaryEncoder; +import org.apache.avro.io.DatumWriter; +import org.apache.avro.io.EncoderFactory; +import org.apache.avro.specific.SpecificDatumWriter; +import org.demo.kafka.avro.AvroProduct; + +/** + * Utility class to generate base64-encoded Avro serialized products + * for use in test events. + */ +public final class GenerateAvroSamples { + + private GenerateAvroSamples() { + // Utility class + } + + public static void main(String[] args) throws IOException { + // Create three different products + AvroProduct product1 = new AvroProduct(1001, "Laptop", 999.99); + AvroProduct product2 = new AvroProduct(1002, "Smartphone", 599.99); + AvroProduct product3 = new AvroProduct(1003, "Headphones", 149.99); + + // Serialize and encode each product + String encodedProduct1 = serializeAndEncode(product1); + String encodedProduct2 = serializeAndEncode(product2); + String encodedProduct3 = serializeAndEncode(product3); + + // Serialize and encode an integer key + String encodedKey = serializeAndEncodeInteger(42); + + // Print the results + System.out.println("Base64 encoded Avro products for use in kafka-avro-event.json:"); + System.out.println("\nProduct 1 (with key):"); + System.out.println("key: \"" + encodedKey + "\","); + System.out.println("value: \"" + encodedProduct1 + "\","); + + System.out.println("\nProduct 2 (with key):"); + System.out.println("key: \"" + encodedKey + "\","); + System.out.println("value: \"" + encodedProduct2 + "\","); + + System.out.println("\nProduct 3 (without key):"); + System.out.println("key: null,"); + System.out.println("value: \"" + encodedProduct3 + "\","); + + // Print a sample event structure + System.out.println("\nSample event structure:"); + printSampleEvent(encodedKey, encodedProduct1, encodedProduct2, encodedProduct3); + } + + private static String serializeAndEncode(AvroProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(baos, null); + DatumWriter<AvroProduct> writer = new SpecificDatumWriter<>(AvroProduct.class); + + writer.write(product, encoder); + encoder.flush(); + + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } + + private static String serializeAndEncodeInteger(Integer value) throws IOException { + // For simple types like integers, we'll just convert to string and encode + return Base64.getEncoder().encodeToString(value.toString().getBytes()); + } + + private static void printSampleEvent(String key, String product1, String product2, String product3) { + System.out.println("{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"eventSourceArn\": \"arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4\",\n" + + + " \"bootstrapServers\": \"b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092\",\n" + + + " \"records\": {\n" + + " \"mytopic-0\": [\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + key + "\",\n" + + " \"value\": \"" + product1 + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 16,\n" + + " \"timestamp\": 1545084650988,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + key + "\",\n" + + " \"value\": \"" + product2 + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 17,\n" + + " \"timestamp\": 1545084650989,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + product3 + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"); + } +} diff --git a/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateJsonSamples.java b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateJsonSamples.java new file mode 100644 index 000000000..d0ef7cb55 --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateJsonSamples.java @@ -0,0 +1,130 @@ +package org.demo.kafka.tools; + +import java.io.IOException; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Utility class to generate base64-encoded JSON serialized products + * for use in test events. + */ +public final class GenerateJsonSamples { + + private GenerateJsonSamples() { + // Utility class + } + + public static void main(String[] args) throws IOException { + // Create three different products + Map<String, Object> product1 = new HashMap<>(); + product1.put("id", 1001); + product1.put("name", "Laptop"); + product1.put("price", 999.99); + + Map<String, Object> product2 = new HashMap<>(); + product2.put("id", 1002); + product2.put("name", "Smartphone"); + product2.put("price", 599.99); + + Map<String, Object> product3 = new HashMap<>(); + product3.put("id", 1003); + product3.put("name", "Headphones"); + product3.put("price", 149.99); + + // Serialize and encode each product + String encodedProduct1 = serializeAndEncode(product1); + String encodedProduct2 = serializeAndEncode(product2); + String encodedProduct3 = serializeAndEncode(product3); + + // Serialize and encode an integer key + String encodedKey = serializeAndEncodeInteger(42); + + // Print the results + System.out.println("Base64 encoded JSON products for use in kafka-json-event.json:"); + System.out.println("\nProduct 1 (with key):"); + System.out.println("key: \"" + encodedKey + "\","); + System.out.println("value: \"" + encodedProduct1 + "\","); + + System.out.println("\nProduct 2 (with key):"); + System.out.println("key: \"" + encodedKey + "\","); + System.out.println("value: \"" + encodedProduct2 + "\","); + + System.out.println("\nProduct 3 (without key):"); + System.out.println("key: null,"); + System.out.println("value: \"" + encodedProduct3 + "\","); + + // Print a sample event structure + System.out.println("\nSample event structure:"); + printSampleEvent(encodedKey, encodedProduct1, encodedProduct2, encodedProduct3); + } + + private static String serializeAndEncode(Map<String, Object> product) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(product); + return Base64.getEncoder().encodeToString(json.getBytes()); + } + + private static String serializeAndEncodeInteger(Integer value) { + // For simple types like integers, we'll just convert to string and encode + return Base64.getEncoder().encodeToString(value.toString().getBytes()); + } + + private static void printSampleEvent(String key, String product1, String product2, String product3) { + System.out.println("{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"eventSourceArn\": \"arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4\",\n" + + + " \"bootstrapServers\": \"b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092\",\n" + + + " \"records\": {\n" + + " \"mytopic-0\": [\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + key + "\",\n" + + " \"value\": \"" + product1 + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + key + "\",\n" + + " \"value\": \"" + product2 + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + product3 + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"); + } +} diff --git a/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateProtobufSamples.java b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateProtobufSamples.java new file mode 100644 index 000000000..aa5f6e330 --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateProtobufSamples.java @@ -0,0 +1,215 @@ +package org.demo.kafka.tools; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Base64; + +import org.apache.kafka.common.utils.ByteUtils; +import org.demo.kafka.protobuf.ProtobufProduct; + +import com.google.protobuf.CodedOutputStream; + +/** + * Utility class to generate base64-encoded Protobuf serialized products + * for use in test events. + */ +public final class GenerateProtobufSamples { + + private GenerateProtobufSamples() { + // Utility class + } + + public static void main(String[] args) throws IOException { + // Create a single product that will be used for all four scenarios + ProtobufProduct product = ProtobufProduct.newBuilder() + .setId(1001) + .setName("Laptop") + .setPrice(999.99) + .build(); + + // Create four different serializations of the same product + String standardProduct = serializeAndEncode(product); + String productWithConfluentSimpleIndex = serializeWithConfluentSimpleMessageIndex(product); + String productWithConfluentComplexIndex = serializeWithConfluentComplexMessageIndex(product); + String productWithGlueMagicByte = serializeWithGlueMagicByte(product); + + // Serialize and encode an integer key (same for all records) + String encodedKey = serializeAndEncodeInteger(42); + + // Print the results + System.out.println("Base64 encoded Protobuf products with different scenarios:"); + System.out.println("\n1. Plain Protobuf (no schema registry):"); + System.out.println("value: \"" + standardProduct + "\""); + + System.out.println("\n2. Confluent with Simple Message Index (optimized single 0):"); + System.out.println("value: \"" + productWithConfluentSimpleIndex + "\""); + + System.out.println("\n3. Confluent with Complex Message Index (array [1,0]):"); + System.out.println("value: \"" + productWithConfluentComplexIndex + "\""); + + System.out.println("\n4. Glue with Magic Byte:"); + System.out.println("value: \"" + productWithGlueMagicByte + "\""); + + // Print the merged event structure + System.out.println("\n" + "=".repeat(80)); + System.out.println("MERGED EVENT WITH ALL FOUR SCENARIOS"); + System.out.println("=".repeat(80)); + printSampleEvent(encodedKey, standardProduct, productWithConfluentSimpleIndex, productWithConfluentComplexIndex, + productWithGlueMagicByte); + } + + private static String serializeAndEncode(ProtobufProduct product) { + return Base64.getEncoder().encodeToString(product.toByteArray()); + } + + /** + * Serializes a protobuf product with a simple Confluent message index (optimized single 0). + * Format: [0][protobuf_data] + * + * @see {@link https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format} + */ + private static String serializeWithConfluentSimpleMessageIndex(ProtobufProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Write optimized simple message index for Confluent (single 0 byte for [0]) + baos.write(0); + + // Write the protobuf data + baos.write(product.toByteArray()); + + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } + + /** + * Serializes a protobuf product with a complex Confluent message index (array [1,0]). + * Format: [2][1][0][protobuf_data] where 2 is the array length using varint encoding + * + * @see {@link https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format} + */ + private static String serializeWithConfluentComplexMessageIndex(ProtobufProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Write complex message index array [1,0] using ByteUtils + ByteBuffer buffer = ByteBuffer.allocate(1024); + ByteUtils.writeVarint(2, buffer); // Array length + ByteUtils.writeVarint(1, buffer); // First index value + ByteUtils.writeVarint(0, buffer); // Second index value + + buffer.flip(); + byte[] indexData = new byte[buffer.remaining()]; + buffer.get(indexData); + baos.write(indexData); + + // Write the protobuf data + baos.write(product.toByteArray()); + + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } + + /** + * Serializes a protobuf product with Glue magic byte. + * Format: [1][protobuf_data] where 1 is the magic byte + */ + private static String serializeWithGlueMagicByte(ProtobufProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); + + // Write Glue magic byte (single UInt32) + codedOutput.writeUInt32NoTag(1); + + // Write the protobuf data + product.writeTo(codedOutput); + + codedOutput.flush(); + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } + + private static String serializeAndEncodeInteger(Integer value) { + // For simple types like integers, we'll just convert to string and encode + return Base64.getEncoder().encodeToString(value.toString().getBytes()); + } + + private static void printSampleEvent(String key, String standardProduct, String confluentSimpleProduct, + String confluentComplexProduct, String glueProduct) { + System.out.println("{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"eventSourceArn\": \"arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4\",\n" + + + " \"bootstrapServers\": \"b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092\",\n" + + + " \"records\": {\n" + + " \"mytopic-0\": [\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + key + "\",\n" + + " \"value\": \"" + standardProduct + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 16,\n" + + " \"timestamp\": 1545084650988,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + key + "\",\n" + + " \"value\": \"" + confluentSimpleProduct + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ],\n" + + " \"valueSchemaMetadata\": {\n" + + " \"schemaId\": \"123\",\n" + + " \"dataFormat\": \"PROTOBUF\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 17,\n" + + " \"timestamp\": 1545084650989,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + confluentComplexProduct + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ],\n" + + " \"valueSchemaMetadata\": {\n" + + " \"schemaId\": \"456\",\n" + + " \"dataFormat\": \"PROTOBUF\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"topic\": \"mytopic\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 18,\n" + + " \"timestamp\": 1545084650990,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + key + "\",\n" + + " \"value\": \"" + glueProduct + "\",\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + + " }\n" + + " ],\n" + + " \"valueSchemaMetadata\": {\n" + + " \"schemaId\": \"12345678-1234-1234-1234-123456789012\",\n" + + " \"dataFormat\": \"PROTOBUF\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"); + } +} diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml deleted file mode 100644 index c4631fd05..000000000 --- a/examples/powertools-examples-parameters/pom.xml +++ /dev/null @@ -1,156 +0,0 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> - <artifactId>powertools-examples-parameters</artifactId> - <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> - - <properties> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> - </dependency> - - <!-- Test dependencies --> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>5.1.1</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <version>5.9.3</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <version>5.9.3</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.1.2</version> - </plugin> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <Xlint>ignore</Xlint> - <encoding>UTF-8</encoding> - </configuration> - <executions> - <execution> - <phase>process-sources</phase> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> -</project> diff --git a/examples/powertools-examples-parameters/sam-graalvm/Dockerfile b/examples/powertools-examples-parameters/sam-graalvm/Dockerfile new file mode 100644 index 000000000..8377d6dc7 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/Dockerfile @@ -0,0 +1,14 @@ +#Use the official AWS SAM base image for Java 21 +FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461 + +#Install GraalVM dependencies +RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz +RUN mv graalvm-jdk-21.* /usr/lib/graalvm + +#Make native image and mvn available on CLI +RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image +RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn + +#Set GraalVM as default +ENV JAVA_HOME=/usr/lib/graalvm +ENV PATH=/usr/lib/graalvm/bin:$PATH diff --git a/examples/powertools-examples-parameters/sam-graalvm/Makefile b/examples/powertools-examples-parameters/sam-graalvm/Makefile new file mode 100644 index 000000000..5a3a00a69 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/Makefile @@ -0,0 +1,6 @@ +build-ParametersFunction: + mvn clean package -P native-image + chmod +x target/hello-world + cp target/hello-world $(ARTIFACTS_DIR) # (ARTIFACTS_DIR --> https://github.com/aws/aws-lambda-builders/blob/develop/aws_lambda_builders/workflows/custom_make/DESIGN.md#implementation) + chmod +x src/main/config/bootstrap + cp src/main/config/bootstrap $(ARTIFACTS_DIR) diff --git a/examples/powertools-examples-parameters/sam-graalvm/README.md b/examples/powertools-examples-parameters/sam-graalvm/README.md new file mode 100644 index 000000000..3797c778b --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/README.md @@ -0,0 +1,80 @@ +# Powertools for AWS Lambda (Java) - Parameters Example with SAM on GraalVM + +This project contains an example of Lambda function using the parameters module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://docs.powertools.aws.dev/lambda-java/utilities/parameters/). + +The example uses the [SSM Parameter Store](https://docs.powertools.aws.dev/lambda/java/utilities/parameters/#ssm-parameter-store) +and the [Secrets Manager](https://docs.powertools.aws.dev/lambda/java/utilities/parameters/#secrets-manager) to inject +runtime parameters into the application. +Have a look at [ParametersFunction.java](src/main/java/org/demo/parameters/ParametersFunction.java) for the full details. + +## Configuration + +- SAM uses [template.yaml](template.yaml) to define the application's AWS resources. + This file defines the Lambda function to be deployed as well as API Gateway for it. + +- Set the environment to use GraalVM + +```shell +export JAVA_HOME=<path to GraalVM> +``` + +## Build the sample application + +- Build the Docker image that will be used as the environment for SAM build: + +```shell +docker build --platform linux/amd64 . -t powertools-examples-parameters-sam-graalvm +``` + +- Build the SAM project using the docker image + +```shell +sam build --use-container --build-image powertools-examples-parameters-sam-graalvm +``` + +#### [Optional] Building with -SNAPSHOT versions of PowerTools + +- If you are testing the example with a -SNAPSHOT version of PowerTools, the maven build inside the docker image will fail. This is because the -SNAPSHOT version of the PowerTools library that you are working on is still not available in maven central/snapshot repository. + To get around this, follow these steps: + - Create the native image using the `docker` command below on your development machine. The native image is created in the `target` directory. + - `` docker run --platform linux/amd64 -it -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 powertools-examples-parameters-sam-graalvm mvn clean -Pnative-image package -DskipTests `` + - Edit the [`Makefile`](Makefile) remove this line + - `mvn clean package -P native-image` + - Build the SAM project using the docker image + - `sam build --use-container --build-image powertools-examples-parameters-sam-graalvm` + +## Deploy the sample application + +- SAM deploy + +```shell +sam deploy +``` + +This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting +started with SAM in [the examples directory](../../README.md) + +## Test the application + +First, hit the URL of the application. You can do this with curl or your browser: + +```bash + curl https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/params/ +``` +You will get your IP address back. The contents of the logs will be more interesting, and show you the values +of the parameters injected into the handler: + +```bash +sam logs --stack-name $MY_STACK_NAME --tail +``` + +```json +{ + ... + "thread": "main", + "level": "INFO", + "loggerName": "org.demo.parameters.ParametersFunction", + "message": "secretjsonobj=MyObject{id=23443, code='hk38543oj24kn796kp67bkb234gkj679l68'}\n", + ... +} +``` diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml new file mode 100644 index 000000000..2aaffd9d1 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -0,0 +1,203 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> + + <properties> + <log4j.version>2.25.3</log4j.version> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters-ssm</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters-secrets</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.7</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-layout-template-json</artifactId> + <version>${log4j.version}</version> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>5.18.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>5.11.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>5.13.1</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>hello-world</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <!-- required for AWS Lambda Runtime Interface Client --> + <arg>--enable-url-protocols=http</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/config/bootstrap b/examples/powertools-examples-parameters/sam-graalvm/src/main/config/bootstrap new file mode 100644 index 000000000..8e7928cd3 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/config/bootstrap @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +./hello-world $_HANDLER \ No newline at end of file diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java b/examples/powertools-examples-parameters/sam-graalvm/src/main/java/org/demo/parameters/MyObject.java similarity index 100% rename from examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java rename to examples/powertools-examples-parameters/sam-graalvm/src/main/java/org/demo/parameters/MyObject.java diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/sam-graalvm/src/main/java/org/demo/parameters/ParametersFunction.java similarity index 80% rename from examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java rename to examples/powertools-examples-parameters/sam-graalvm/src/main/java/org/demo/parameters/ParametersFunction.java index 5b691cfd9..9c3c422cf 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/java/org/demo/parameters/ParametersFunction.java @@ -29,19 +29,32 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.powertools.parameters.ParamManager; -import software.amazon.lambda.powertools.parameters.SSMProvider; -import software.amazon.lambda.powertools.parameters.SecretsProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.parameters.secrets.SecretsParam; +import software.amazon.lambda.powertools.parameters.secrets.SecretsProvider; +import software.amazon.lambda.powertools.parameters.ssm.SSMParam; +import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; public class ParametersFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger log = LogManager.getLogger(ParametersFunction.class); + private static final Logger log = LoggerFactory.getLogger(ParametersFunction.class); - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); + // Annotation-style injection from secrets manager + @SecretsParam(key = "/powertools-java/userpwd") + String secretParamInjected; - String simpleValue = ssmProvider.defaultMaxAge(30, SECONDS).get("/powertools-java/sample/simplekey"); + // Annotation-style injection from Systems Manager + @SSMParam(key = "/powertools-java/sample/simplekey") + String ssmParamInjected; + + SSMProvider ssmProvider = SSMProvider + .builder() + .build(); + SecretsProvider secretsProvider = SecretsProvider + .builder() + .build(); + + String simpleValue = ssmProvider.withMaxAge(30, SECONDS).get("/powertools-java/sample/simplekey"); String listValue = ssmProvider.withMaxAge(60, SECONDS).get("/powertools-java/sample/keylist"); MyObject jsonObj = ssmProvider.withTransformation(json).get("/powertools-java/sample/keyjson", MyObject.class); Map<String, String> allValues = ssmProvider.getMultiple("/powertools-java/sample"); diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..e97baa294 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,44 @@ +[ + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields":[{"name":"logger"}] + }, + { + "name":"java.lang.Void", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] + }, + { + "name":"jdk.internal.module.IllegalAccessLogger", + "fields":[{"name":"logger"}] + }, + { + "name":"sun.misc.Unsafe", + "fields":[{"name":"theUnsafe"}] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties new file mode 100644 index 000000000..db5ebaa55 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties @@ -0,0 +1 @@ +Args = --enable-url-protocols=http,https \ No newline at end of file diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json new file mode 100644 index 000000000..16a37f483 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "org.demo.parameters.ParametersFunction", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "org.demo.parameters.MyObject", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/examples/powertools-examples-serialization/src/main/resources/log4j2.xml b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/log4j2.xml similarity index 84% rename from examples/powertools-examples-serialization/src/main/resources/log4j2.xml rename to examples/powertools-examples-parameters/sam-graalvm/src/main/resources/log4j2.xml index 033da8a11..fe943d707 100644 --- a/examples/powertools-examples-serialization/src/main/resources/log4j2.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/log4j2.xml @@ -2,7 +2,7 @@ <Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> <Appenders> <Console name="JsonAppender" target="SYSTEM_OUT"> - <LambdaJsonLayout compact="true" eventEol="true"/> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> </Console> </Appenders> <Loggers> diff --git a/examples/powertools-examples-parameters/sam-graalvm/template.yaml b/examples/powertools-examples-parameters/sam-graalvm/template.yaml new file mode 100644 index 000000000..b4a4d6907 --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/template.yaml @@ -0,0 +1,100 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + validation demo + +Globals: + Function: + Timeout: 20 + MemorySize: 512 + Tracing: Active + + +Resources: + ParametersFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.parameters.ParametersFunction::handleRequest + Runtime: provided.al2023 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + LOG_LEVEL: INFO + Policies: + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: !Ref UserPwd + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: !Ref SecretConfig + - Statement: + - Sid: SSMGetParameterPolicy + Effect: Allow + Action: + - ssm:GetParameter + - ssm:GetParameters + - ssm:GetParametersByPath + Resource: '*' + Events: + HelloWorld: + Type: Api + Properties: + Path: /params + Method: get + + UserPwd: + Type: AWS::SecretsManager::Secret + Properties: + Name: /powertools-java/userpwd + Description: Generated secret for lambda-powertools-java powertools-parameters + module + GenerateSecretString: + SecretStringTemplate: '{"username": "test-user"}' + GenerateStringKey: password + PasswordLength: 15 + ExcludeCharacters: '"@/\' + SecretConfig: + Type: AWS::SecretsManager::Secret + Properties: + Name: /powertools-java/secretcode + Description: Json secret for lambda-powertools-java powertools-parameters module + SecretString: '{"id":23443,"code":"hk38543oj24kn796kp67bkb234gkj679l68"}' + BasicParameter: + Type: AWS::SSM::Parameter + Properties: + Name: /powertools-java/sample/simplekey + Type: String + Value: simplevalue + Description: Simple SSM Parameter for lambda-powertools-java powertools-parameters + module + ParameterList: + Type: AWS::SSM::Parameter + Properties: + Name: /powertools-java/sample/keylist + Type: StringList + Value: value1,value2,value3 + Description: SSM Parameter List for lambda-powertools-java powertools-parameters + module + JsonParameter: + Type: AWS::SSM::Parameter + Properties: + Name: /powertools-java/sample/keyjson + Type: String + Value: '{"id":23443,"code":"hk38543oj24kn796kp67bkb234gkj679l68"}' + Description: Json SSM Parameter for lambda-powertools-java powertools-parameters + module + Base64Parameter: + Type: AWS::SSM::Parameter + Properties: + Name: /powertools-java/sample/keybase64 + Type: String + Value: aGVsbG8gd29ybGQ= + Description: Base64 SSM Parameter for lambda-powertools-java powertools-parameters module + +Outputs: + ParametersApi: + Description: "API Gateway endpoint URL for Prod stage for Parameters function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/params/" + ParametersFunction: + Description: "Parameters Lambda Function ARN" + Value: !GetAtt ParametersFunction.Arn \ No newline at end of file diff --git a/examples/powertools-examples-parameters/README.md b/examples/powertools-examples-parameters/sam/README.md similarity index 96% rename from examples/powertools-examples-parameters/README.md rename to examples/powertools-examples-parameters/sam/README.md index a65307f69..661752957 100644 --- a/examples/powertools-examples-parameters/README.md +++ b/examples/powertools-examples-parameters/sam/README.md @@ -10,7 +10,7 @@ Have a look at [ParametersFunction.java](src/main/java/org/demo/parameters/Param ## Deploy the sample application This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting -started with SAM in [the examples directory](../README.md) +started with SAM in [the examples directory](../../README.md) ## Test the application diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml new file mode 100644 index 000000000..d2c3e68d2 --- /dev/null +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -0,0 +1,116 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-parameters-sam</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters-ssm</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters-secrets</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>5.18.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>5.11.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>5.13.1</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/MyObject.java b/examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/MyObject.java new file mode 100644 index 000000000..d406ae3df --- /dev/null +++ b/examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/MyObject.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.demo.parameters; + +public class MyObject { + + private long id; + private String code; + + public MyObject() { + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @Override + public String toString() { + return "MyObject{" + + "id=" + id + + ", code='" + code + '\'' + + '}'; + } +} diff --git a/examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/ParametersFunction.java new file mode 100644 index 000000000..9c3c422cf --- /dev/null +++ b/examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/ParametersFunction.java @@ -0,0 +1,109 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.demo.parameters; + +import static java.time.temporal.ChronoUnit.SECONDS; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.parameters.secrets.SecretsParam; +import software.amazon.lambda.powertools.parameters.secrets.SecretsProvider; +import software.amazon.lambda.powertools.parameters.ssm.SSMParam; +import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; + +public class ParametersFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private static final Logger log = LoggerFactory.getLogger(ParametersFunction.class); + + // Annotation-style injection from secrets manager + @SecretsParam(key = "/powertools-java/userpwd") + String secretParamInjected; + + // Annotation-style injection from Systems Manager + @SSMParam(key = "/powertools-java/sample/simplekey") + String ssmParamInjected; + + SSMProvider ssmProvider = SSMProvider + .builder() + .build(); + SecretsProvider secretsProvider = SecretsProvider + .builder() + .build(); + + String simpleValue = ssmProvider.withMaxAge(30, SECONDS).get("/powertools-java/sample/simplekey"); + String listValue = ssmProvider.withMaxAge(60, SECONDS).get("/powertools-java/sample/keylist"); + MyObject jsonObj = ssmProvider.withTransformation(json).get("/powertools-java/sample/keyjson", MyObject.class); + Map<String, String> allValues = ssmProvider.getMultiple("/powertools-java/sample"); + String b64value = ssmProvider.withTransformation(base64).get("/powertools-java/sample/keybase64"); + + Map<String, String> secretJson = + secretsProvider.withTransformation(json).get("/powertools-java/userpwd", Map.class); + MyObject secretJsonObj = secretsProvider.withMaxAge(42, SECONDS).withTransformation(json) + .get("/powertools-java/secretcode", MyObject.class); + + @Override + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + + log.info("\n=============== SSM Parameter Store ==============="); + log.info("simplevalue={}, listvalue={}, b64value={}\n", simpleValue, listValue, b64value); + log.info("jsonobj={}\n", jsonObj); + + log.info("allvalues (multiple):"); + allValues.forEach((key, value) -> log.info("- {}={}\n", key, value)); + + log.info("\n=============== Secrets Manager ==============="); + log.info("secretjson:"); + secretJson.forEach((key, value) -> log.info("- {}={}\n", key, value)); + log.info("secretjsonobj={}\n", secretJsonObj); + + Map<String, String> headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + return response + .withStatusCode(200) + .withBody(output); + } catch (IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-parameters/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-parameters/sam/src/main/resources/log4j2.xml new file mode 100644 index 000000000..fe943d707 --- /dev/null +++ b/examples/powertools-examples-parameters/sam/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender"/> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/examples/powertools-examples-parameters/template.yaml b/examples/powertools-examples-parameters/sam/template.yaml similarity index 100% rename from examples/powertools-examples-parameters/template.yaml rename to examples/powertools-examples-parameters/sam/template.yaml diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml deleted file mode 100644 index 4974842dd..000000000 --- a/examples/powertools-examples-serialization/pom.xml +++ /dev/null @@ -1,134 +0,0 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> - <artifactId>powertools-examples-serialization</artifactId> - <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> - - <properties> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-serialization</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> - </dependency> - - <!-- Test dependencies --> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>4.11.0</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <version>5.9.3</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <version>5.9.3</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.1.2</version> - </plugin> - </plugins> - </build> - - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <Xlint>ignore</Xlint> - <encoding>UTF-8</encoding> - </configuration> - <executions> - <execution> - <phase>process-sources</phase> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> -</project> diff --git a/examples/powertools-examples-serialization/sam-graalvm/Dockerfile b/examples/powertools-examples-serialization/sam-graalvm/Dockerfile new file mode 100644 index 000000000..8377d6dc7 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/Dockerfile @@ -0,0 +1,14 @@ +#Use the official AWS SAM base image for Java 21 +FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461 + +#Install GraalVM dependencies +RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz +RUN mv graalvm-jdk-21.* /usr/lib/graalvm + +#Make native image and mvn available on CLI +RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image +RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn + +#Set GraalVM as default +ENV JAVA_HOME=/usr/lib/graalvm +ENV PATH=/usr/lib/graalvm/bin:$PATH diff --git a/examples/powertools-examples-serialization/sam-graalvm/Makefile b/examples/powertools-examples-serialization/sam-graalvm/Makefile new file mode 100644 index 000000000..304b66239 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/Makefile @@ -0,0 +1,13 @@ +build-APIGatewayDeserializationFunction: + mvn clean package -P native-image + chmod +x target/hello-world + cp target/hello-world $(ARTIFACTS_DIR) # (ARTIFACTS_DIR --> https://github.com/aws/aws-lambda-builders/blob/develop/aws_lambda_builders/workflows/custom_make/DESIGN.md#implementation) + chmod +x src/main/config/bootstrap + cp src/main/config/bootstrap $(ARTIFACTS_DIR) + +build-SQSEventDeserializationFunction: + mvn clean package -P native-image + chmod +x target/hello-world + cp target/hello-world $(ARTIFACTS_DIR) # (ARTIFACTS_DIR --> https://github.com/aws/aws-lambda-builders/blob/develop/aws_lambda_builders/workflows/custom_make/DESIGN.md#implementation) + chmod +x src/main/config/bootstrap + cp src/main/config/bootstrap $(ARTIFACTS_DIR) diff --git a/examples/powertools-examples-serialization/sam-graalvm/README.md b/examples/powertools-examples-serialization/sam-graalvm/README.md new file mode 100644 index 000000000..01be98085 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/README.md @@ -0,0 +1,111 @@ +# Powertools for AWS Lambda (Java) - Serialization Example + +This project contains an example of Lambda function using the serialization utilities module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://docs.powertools.aws.dev/lambda-java/latest/utilities/serialization/). + +The project contains two `RequestHandler`s - + +* [APIGatewayRequestDeserializationFunction](src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java) - Uses the serialization library to deserialize an API Gateway request body +* [SQSEventDeserializationFunction](src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java) - Uses the serialization library to deserialize an SQS message body + +In both cases, the output of the serialized message will be printed to the function logs. The message format +in JSON looks like this: + +```json +{ + "id":1234, + "name":"product", + "price":42 +} +``` + +## Deploy the sample application + +This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting +started with SAM in [the examples directory](../../README.md) + +## Configuration + +- Set the environment to use GraalVM + +```shell +export JAVA_HOME=<path to GraalVM> +``` + +## Build the sample application + +- Build the Docker image that will be used as the environment for SAM build: + +```shell +docker build --platform linux/amd64 . -t powertools-examples-serialization-sam-graalvm +``` + +- Build the SAM project using the docker image + +```shell +sam build --use-container --build-image powertools-examples-serialization-sam-graalvm +``` + +#### [Optional] Building with -SNAPSHOT versions of PowerTools + +- If you are testing the example with a -SNAPSHOT version of PowerTools, the maven build inside the docker image will fail. This is because the -SNAPSHOT version of the PowerTools library that you are working on is still not available in maven central/snapshot repository. + To get around this, follow these steps: + - Create the native image using the `docker` command below on your development machine. The native image is created in the `target` directory. + - `` docker run --platform linux/amd64 -it -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 powertools-examples-serialization-sam-graalvm mvn clean -Pnative-image package -DskipTests `` + - Edit the [`Makefile`](Makefile) remove this line + - `mvn clean package -P native-image` + - Build the SAM project using the docker image + - `sam build --use-container --build-image powertools-examples-serialization-sam-graalvm` + + +## Test the application + +### 1. API Gateway Endpoint + +To test the HTTP endpoint, we can post a product to the test URL: + +```bash +curl -X POST https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/product/ -H "Content-Type: application/json" -d '{"id": 1234, "name": "product", "price": 42}' +``` + +The result will indicate that the handler has successfully deserialized the request body: + +``` +Received request for productId: 1234 +``` + +If we look at the logs using `sam logs --tail --stack-name $MY_STACK`, we will see the full deserialized request: + +```json +{ + ... + "level": "INFO", + "loggerName": "org.demo.serialization.APIGatewayRequestDeserializationFunction", + "message": "product=Product{id=1234, name='product', price=42.0}\n", + ... +} +``` + +### 2. SQS Queue +For the SQS handler, we have to send a request to our queue. We can either construct the Queue URL (see below), or +find it from the SQS section of the AWS console. + +```bash + aws sqs send-message --queue-url "https://sqs.[REGION].amazonaws.com/[ACCOUNT-ID]/sqs-event-deserialization-queue" --message-body '{"id": 1234, "name": "product", "price": 123}' +``` + +Here we can find the message by filtering through the logs for messages that have come back from our SQS handler: + +```bash +sam logs --tail --stack-name $MY_STACK --filter SQS +``` + +```bash + { + ... + "level": "INFO", + "loggerName": "org.demo.serialization.SQSEventDeserializationFunction", + "message": "products=[Product{id=1234, name='product', price=42.0}]\n", + ... +} + +``` diff --git a/examples/powertools-examples-serialization/events/APIGatewayEvent.json b/examples/powertools-examples-serialization/sam-graalvm/events/APIGatewayEvent.json similarity index 100% rename from examples/powertools-examples-serialization/events/APIGatewayEvent.json rename to examples/powertools-examples-serialization/sam-graalvm/events/APIGatewayEvent.json diff --git a/examples/powertools-examples-serialization/events/SQSEvent.json b/examples/powertools-examples-serialization/sam-graalvm/events/SQSEvent.json similarity index 100% rename from examples/powertools-examples-serialization/events/SQSEvent.json rename to examples/powertools-examples-serialization/sam-graalvm/events/SQSEvent.json diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml new file mode 100644 index 000000000..5077c8989 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -0,0 +1,89 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-serialization</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.7</version> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>hello-world</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <!-- required for AWS Lambda Runtime Interface Client --> + <arg>--enable-url-protocols=http</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/config/bootstrap b/examples/powertools-examples-serialization/sam-graalvm/src/main/config/bootstrap new file mode 100644 index 000000000..8e7928cd3 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/config/bootstrap @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +./hello-world $_HANDLER \ No newline at end of file diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java b/examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java similarity index 86% rename from examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java rename to examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java index e70b37959..3ca75cf4a 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java @@ -22,18 +22,21 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import java.util.HashMap; import java.util.Map; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class APIGatewayRequestDeserializationFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger LOGGER = LogManager.getLogger(APIGatewayRequestDeserializationFunction.class); - private static final Map<String, String> HEADERS = new HashMap<String, String>() {{ + private static final Logger LOGGER = LoggerFactory.getLogger(APIGatewayRequestDeserializationFunction.class); + private static final Map<String, String> HEADERS = new HashMap<String, String>() { + private static final long serialVersionUID = 7074189990115081999L; + { put("Content-Type", "application/json"); put("X-Custom-Header", "application/json"); - }}; + } + }; public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) { diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java b/examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/Product.java similarity index 100% rename from examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java rename to examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/Product.java diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java b/examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java similarity index 88% rename from examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java rename to examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java index 36dbed074..79097e19c 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java @@ -20,13 +20,13 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import java.util.List; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SQSEventDeserializationFunction implements RequestHandler<SQSEvent, String> { - private final static Logger LOGGER = LogManager.getLogger(SQSEventDeserializationFunction.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SQSEventDeserializationFunction.class); public String handleRequest(SQSEvent event, Context context) { List<Product> products = extractDataFrom(event).asListOf(Product.class); diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..7d38fc57d --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,48 @@ +[ + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields":[{"name":"logger"}] + }, + { + "name":"java.lang.Void", + "methods":[{"name":"<init>","parameterTypes":[] }] + }, + { + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] + }, + { + "name":"jdk.internal.module.IllegalAccessLogger", + "fields":[{"name":"logger"}] + }, + { + "name":"sun.misc.Unsafe", + "fields":[{"name":"theUnsafe"}] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + }, + { + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"isColdStart"},{"name":"serviceName"}] + } +] diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties new file mode 100644 index 000000000..a2249e17d --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties @@ -0,0 +1,3 @@ +Args = --enable-url-protocols=http,https \ +--initialize-at-run-time=\ + software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json new file mode 100644 index 000000000..e35ca7c4f --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json @@ -0,0 +1,29 @@ +[ + { + "name": "org.demo.serialization.APIGatewayRequestDeserializationFunction", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "org.demo.serialization.Product", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "org.demo.serialization.SQSEventDeserializationFunction", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/log4j2.xml b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/log4j2.xml new file mode 100644 index 000000000..fe943d707 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender"/> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam-graalvm/template.yaml b/examples/powertools-examples-serialization/sam-graalvm/template.yaml new file mode 100644 index 000000000..39fd22928 --- /dev/null +++ b/examples/powertools-examples-serialization/sam-graalvm/template.yaml @@ -0,0 +1,64 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + serialization utils demo + +Globals: + Function: + Timeout: 20 + MemorySize: 512 + Tracing: Active + + +Resources: + APIGatewayDeserializationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.serialization.APIGatewayRequestDeserializationFunction::handleRequest + Runtime: provided.al2023 + Events: + Product: + Type: Api + Properties: + Path: /product + Method: post + + DemoSqsQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: "sqs-event-deserialization-queue" + + SQSEventDeserializationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.serialization.SQSEventDeserializationFunction::handleRequest + Runtime: provided.al2023 + Policies: + - Statement: + - Sid: SQSSendMessageBatch + Effect: Allow + Action: + - sqs:SendMessageBatch + - sqs:SendMessage + Resource: !GetAtt DemoSqsQueue.Arn + Events: + SQSEvent: + Type: SQS + Properties: + Queue: !GetAtt DemoSqsQueue.Arn + BatchSize: 2 + MaximumBatchingWindowInSeconds: 30 + + +Outputs: + Api: + Description: "API Gateway endpoint URL for Prod stage for Serialization function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/product/" + Function: + Description: "Serialization Lambda Function ARN" + Value: !GetAtt APIGatewayDeserializationFunction.Arn + DemoSqsQueue: + Description: "ARN for SQS queue" + Value: !GetAtt DemoSqsQueue.Arn \ No newline at end of file diff --git a/examples/powertools-examples-serialization/README.md b/examples/powertools-examples-serialization/sam/README.md similarity index 97% rename from examples/powertools-examples-serialization/README.md rename to examples/powertools-examples-serialization/sam/README.md index 4e3f66eb0..247752f29 100644 --- a/examples/powertools-examples-serialization/README.md +++ b/examples/powertools-examples-serialization/sam/README.md @@ -21,7 +21,7 @@ in JSON looks like this: ## Deploy the sample application This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting -started with SAM in [the examples directory](../README.md) +started with SAM in [the examples directory](../../README.md) ## Test the application diff --git a/examples/powertools-examples-serialization/sam/events/APIGatewayEvent.json b/examples/powertools-examples-serialization/sam/events/APIGatewayEvent.json new file mode 100644 index 000000000..cb38a3429 --- /dev/null +++ b/examples/powertools-examples-serialization/sam/events/APIGatewayEvent.json @@ -0,0 +1,63 @@ +{ + "body": "{\"id\":1234, \"name\":\"product\", \"price\":42}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } + } + \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam/events/SQSEvent.json b/examples/powertools-examples-serialization/sam/events/SQSEvent.json new file mode 100644 index 000000000..9056d3ef2 --- /dev/null +++ b/examples/powertools-examples-serialization/sam/events/SQSEvent.json @@ -0,0 +1,39 @@ +{ + "Records": [ + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{ \"id\": 1234, \"name\": \"product\", \"price\": 42}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{ \"id\": 12345, \"name\": \"product5\", \"price\": 45}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + } + ] +} \ No newline at end of file diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml new file mode 100644 index 000000000..cf66c3e14 --- /dev/null +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -0,0 +1,60 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.9.0</version> + <artifactId>powertools-examples-serialization-sam</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + </properties> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-serialization</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.1</version> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java new file mode 100644 index 000000000..3ca75cf4a --- /dev/null +++ b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.demo.serialization; + +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class APIGatewayRequestDeserializationFunction + implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(APIGatewayRequestDeserializationFunction.class); + private static final Map<String, String> HEADERS = new HashMap<String, String>() { + private static final long serialVersionUID = 7074189990115081999L; + { + put("Content-Type", "application/json"); + put("X-Custom-Header", "application/json"); + } + }; + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) { + + Product product = extractDataFrom(event).as(Product.class); + LOGGER.info("\n=============== Deserialized request body: ==============="); + LOGGER.info("product={}\n", product); + + return new APIGatewayProxyResponseEvent() + .withHeaders(HEADERS) + .withStatusCode(200) + .withBody("Received request for productId: " + product.getId()); + } +} + diff --git a/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/Product.java b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/Product.java new file mode 100644 index 000000000..25bae34f6 --- /dev/null +++ b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/Product.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.demo.serialization; + +public class Product { + private long id; + private String name; + private double price; + + public Product() { + } + + public Product(long id, String name, double price) { + this.id = id; + this.name = name; + this.price = price; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + @Override + public String toString() { + return "Product{" + + "id=" + id + + ", name='" + name + '\'' + + ", price=" + price + + '}'; + } +} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java similarity index 51% rename from powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java rename to examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java index e96dc5581..79097e19c 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java +++ b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java @@ -12,18 +12,29 @@ * */ -package software.amazon.lambda.powertools.sqs.handlers; +package org.demo.serialization; + +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class SQSEventDeserializationFunction implements RequestHandler<SQSEvent, String> { -public class SqsNoDeleteMessageHandler implements RequestHandler<SQSEvent, String> { + private static final Logger LOGGER = LoggerFactory.getLogger(SQSEventDeserializationFunction.class); - @Override - @SqsLargeMessage(deletePayloads = false) - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); + public String handleRequest(SQSEvent event, Context context) { + List<Product> products = extractDataFrom(event).asListOf(Product.class); + + LOGGER.info("\n=============== Deserialized messages: ==============="); + LOGGER.info("products={}\n", products); + + return "Number of received messages: " + products.size(); } -} \ No newline at end of file +} + diff --git a/examples/powertools-examples-serialization/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-serialization/sam/src/main/resources/log4j2.xml new file mode 100644 index 000000000..fe943d707 --- /dev/null +++ b/examples/powertools-examples-serialization/sam/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender"/> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/examples/powertools-examples-serialization/template.yaml b/examples/powertools-examples-serialization/sam/template.yaml similarity index 100% rename from examples/powertools-examples-serialization/template.yaml rename to examples/powertools-examples-serialization/sam/template.yaml diff --git a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java deleted file mode 100644 index ec8cdbd33..000000000 --- a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.demo.serialization; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -class APIGatewayRequestDeserializationFunctionTest { - - @Mock - private Context context; - private APIGatewayRequestDeserializationFunction deserializationFunction; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - deserializationFunction = new APIGatewayRequestDeserializationFunction(); - } - - @Test - public void shouldReturnOkStatusWithProductId() { - String body = "{\"id\":1234, \"name\":\"product\", \"price\":42}"; - APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent().withBody(body); - - APIGatewayProxyResponseEvent response = deserializationFunction.handleRequest(request, context); - - assertEquals(200, response.getStatusCode()); - assertEquals("Received request for productId: 1234", response.getBody()); - } -} \ No newline at end of file diff --git a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java deleted file mode 100644 index b46af3052..000000000 --- a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.demo.serialization; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.util.ArrayList; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -class SQSEventDeserializationFunctionTest { - - @Mock - private Context context; - private SQSEventDeserializationFunction deserializationFunction; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - deserializationFunction = new SQSEventDeserializationFunction(); - } - - @Test - public void shouldReturnNumberOfReceivedMessages() { - SQSEvent.SQSMessage message1 = messageWithBody("{ \"id\": 1234, \"name\": \"product\", \"price\": 42}"); - SQSEvent.SQSMessage message2 = messageWithBody("{ \"id\": 12345, \"name\": \"product5\", \"price\": 45}"); - SQSEvent event = new SQSEvent(); - event.setRecords(new ArrayList<SQSEvent.SQSMessage>() {{ - add(message1); - add(message2); - }}); - - String response = deserializationFunction.handleRequest(event, context); - - assertEquals("Number of received messages: 2", response); - } - - private SQSEvent.SQSMessage messageWithBody(String body) { - SQSEvent.SQSMessage record1 = new SQSEvent.SQSMessage(); - record1.setBody(body); - return record1; - } -} \ No newline at end of file diff --git a/examples/powertools-examples-sqs/README.md b/examples/powertools-examples-sqs/README.md deleted file mode 100644 index 45f4a4a74..000000000 --- a/examples/powertools-examples-sqs/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Powertools for AWS Lambda (Java) - SQS Batch Processing Example - -This project contains an example of Lambda function using the batch processing utilities module of Powertools for AWS Lambda (Java). -For more information on this module, please refer to the [documentation](https://docs.powertools.aws.dev/lambda/java/utilities/batch/). - -The project contains two functions: - -* [SqsMessageSender](src/main/java/org/demo/sqs/SqsMessageSender.java) - Sends a set of messages to an SQS queue. -This function is triggered every 5 minutes by an EventBridge schedule rule. -* [SqsPoller](src/main/java/org/demo/sqs/SqsPoller.java) - Listens to the same queue, processing items off in batches - -The poller intentionally fails intermittently processing messages to demonstrate the replay behaviour of the batch -module: - -<details> -<summary> -<b>SqsPoller.java</b> -</summary> -[SqsPoller.java:43](src/main/java/org/demo/sqs/SqsPoller.java) - -```java - public String process(SQSMessage message) { - log.info("Processing message with id {}", message.getMessageId()); - - int nextInt = random.nextInt(100); - - if(nextInt <= 10) { - log.info("Randomly picked message with id {} as business validation failure.", message.getMessageId()); - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - if(nextInt > 90) { - log.info("Randomly picked message with id {} as intermittent failure.", message.getMessageId()); - throw new RuntimeException("Failed due to intermittent issue. Will be sent back for retry." + message.getMessageId()); - } - - return "Success"; - } -``` - -</details> - -## Deploy the sample application - -This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting -started with SAM in [the examples directory](../README.md) - -## Test the application - -As the test is pushing through a batch every 5 minutes, we can simply watch the logs to see the batches being processed: - -```bash - sam logs --tail --stack-name $MY_STACK -``` - -As the handler intentionally introduces intermittent failures, we should expect to see error messages too! diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml deleted file mode 100644 index e5156adee..000000000 --- a/examples/powertools-examples-sqs/pom.xml +++ /dev/null @@ -1,196 +0,0 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> - <artifactId>powertools-examples-sqs</artifactId> - <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> - - <properties> - <log4j.version>2.20.0</log4j.version> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>url-connection-client</artifactId> - <version>2.20.126</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - <version>${log4j.version}</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - <version>${log4j.version}</version> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-datatype-joda</artifactId> - <version>2.15.2</version> - </dependency> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <transformers> - <transformer - implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> - </transformer> - </transformers> - </configuration> - </execution> - </executions> - <dependencies> - <dependency> - <groupId>com.github.edwgiz</groupId> - <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> - <version>2.15</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> -</project> diff --git a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java deleted file mode 100644 index 701d6808f..000000000 --- a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.demo.sqs; - -import static java.util.stream.Collectors.toList; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.joda.JodaModule; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.stream.IntStream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.MessageAttributeValue; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; - -public class SqsMessageSender implements RequestHandler<ScheduledEvent, String> { - - private static final Logger log = LogManager.getLogger(SqsMessageSender.class); - - private static final SqsClient sqsClient = SqsClient.builder() - .httpClient(UrlConnectionHttpClient.create()) - .build(); - - private static final Random random = new SecureRandom(); - - private static final ObjectMapper objectMapper; - - static { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JodaModule()); - LoggingUtils.defaultObjectMapper(objectMapper); - } - - @Logging(logEvent = true) - public String handleRequest(final ScheduledEvent input, final Context context) { - String queueUrl = System.getenv("QUEUE_URL"); - - // Push 5 messages on each invoke. - List<SendMessageBatchRequestEntry> batchRequestEntries = IntStream.range(0, 5) - .mapToObj(value -> - { - Map<String, MessageAttributeValue> attributeValueHashMap = new HashMap<>(); - attributeValueHashMap.put("Key" + value, MessageAttributeValue.builder() - .dataType("String") - .stringValue("Value" + value) - .build()); - - byte[] array = new byte[7]; - random.nextBytes(array); - - return SendMessageBatchRequestEntry.builder() - .messageAttributes(attributeValueHashMap) - .id(input.getId() + value) - .messageBody("Sample Message " + value) - .build(); - }).collect(toList()); - - SendMessageBatchResponse sendMessageBatchResponse = sqsClient.sendMessageBatch(SendMessageBatchRequest.builder() - .queueUrl(queueUrl) - .entries(batchRequestEntries) - .build()); - - log.info("Sent Message {}", sendMessageBatchResponse); - - return "Success"; - } -} \ No newline at end of file diff --git a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java deleted file mode 100644 index 9ad5c7868..000000000 --- a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.demo.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.security.SecureRandom; -import java.util.Random; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; -import software.amazon.lambda.powertools.sqs.SqsUtils; - -/** - * Handler for requests to Lambda function. - */ -public class SqsPoller implements RequestHandler<SQSEvent, String> { - - static { - // https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/lambda-optimize-starttime.html - SqsUtils.overrideSqsClient(SqsClient.builder() - .httpClient(UrlConnectionHttpClient.create()) - .build()); - } - - Logger log = LogManager.getLogger(SqsPoller.class); - Random random = new SecureRandom(); - - @SqsBatch(value = BatchProcessor.class, nonRetryableExceptions = {IllegalArgumentException.class}) - @Logging(logEvent = true) - public String handleRequest(final SQSEvent input, final Context context) { - return "Success"; - } - - private class BatchProcessor implements SqsMessageHandler<Object> { - @Override - public String process(SQSMessage message) { - log.info("Processing message with id {}", message.getMessageId()); - - int nextInt = random.nextInt(100); - - if (nextInt <= 10) { - log.info("Randomly picked message with id {} as business validation failure.", message.getMessageId()); - throw new IllegalArgumentException( - "Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - if (nextInt > 90) { - log.info("Randomly picked message with id {} as intermittent failure.", message.getMessageId()); - throw new RuntimeException( - "Failed due to intermittent issue. Will be sent back for retry." + message.getMessageId()); - } - - return "Success"; - } - } -} \ No newline at end of file diff --git a/examples/powertools-examples-sqs/template.yaml b/examples/powertools-examples-sqs/template.yaml deleted file mode 100644 index 50327de18..000000000 --- a/examples/powertools-examples-sqs/template.yaml +++ /dev/null @@ -1,148 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - sqs batch processing demo - -Globals: - Function: - Timeout: 20 - Runtime: java11 - MemorySize: 512 - Tracing: Active - Environment: - Variables: - # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables - POWERTOOLS_LOG_LEVEL: INFO - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 - POWERTOOLS_LOGGER_LOG_EVENT: true - -Resources: - CustomerKey: - Type: AWS::KMS::Key - Properties: - Description: KMS key for encrypted queues - Enabled: true - KeyPolicy: - Version: '2012-10-17' - Statement: - - Sid: Enable IAM User Permissions - Effect: Allow - Principal: - AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root' - Action: 'kms:*' - Resource: '*' - - Sid: Allow use of the key - Effect: Allow - Principal: - Service: lambda.amazonaws.com - Action: - - kms:Decrypt - - kms:GenerateDataKey - Resource: '*' - - CustomerKeyAlias: - Type: AWS::KMS::Alias - Properties: - AliasName: alias/sqs-key - TargetKeyId: !Ref CustomerKey - - DemoDlqSqsQueue: - Type: AWS::SQS::Queue - Properties: - KmsMasterKeyId: !Ref CustomerKey - - DemoSqsQueue: - Type: AWS::SQS::Queue - Properties: - RedrivePolicy: - deadLetterTargetArn: - Fn::GetAtt: - - "DemoDlqSqsQueue" - - "Arn" - maxReceiveCount: 2 - KmsMasterKeyId: !Ref CustomerKey - - DemoSQSSenderFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: org.demo.sqs.SqsMessageSender::handleRequest - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: sqs-demo - QUEUE_URL: !Ref DemoSqsQueue - Policies: - - Statement: - - Sid: SQSSendMessageBatch - Effect: Allow - Action: - - sqs:SendMessageBatch - - sqs:SendMessage - Resource: !GetAtt DemoSqsQueue.Arn - - Sid: SQSKMSKey - Effect: Allow - Action: - - kms:GenerateDataKey - - kms:Decrypt - Resource: !GetAtt CustomerKey.Arn - Events: - CWSchedule: - Type: Schedule - Properties: - Schedule: 'rate(5 minutes)' - Name: !Join ["-", ["message-producer-schedule", !Select [0, !Split [-, !Select [2, !Split [/, !Ref AWS::StackId ]]]]]] - Description: Produce message to SQS via a Lambda function - Enabled: true - - DemoSQSConsumerFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: org.demo.sqs.SqsPoller::handleRequest - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: sqs-demo - Policies: - - Statement: - - Sid: SQSDeleteGetAttribute - Effect: Allow - Action: - - sqs:DeleteMessageBatch - - sqs:GetQueueAttributes - Resource: !GetAtt DemoSqsQueue.Arn - - Sid: SQSSendMessageBatch - Effect: Allow - Action: - - sqs:SendMessageBatch - - sqs:SendMessage - Resource: !GetAtt DemoDlqSqsQueue.Arn - - Sid: SQSKMSKey - Effect: Allow - Action: - - kms:GenerateDataKey - - kms:Decrypt - Resource: !GetAtt CustomerKey.Arn - Events: - MySQSEvent: - Type: SQS - Properties: - Queue: !GetAtt DemoSqsQueue.Arn - BatchSize: 2 - MaximumBatchingWindowInSeconds: 300 - -Outputs: - DemoSqsQueue: - Description: "ARN for main SQS queue" - Value: !GetAtt DemoSqsQueue.Arn - DemoDlqSqsQueue: - Description: "ARN for DLQ" - Value: !GetAtt DemoDlqSqsQueue.Arn - DemoSQSSenderFunction: - Description: "Sender SQS Lambda Function ARN" - Value: !GetAtt DemoSQSSenderFunction.Arn - DemoSQSConsumerFunction: - Description: "Consumer SQS Lambda Function ARN" - Value: !GetAtt DemoSQSConsumerFunction.Arn - DemoSQSConsumerFunctionRole: - Description: "Implicit IAM Role created for SQS Lambda Function ARN" - Value: !GetAtt DemoSQSConsumerFunctionRole.Arn diff --git a/examples/powertools-examples-validation/README.md b/examples/powertools-examples-validation/README.md index 3f6790b0c..2f2028301 100644 --- a/examples/powertools-examples-validation/README.md +++ b/examples/powertools-examples-validation/README.md @@ -15,7 +15,7 @@ started with SAM in [the examples directory](../README.md) To test the validation, we can POST a JSON object shaped like our schema: ```bash - curl -X POST https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ -H "Content-Type: application/json" -d '{"address": "https://checkip.amazonaws.com"}' + curl -X POST https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ -H "Content-Type: application/json" -d '{"id": 123,"name":"The Hitchhikers Guide to the Galaxy","price":10.99}' ``` If we break the schema - for instance, by removing one of the compulsory fields, diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 1c7e33de0..2fa8462a5 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -13,24 +13,24 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> + <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> <properties> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.skip>true</maven.deploy.skip> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -41,82 +41,59 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.4.0</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> </dependency> <!-- Test dependencies --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>4.11.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.3</version> + <version>5.11.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> - <version>5.9.3</version> + <version>5.13.1</version> <scope>test</scope> </dependency> </dependencies> <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.1.2</version> - </plugin> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-validation</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - <profiles> - <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class - version mismatch issues. All subsequent Java releases build with the default AspectJ configuration - on the project. - - Note: - - if you are running Java > 1.8, you can remove this profile altogether - - If you are running on Java 1.8, you should apply the aspectJ version here to the project, and remove - the profile. - --> - <profile> - <id>jdk8</id> - <activation> - <jdk>(,11)</jdk> <!-- 8 --> - </activation> - <properties> - <aspectj.version>1.9.7</aspectj.version> - </properties> - <dependencyManagement> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-validation</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> <dependencies> <dependency> <groupId>org.aspectj</groupId> @@ -124,45 +101,16 @@ <version>${aspectj.version}</version> </dependency> </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-validation</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> </project> diff --git a/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java b/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java deleted file mode 100644 index d5e6de313..000000000 --- a/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.demo.validation; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import software.amazon.lambda.powertools.validation.ValidationException; - -public class InboundValidationTest { - - @Mock - private Context context; - private InboundValidation inboundValidation; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - inboundValidation = new InboundValidation(); - } - - @Test - public void shouldReturnOkStatusWhenInputIsValid() { - String body = "{\n" + - " \"id\": 43242,\n" + - " \"name\": \"FooBar XY\",\n" + - " \"price\": 258\n" + - " }"; - APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent().withBody(body); - - APIGatewayProxyResponseEvent response = inboundValidation.handleRequest(request, context); - - assertEquals(200, response.getStatusCode()); - } - - @Test - public void shouldThrowExceptionWhenRequestInInvalid() { - String bodyWithMissedId = "{\n" + - " \"name\": \"FooBar XY\",\n" + - " \"price\": 258\n" + - " }"; - APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent().withBody(bodyWithMissedId); - - assertThrows(ValidationException.class, () -> inboundValidation.handleRequest(request, context)); - } -} \ No newline at end of file diff --git a/examples/spotbugs-exclude.xml b/examples/spotbugs-exclude.xml deleted file mode 100644 index e33e65478..000000000 --- a/examples/spotbugs-exclude.xml +++ /dev/null @@ -1,20 +0,0 @@ -<!-- This file specifies a spotbugs filter for excluding reports that - should not be considered errors. - The format of this file is documented at: - https://spotbugs.readthedocs.io/en/latest/filter.html - When possible, please specify the full names of the bug codes, - using the pattern attribute, to make it clearer what reports are - being suppressed. You can find a listing of codes at: - https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html - --> -<FindBugsFilter> - <Match> - <Bug pattern="DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED"/> - <Or> - <And> - <Class name="helloworld.App"/> - <Method name="threadOption1"/> - </And> - </Or> - </Match> -</FindBugsFilter> \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index d54ece508..b52b88cca 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,11 +1,14 @@ site_name: Powertools for AWS Lambda (Java) site_description: Powertools for AWS Lambda (Java) site_author: Amazon Web Services -site_url: https://docs.powertools.aws.dev/lambda-java/ +site_url: https://docs.aws.amazon.com/powertools/java/latest/ nav: - Homepage: index.md + - Usage patterns: usage-patterns.md - Changelog: changelog.md + - Upgrade Guide: upgrade.md - FAQs: FAQs.md + - Roadmap: roadmap.md - Core utilities: - core/logging.md - core/tracing.md @@ -13,16 +16,18 @@ nav: - Utilities: - utilities/idempotency.md - utilities/parameters.md - - utilities/large_messages.md - utilities/batch.md + - utilities/kafka.md + - utilities/large_messages.md - utilities/validation.md - utilities/custom_resources.md - utilities/serialization.md - - Deprecated: - - utilities/sqs_large_message_handling.md - - utilities/sqs_batch.md - Processes: - processes/maintainers.md + - "Versioning policy": processes/versioning.md + - Resources: + - "llms.txt": ./llms.txt + - "llms.txt (full version)": ./llms-full.txt theme: name: material @@ -60,8 +65,10 @@ markdown_extensions: alternate_style: true - pymdownx.details - pymdownx.snippets: - base_path: '.' + base_path: "." check_paths: true + - pymdownx.tasklist: + custom_checkbox: true - pymdownx.superfences: custom_fences: - name: mermaid @@ -73,22 +80,59 @@ markdown_extensions: toc_depth: 4 - attr_list -copyright: Copyright © 2021 Amazon Web Services +copyright: | + <div id="awsdocs-legal-zone-copyright"> + <a href="https://aws.amazon.com/privacy" target="_blank" rel="nofollow">Privacy</a> | + <a href="https://aws.amazon.com/terms/" target="_blank" rel="nofollow">Site terms</a> | + <span class="copyright"> + © 2025, Amazon Web Services, Inc. or its affiliates. All rights reserved. + </span> + </div> plugins: - git-revision-date - search - macros + - privacy + - llmstxt: + markdown_description: Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. It provides a suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. + full_output: llms-full.txt + sections: + Project Overview: + - index.md + - usage-patterns.md + - changelog.md + - FAQs.md + - roadmap.md + Core Utilities: + - core/logging.md + - core/metrics.md + - core/tracing.md + Utilities: + - utilities/idempotency.md + - utilities/parameters.md + - utilities/batch.md + - utilities/kafka.md + - utilities/large_messages.md + - utilities/validation.md + - utilities/custom_resources.md + - utilities/serialization.md + Processes: + - processes/maintainers.md + - processes/versioning.md extra_css: - stylesheets/extra.css extra_javascript: - - javascript/aws-amplify.min.js - - javascript/extra.js + - https://docs.powertools.aws.dev/shared/mermaid.min.js + - https://docs.aws.amazon.com/assets/js/awsdocs-boot.js extra: powertools: - version: 1.16.1 # to update after each release (we do not want snapshot version here) + version: 2.9.0 + version: + provider: mike + default: latest repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index d75a731b1..e6cf78b14 100644 --- a/pom.xml +++ b/pom.xml @@ -14,19 +14,32 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> <packaging>pom</packaging> - <name>Powertools for AWS Lambda (Java) library Parent</name> + <name>Powertools for AWS Lambda (Java) - Parent</name> <description> A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. </description> + <scm> + <url>https://github.com/aws-powertools/powertools-lambda-java</url> + <connection>scm:git:git://github.com/aws-powertools/powertools-lambda-java.git</connection> + <developerConnection>scm:git:ssh://github.com:aws-powertools/powertools-lambda-java.git</developerConnection> + </scm> + <developers> + <developer> + <name>Powertools for AWS team</name> + <email>aws-powertools-maintainers@amazon.com</email> + <organization>Amazon Web Services Inc.</organization> + <organizationUrl>https://aws.amazon.com</organizationUrl> + </developer> + </developers> <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> @@ -41,73 +54,102 @@ </licenses> <modules> - <module>powertools-core</module> + <module>powertools-common</module> <module>powertools-serialization</module> + <module>powertools-kafka</module> <module>powertools-logging</module> + <module>powertools-logging/powertools-logging-log4j</module> + <module>powertools-logging/powertools-logging-logback</module> <module>powertools-tracing</module> - <module>powertools-sqs</module> <module>powertools-metrics</module> <module>powertools-parameters</module> <module>powertools-validation</module> - <module>powertools-test-suite</module> <module>powertools-cloudformation</module> <module>powertools-idempotency</module> <module>powertools-large-messages</module> <module>powertools-e2e-tests</module> + <module>powertools-e2e-tests/handlers</module> <module>powertools-batch</module> + <module>powertools-parameters/powertools-parameters-ssm</module> + <module>powertools-parameters/powertools-parameters-secrets</module> + <module>powertools-parameters/powertools-parameters-dynamodb</module> + <module>powertools-parameters/powertools-parameters-appconfig</module> + <module>powertools-parameters/powertools-parameters-tests</module> <module>examples</module> </modules> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - - <developers> - <developer> - <name>Powertools for AWS Lambda (Java) team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - <properties> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <log4j.version>2.20.0</log4j.version> - <jackson.version>2.15.2</jackson.version> - <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.126</aws.sdk.version> - <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> - <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> + <log4j.version>2.25.3</log4j.version> + <slf4j.version>2.0.17</slf4j.version> + <jackson.version>2.20.1</jackson.version> + <aws.sdk.version>2.40.9</aws.sdk.version> + <aws.xray.recorder.version>2.20.0</aws.xray.recorder.version> + <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <lambda.core.version>1.2.2</lambda.core.version> - <lambda.events.version>3.11.2</lambda.events.version> - <lambda.serial.version>1.1.2</lambda.serial.version> - <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> + <lambda.core.version>1.4.0</lambda.core.version> + <lambda.events.version>3.16.1</lambda.events.version> + <lambda.serial.version>1.1.6</lambda.serial.version> + <maven-compiler-plugin.version>3.14.1</maven-compiler-plugin.version> + <aspectj.version>1.9.7</aspectj.version> <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> - <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> - <jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version> + <jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> - <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version> - <maven-source-plugin.version>3.3.0</maven-source-plugin.version> - <maven-gpg-plugin.version>3.1.0</maven-gpg-plugin.version> - <junit.version>5.10.0</junit.version> - <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> - <jmespath.version>0.5.1</jmespath.version> + <maven-javadoc-plugin.version>3.12.0</maven-javadoc-plugin.version> + <maven-source-plugin.version>3.3.1</maven-source-plugin.version> + <maven-gpg-plugin.version>3.2.1</maven-gpg-plugin.version> + <aspectj-maven-plugin.version>1.14.1</aspectj-maven-plugin.version> + <maven-surefire-plugin.version>3.5.4</maven-surefire-plugin.version> + <jacoco-maven-plugin.version>0.8.14</jacoco-maven-plugin.version> + <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> + <maven-javadoc-plugin.version>3.12.0</maven-javadoc-plugin.version> + <maven-source-plugin.version>3.3.1</maven-source-plugin.version> + <maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version> + <junit.version>5.14.0</junit.version> + <aws-embedded-metrics.version>4.1.2</aws-embedded-metrics.version> + <jmespath.version>0.6.0</jmespath.version> + <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> + <versions-maven-plugin.version>2.20.1</versions-maven-plugin.version> + <elastic.version>1.7.0</elastic.version> + <mockito.version>5.20.0</mockito.version> + <mockito-junit-jupiter.version>5.21.0</mockito-junit-jupiter.version> + <junit-pioneer.version>2.3.0</junit-pioneer.version> + <crac.version>1.5.0</crac.version> + + <!-- As we have a .mvn directory at the root of the project, this will evaluate to the root directory + regardless of where maven is run - sub-module, or root. --> + <project.rootdir>${maven.multiModuleProjectDirectory}</project.rootdir> </properties> <distributionManagement> <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> + <id>central</id> + <url>https://central.sonatype.com/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement> + <!-- https://central.sonatype.org/publish/publish-portal-snapshots/#consuming-via-maven --> + <repositories> + <repository> + <name>Central Portal Snapshots</name> + <id>central-portal-snapshots</id> + <url>https://central.sonatype.com/repository/maven-snapshots/</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + </repositories> + <dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -120,6 +162,16 @@ <artifactId>powertools-logging</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-logback</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-sqs</artifactId> @@ -178,23 +230,20 @@ <version>${aspectj.version}</version> </dependency> <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-databind</artifactId> - <version>${jackson.version}</version> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-core</artifactId> + <groupId>com.fasterxml.jackson</groupId> + <artifactId>jackson-bom</artifactId> <version>${jackson.version}</version> + <scope>import</scope> + <type>pom</type> </dependency> <dependency> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-datatype-jsr310</artifactId> - <version>${jackson.version}</version> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> + <artifactId>log4j-slf4j-impl</artifactId> <version>${log4j.version}</version> </dependency> <dependency> @@ -217,6 +266,21 @@ <artifactId>log4j-jcl</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>co.elastic.logging</groupId> + <artifactId>logback-ecs-encoder</artifactId> + <version>${elastic.version}</version> + </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + <version>${crac.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-core</artifactId> @@ -242,6 +306,11 @@ <artifactId>aws-embedded-metrics</artifactId> <version>${aws-embedded-metrics.version}</version> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.20.0</version> + </dependency> <!-- Test dependencies --> <dependency> @@ -254,25 +323,7 @@ <dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> - <version>1.9.1</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - <version>3.13.0</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>4.11.0</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <version>4.11.0</version> + <version>${junit-pioneer.version}</version> <scope>test</scope> </dependency> <dependency> @@ -284,13 +335,25 @@ <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> - <version>3.24.2</version> + <version>3.27.6</version> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>net.bytebuddy</groupId> + <artifactId>byte-buddy</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>${slf4j.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.skyscreamer</groupId> <artifactId>jsonassert</artifactId> - <version>1.5.1</version> + <version>1.5.3</version> <scope>test</scope> </dependency> <dependency> @@ -298,16 +361,34 @@ <artifactId>aspectjtools</artifactId> <version>${aspectj.version}</version> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <version>${mockito-junit-jupiter.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-tests</artifactId> - <version>1.1.1</version> + <version>1.1.2</version> <scope>test</scope> </dependency> <dependency> - <groupId>com.github.tomakehurst</groupId> - <artifactId>wiremock-jre8</artifactId> - <version>2.35.0</version> + <groupId>org.wiremock</groupId> + <artifactId>wiremock</artifactId> + <version>3.13.2</version> <scope>test</scope> </dependency> </dependencies> @@ -316,6 +397,14 @@ <build> <pluginManagement> <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>versions-maven-plugin</artifactId> + <version>${versions-maven-plugin.version}</version> + <configuration> + <generateBackupPoms>false</generateBackupPoms> + </configuration> + </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> @@ -356,6 +445,11 @@ <artifactId>maven-gpg-plugin</artifactId> <version>${maven-gpg-plugin.version}</version> </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>3.6.2</version> + </plugin> </plugins> </pluginManagement> <plugins> @@ -368,6 +462,14 @@ <useIncrementalCompilation>false</useIncrementalCompilation> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-artifact-plugin</artifactId> + <version>3.6.1</version> + <configuration> + <reproducible>true</reproducible> + </configuration> + </plugin> <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> @@ -381,7 +483,6 @@ </configuration> <executions> <execution> - <phase>process-sources</phase> <goals> <goal>compile</goal> <goal>test-compile</goal> @@ -420,13 +521,12 @@ </executions> </plugin> <plugin> - <groupId>org.sonatype.plugins</groupId> - <artifactId>nexus-staging-maven-plugin</artifactId> + <groupId>org.sonatype.central</groupId> + <artifactId>central-publishing-maven-plugin</artifactId> + <version>0.9.0</version> <extensions>true</extensions> <configuration> - <serverId>ossrh</serverId> - <nexusUrl>https://aws.oss.sonatype.org</nexusUrl> - <autoReleaseAfterClose>true</autoReleaseAfterClose> + <publishingServerId>central</publishingServerId> </configuration> </plugin> </plugins> @@ -497,7 +597,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.7.3.5</version> + <version>4.9.8.1</version> <executions> <execution> <id>test</id> @@ -508,7 +608,7 @@ </executions> <configuration> <xmlOutput>true</xmlOutput> - <excludeFilterFile>../spotbugs-exclude.xml</excludeFilterFile> + <excludeFilterFile>${project.rootdir}/spotbugs-exclude.xml</excludeFilterFile> </configuration> </plugin> </plugins> @@ -524,7 +624,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> <configuration> <argLine> @{argLine} @@ -536,45 +635,6 @@ </plugins> </build> </profile> - <profile> - <id>newerThanJdk8</id> - <activation> - <jdk>[9,)</jdk> - </activation> - <build> - <plugins> - <plugin> - <!-- we run checkstyle only on Java 11, no need to run it for all JDK, it's just code style --> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - <version>3.3.0</version> - <configuration> - <configLocation>checkstyle.xml</configLocation> - <encoding>UTF-8</encoding> - <consoleOutput>true</consoleOutput> - <failsOnError>true</failsOnError> - <linkXRef>false</linkXRef> - </configuration> - <!-- does not work without this dependency --> - <!-- does not work with this dependency on Java 8 --> - <dependencies> - <dependency> - <groupId>com.puppycrawl.tools</groupId> - <artifactId>checkstyle</artifactId> - <version>10.12.2</version> - </dependency> - </dependencies> - <executions> - <execution> - <goals> - <goal>check</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> </profiles> </project> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 9e25dabd8..37cfdf7b2 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,9 +6,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> + <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> + <name>Powertools for AWS Lambda (Java) - Batch messages</name> + <build> <plugins> <plugin> @@ -18,10 +21,21 @@ <skip>true</skip> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <!-- enforce multiple threads for parallel processing tests --> + <argLine> + -Djava.util.concurrent.ForkJoinPool.common.parallelism=4 + </argLine> + </configuration> + </plugin> </plugins> </build> <artifactId>powertools-batch</artifactId> + <dependencies> <dependency> <groupId>com.amazonaws</groupId> @@ -36,6 +50,16 @@ <artifactId>powertools-serialization</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <scope>provided</scope> + </dependency> <!-- Test dependencies --> <dependency> @@ -59,10 +83,15 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> <scope>test</scope> </dependency> </dependencies> -</project> \ No newline at end of file +</project> diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/BatchMessageHandler.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/BatchMessageHandler.java index 730211feb..c63409e35 100644 --- a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/BatchMessageHandler.java +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/BatchMessageHandler.java @@ -16,6 +16,9 @@ import com.amazonaws.services.lambda.runtime.Context; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; + /** * The basic interface a batch message handler must meet. * @@ -33,6 +36,31 @@ public interface BatchMessageHandler<E, R> { * @param context The lambda context * @return A partial batch response */ - public abstract R processBatch(E event, Context context); + R processBatch(E event, Context context); + + /** + * Processes the given batch in parallel returning a partial batch + * response indicating the success and failure of individual + * messages within the batch. <br/> + * Note that parallel processing is not always better than sequential processing, + * and you should benchmark your code to determine the best approach for your use case. <br/> + * Also note that to get more threads available (more vCPUs), + * you need to increase the amount of memory allocated to your Lambda function. <br/> + + * + * @param event The Lambda event containing the batch to process + * @param context The lambda context + * @return A partial batch response + */ + R processBatchInParallel(E event, Context context); + + /** + * Same as {@link #processBatchInParallel(Object, Context)} but with an option to provide custom {@link Executor} + * @param event The Lambda event containing the batch to process + * @param context The lambda context + * @param executor Custom executor to use for parallel processing + * @return A partial batch response + */ + R processBatchInParallel(E event, Context context, Executor executor); } diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java index aa6eba839..dbfdf63cd 100644 --- a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java @@ -14,31 +14,41 @@ package software.amazon.lambda.powertools.batch.handler; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; -import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; + +import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; +import software.amazon.lambda.powertools.batch.internal.XRayTraceEntityPropagator; + /** * A batch message processor for DynamoDB Streams batches. * * @see <a href="https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-batchfailurereporting">DynamoDB Streams batch failure reporting</a> */ public class DynamoDbBatchMessageHandler implements BatchMessageHandler<DynamodbEvent, StreamsEventResponse> { - private final static Logger LOGGER = LoggerFactory.getLogger(DynamoDbBatchMessageHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbBatchMessageHandler.class); private final Consumer<DynamodbEvent.DynamodbStreamRecord> successHandler; private final BiConsumer<DynamodbEvent.DynamodbStreamRecord, Throwable> failureHandler; private final BiConsumer<DynamodbEvent.DynamodbStreamRecord, Context> rawMessageHandler; public DynamoDbBatchMessageHandler(Consumer<DynamodbEvent.DynamodbStreamRecord> successHandler, - BiConsumer<DynamodbEvent.DynamodbStreamRecord, Throwable> failureHandler, - BiConsumer<DynamodbEvent.DynamodbStreamRecord, Context> rawMessageHandler) { + BiConsumer<DynamodbEvent.DynamodbStreamRecord, Throwable> failureHandler, + BiConsumer<DynamodbEvent.DynamodbStreamRecord, Context> rawMessageHandler) { this.successHandler = successHandler; this.failureHandler = failureHandler; this.rawMessageHandler = rawMessageHandler; @@ -46,35 +56,97 @@ public DynamoDbBatchMessageHandler(Consumer<DynamodbEvent.DynamodbStreamRecord> @Override public StreamsEventResponse processBatch(DynamodbEvent event, Context context) { - List<StreamsEventResponse.BatchItemFailure> batchFailures = new ArrayList<>(); + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .stream() + .map(eventRecord -> processBatchItem(eventRecord, context)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); - for (DynamodbEvent.DynamodbStreamRecord record : event.getRecords()) { - try { + return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } - rawMessageHandler.accept(record, context); - // Report success if we have a handler - if (this.successHandler != null) { - this.successHandler.accept(record); - } - } catch (Throwable t) { - String sequenceNumber = record.getDynamodb().getSequenceNumber(); - LOGGER.error("Error while processing record with id {}: {}, adding it to batch item failures", - sequenceNumber, t.getMessage()); - LOGGER.error("Error was", t); - batchFailures.add(new StreamsEventResponse.BatchItemFailure(sequenceNumber)); - - // Report failure if we have a handler - if (this.failureHandler != null) { - // A failing failure handler is no reason to fail the batch - try { - this.failureHandler.accept(record, t); - } catch (Throwable t2) { - LOGGER.warn("failureHandler threw handling failure", t2); - } + @Override + public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context context) { + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .parallelStream() // Parallel processing + .map(eventRecord -> { + AtomicReference<Optional<StreamsEventResponse.BatchItemFailure>> result = new AtomicReference<>(); + + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + result.set(processBatchItem(eventRecord, context)); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + + return result.get(); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } + + @Override + public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context context, Executor executor) { + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = new ArrayList<>(); + List<CompletableFuture<Void>> futures = event.getRecords().stream() + .map(eventRecord -> CompletableFuture.runAsync(() -> { + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, + context); + failureOpt.ifPresent(batchItemFailures::add); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + }, executor)) + .collect(Collectors.toList()); + futures.forEach(CompletableFuture::join); + return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } + + private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem( + DynamodbEvent.DynamodbStreamRecord streamRecord, Context context) { + try { + LOGGER.debug("Processing item {}", streamRecord.getEventID()); + + rawMessageHandler.accept(streamRecord, context); + + // Report success if we have a handler + if (this.successHandler != null) { + this.successHandler.accept(streamRecord); + } + return Optional.empty(); + } catch (Exception e) { + String sequenceNumber = streamRecord.getDynamodb().getSequenceNumber(); + LOGGER.error("Error while processing record with id {}: {}, adding it to batch item failures", + sequenceNumber, e.getMessage()); + LOGGER.error("Error was", e); + + // Report failure if we have a handler + if (this.failureHandler != null) { + // A failing failure handler is no reason to fail the batch + try { + this.failureHandler.accept(streamRecord, e); + } catch (Exception e2) { + LOGGER.warn("failureHandler threw handling failure", e2); } } + return Optional + .of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(sequenceNumber).build()); } - - return new StreamsEventResponse(batchFailures); } } diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java index fe1aaf354..f147578d4 100644 --- a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java @@ -14,16 +14,25 @@ package software.amazon.lambda.powertools.batch.handler; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.KinesisEvent; -import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; + +import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; +import software.amazon.lambda.powertools.batch.internal.XRayTraceEntityPropagator; import software.amazon.lambda.powertools.utilities.EventDeserializer; /** @@ -34,7 +43,7 @@ * @param <M> The user-defined type of the Kinesis record payload */ public class KinesisStreamsBatchMessageHandler<M> implements BatchMessageHandler<KinesisEvent, StreamsEventResponse> { - private final static Logger LOGGER = LoggerFactory.getLogger(KinesisStreamsBatchMessageHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(KinesisStreamsBatchMessageHandler.class); private final BiConsumer<KinesisEvent.KinesisEventRecord, Context> rawMessageHandler; private final BiConsumer<M, Context> messageHandler; @@ -43,10 +52,10 @@ public class KinesisStreamsBatchMessageHandler<M> implements BatchMessageHandler private final BiConsumer<KinesisEvent.KinesisEventRecord, Throwable> failureHandler; public KinesisStreamsBatchMessageHandler(BiConsumer<KinesisEvent.KinesisEventRecord, Context> rawMessageHandler, - BiConsumer<M, Context> messageHandler, - Class<M> messageClass, - Consumer<KinesisEvent.KinesisEventRecord> successHandler, - BiConsumer<KinesisEvent.KinesisEventRecord, Throwable> failureHandler) { + BiConsumer<M, Context> messageHandler, + Class<M> messageClass, + Consumer<KinesisEvent.KinesisEventRecord> successHandler, + BiConsumer<KinesisEvent.KinesisEventRecord, Throwable> failureHandler) { this.rawMessageHandler = rawMessageHandler; this.messageHandler = messageHandler; @@ -57,42 +66,103 @@ public KinesisStreamsBatchMessageHandler(BiConsumer<KinesisEvent.KinesisEventRec @Override public StreamsEventResponse processBatch(KinesisEvent event, Context context) { - List<StreamsEventResponse.BatchItemFailure> batchFailures = new ArrayList<>(); - - for (KinesisEvent.KinesisEventRecord record : event.getRecords()) { - try { - if (this.rawMessageHandler != null) { - rawMessageHandler.accept(record, context); - } else { - M messageDeserialized = EventDeserializer.extractDataFrom(record).as(messageClass); - messageHandler.accept(messageDeserialized, context); - } + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .stream() + .map(eventRecord -> processBatchItem(eventRecord, context)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); - // Report success if we have a handler - if (this.successHandler != null) { - this.successHandler.accept(record); - } - } catch (Throwable t) { - String sequenceNumber = record.getEventID(); - LOGGER.error("Error while processing record with eventID {}: {}, adding it to batch item failures", - sequenceNumber, t.getMessage()); - LOGGER.error("Error was", t); - - batchFailures.add(new StreamsEventResponse.BatchItemFailure(record.getKinesis().getSequenceNumber())); - - // Report failure if we have a handler - if (this.failureHandler != null) { - // A failing failure handler is no reason to fail the batch - try { - this.failureHandler.accept(record, t); - } catch (Throwable t2) { - LOGGER.warn("failureHandler threw handling failure", t2); - } + return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } + + @Override + public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context context) { + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .parallelStream() // Parallel processing + .map(eventRecord -> { + AtomicReference<Optional<StreamsEventResponse.BatchItemFailure>> result = new AtomicReference<>(); + + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + result.set(processBatchItem(eventRecord, context)); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + + return result.get(); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } + + @Override + public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context context, Executor executor) { + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = new ArrayList<>(); + List<CompletableFuture<Void>> futures = event.getRecords().stream() + .map(eventRecord -> CompletableFuture.runAsync(() -> { + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, + context); + failureOpt.ifPresent(batchItemFailures::add); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + }, executor)) + .collect(Collectors.toList()); + futures.forEach(CompletableFuture::join); + return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } + + private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem( + KinesisEvent.KinesisEventRecord eventRecord, Context context) { + try { + LOGGER.debug("Processing item {}", eventRecord.getEventID()); + + if (this.rawMessageHandler != null) { + rawMessageHandler.accept(eventRecord, context); + } else { + M messageDeserialized = EventDeserializer.extractDataFrom(eventRecord).as(messageClass); + messageHandler.accept(messageDeserialized, context); + } + + // Report success if we have a handler + if (this.successHandler != null) { + this.successHandler.accept(eventRecord); + } + return Optional.empty(); + } catch (Exception e) { + String sequenceNumber = eventRecord.getEventID(); + LOGGER.error("Error while processing record with eventID {}: {}, adding it to batch item failures", + sequenceNumber, e.getMessage()); + LOGGER.error("Error was", e); + + // Report failure if we have a handler + if (this.failureHandler != null) { + // A failing failure handler is no reason to fail the batch + try { + this.failureHandler.accept(eventRecord, e); + } catch (Exception e2) { + LOGGER.warn("failureHandler threw handling failure", e2); } } - } - return new StreamsEventResponse(batchFailures); + return Optional.of(StreamsEventResponse.BatchItemFailure.builder() + .withItemIdentifier(eventRecord.getKinesis().getSequenceNumber()).build()); + } } } - diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java index b3c416a69..737c7cceb 100644 --- a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java @@ -14,14 +14,26 @@ package software.amazon.lambda.powertools.batch.handler; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; + +import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; +import software.amazon.lambda.powertools.batch.internal.XRayTraceEntityPropagator; import software.amazon.lambda.powertools.utilities.EventDeserializer; /** @@ -31,11 +43,11 @@ * @see <a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting">SQS Batch failure reporting</a> */ public class SqsBatchMessageHandler<M> implements BatchMessageHandler<SQSEvent, SQSBatchResponse> { - private final static Logger LOGGER = LoggerFactory.getLogger(SqsBatchMessageHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SqsBatchMessageHandler.class); // The attribute on an SQS-FIFO message used to record the message group ID // https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#sample-fifo-queues-message-event - private final static String MESSAGE_GROUP_ID_KEY = "MessageGroupId"; + private static final String MESSAGE_GROUP_ID_KEY = "MessageGroupId"; private final Class<M> messageClass; private final BiConsumer<M, Context> messageHandler; @@ -44,9 +56,9 @@ public class SqsBatchMessageHandler<M> implements BatchMessageHandler<SQSEvent, private final BiConsumer<SQSEvent.SQSMessage, Throwable> failureHandler; public SqsBatchMessageHandler(BiConsumer<M, Context> messageHandler, Class<M> messageClass, - BiConsumer<SQSEvent.SQSMessage, Context> rawMessageHandler, - Consumer<SQSEvent.SQSMessage> successHandler, - BiConsumer<SQSEvent.SQSMessage, Throwable> failureHandler) { + BiConsumer<SQSEvent.SQSMessage, Context> rawMessageHandler, + Consumer<SQSEvent.SQSMessage> successHandler, + BiConsumer<SQSEvent.SQSMessage, Throwable> failureHandler) { this.messageHandler = messageHandler; this.messageClass = messageClass; this.rawMessageHandler = rawMessageHandler; @@ -61,57 +73,27 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { // If we are working on a FIFO queue, when any message fails we should stop processing and return the // rest of the batch as failed too. We use this variable to track when that has happened. // https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting - boolean failWholeBatch = false; + final AtomicBoolean failWholeBatch = new AtomicBoolean(false); int messageCursor = 0; - for (; messageCursor < event.getRecords().size() && !failWholeBatch; messageCursor++) { + for (; messageCursor < event.getRecords().size() && !failWholeBatch.get(); messageCursor++) { SQSEvent.SQSMessage message = event.getRecords().get(messageCursor); - String messageGroupId = message.getAttributes() != null ? - message.getAttributes().get(MESSAGE_GROUP_ID_KEY) : null; - - try { - if (this.rawMessageHandler != null) { - rawMessageHandler.accept(message, context); - } else { - M messageDeserialized = EventDeserializer.extractDataFrom(message).as(messageClass); - messageHandler.accept(messageDeserialized, context); - } - - // Report success if we have a handler - if (this.successHandler != null) { - this.successHandler.accept(message); - } + String messageGroupId = message.getAttributes() != null ? message.getAttributes().get(MESSAGE_GROUP_ID_KEY) + : null; - } catch (Throwable t) { - LOGGER.error("Error while processing message with messageId {}: {}, adding it to batch item failures", - message.getMessageId(), t.getMessage()); - LOGGER.error("Error was", t); - - response.getBatchItemFailures() - .add(SQSBatchResponse.BatchItemFailure.builder().withItemIdentifier(message.getMessageId()) - .build()); + processBatchItem(message, context).ifPresent(batchItemFailure -> { + response.getBatchItemFailures().add(batchItemFailure); if (messageGroupId != null) { - failWholeBatch = true; + failWholeBatch.set(true); LOGGER.info( - "A message in a batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too" - , messageGroupId, message.getMessageId()); - } - - // Report failure if we have a handler - if (this.failureHandler != null) { - // A failing failure handler is no reason to fail the batch - try { - this.failureHandler.accept(message, t); - } catch (Throwable t2) { - LOGGER.warn("failureHandler threw handling failure", t2); - } + "A message in a batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too", + messageGroupId, message.getMessageId()); } - - } + }); } - if (failWholeBatch) { + if (failWholeBatch.get()) { // Add the remaining messages to the batch item failures event.getRecords() .subList(messageCursor, event.getRecords().size()) @@ -121,4 +103,107 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { } return response; } + + @Override + public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) { + if (isFIFOEnabled(event)) { + throw new UnsupportedOperationException( + "FIFO queues are not supported in parallel mode, use the processBatch method instead"); + } + + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + + List<SQSBatchResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .parallelStream() // Parallel processing + .map(sqsMessage -> { + AtomicReference<Optional<SQSBatchResponse.BatchItemFailure>> result = new AtomicReference<>(); + + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + result.set(processBatchItem(sqsMessage, context)); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + + return result.get(); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + return SQSBatchResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } + + @Override + public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context, Executor executor) { + if (isFIFOEnabled(event)) { + throw new UnsupportedOperationException( + "FIFO queues are not supported in parallel mode, use the processBatch method instead"); + } + + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + + List<SQSBatchResponse.BatchItemFailure> batchItemFailures = new ArrayList<>(); + List<CompletableFuture<Void>> futures = event.getRecords().stream() + .map(eventRecord -> CompletableFuture.runAsync(() -> { + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + Optional<SQSBatchResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, + context); + failureOpt.ifPresent(batchItemFailures::add); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + }, executor)) + .collect(Collectors.toList()); + futures.forEach(CompletableFuture::join); + + return SQSBatchResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } + + private Optional<SQSBatchResponse.BatchItemFailure> processBatchItem(SQSEvent.SQSMessage message, Context context) { + try { + LOGGER.debug("Processing message {}", message.getMessageId()); + + if (this.rawMessageHandler != null) { + rawMessageHandler.accept(message, context); + } else { + M messageDeserialized = EventDeserializer.extractDataFrom(message).as(messageClass); + messageHandler.accept(messageDeserialized, context); + } + + // Report success if we have a handler + if (this.successHandler != null) { + this.successHandler.accept(message); + } + return Optional.empty(); + } catch (Exception e) { + LOGGER.error("Error while processing message with messageId {}: {}, adding it to batch item failures", + message.getMessageId(), e.getMessage()); + LOGGER.error("Error was", e); + + // Report failure if we have a handler + if (this.failureHandler != null) { + // A failing failure handler is no reason to fail the batch + try { + this.failureHandler.accept(message, e); + } catch (Exception e2) { + LOGGER.warn("failureHandler threw handling failure", e2); + } + } + return Optional.of(SQSBatchResponse.BatchItemFailure.builder().withItemIdentifier(message.getMessageId()) + .build()); + } + } + + private boolean isFIFOEnabled(SQSEvent sqsEvent) { + return !sqsEvent.getRecords().isEmpty() + && sqsEvent.getRecords().get(0).getAttributes().get(MESSAGE_GROUP_ID_KEY) != null; + } } diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptor.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptor.java new file mode 100644 index 000000000..f49dbe5c5 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.batch.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-batch module is on the classpath. + */ +public final class BatchUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("batch"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/MultiThreadMDC.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/MultiThreadMDC.java new file mode 100644 index 000000000..b2b85044b --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/MultiThreadMDC.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.batch.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +/** + * MDC (SLF4J) is not passed to other threads (ThreadLocal). + * This class permits to manually copy the MDC to a given thread. + */ +public class MultiThreadMDC { + + private static final Logger LOGGER = LoggerFactory.getLogger(MultiThreadMDC.class); + + private final List<String> mdcAwareThreads = new ArrayList<>(); + private final Map<String, String> contextMap; + + public MultiThreadMDC() { + mdcAwareThreads.add("main"); + contextMap = MDC.getCopyOfContextMap(); + } + + public void copyMDCToThread(String thread) { + if (!mdcAwareThreads.contains(thread)) { + LOGGER.debug("Copy MDC to thread {}", thread); + MDC.setContextMap(contextMap); + mdcAwareThreads.add(thread); + } + } + + public void removeThread(String thread) { + if (mdcAwareThreads.contains(thread)) { + LOGGER.debug("Removing thread {}", thread); + mdcAwareThreads.remove(thread); + } + } +} diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java new file mode 100644 index 000000000..2858f4756 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.batch.internal; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class to propagate X-Ray trace entity context to worker threads using reflection. + * Reflection is used to avoid taking a dependency on X-RAY SDK. + */ +public final class XRayTraceEntityPropagator { + private static final Logger LOGGER = LoggerFactory.getLogger(XRayTraceEntityPropagator.class); + private static final boolean XRAY_AVAILABLE; + private static final Method GET_TRACE_ENTITY_METHOD; + + // We do the more "expensive" Class.forName in this static block to detect exactly once at import time if X-RAY + // is available or not. Subsequent <method>.invoke() are very fast on modern JDKs. + static { + Method method = null; + boolean available = false; + + try { + Class<?> awsXRayClass = Class.forName("com.amazonaws.xray.AWSXRay"); + method = awsXRayClass.getMethod("getTraceEntity"); + available = true; + LOGGER.debug("X-Ray SDK detected. Trace context will be propagated to worker threads."); + } catch (ClassNotFoundException | NoSuchMethodException e) { + LOGGER.debug("X-Ray SDK not detected. Trace context propagation disabled"); + } + + GET_TRACE_ENTITY_METHOD = method; + XRAY_AVAILABLE = available; + } + + private XRayTraceEntityPropagator() { + // Utility class + } + + public static Object captureTraceEntity() { + if (!XRAY_AVAILABLE) { + return null; + } + + try { + return GET_TRACE_ENTITY_METHOD.invoke(null); + } catch (Exception e) { + // We don't want to break batch processing if this fails. + LOGGER.warn("Failed to capture trace entity.", e); + return null; + } + } + + // See https://docs.aws.amazon.com/xray/latest/devguide/scorekeep-workerthreads.html + public static void runWithEntity(Object traceEntity, Runnable runnable) { + if (!XRAY_AVAILABLE || traceEntity == null) { + runnable.run(); + return; + } + + try { + traceEntity.getClass().getMethod("run", Runnable.class).invoke(traceEntity, runnable); + } catch (Exception e) { + // We don't want to break batch processing if this fails. + LOGGER.warn("Failed to run with trace entity, falling back to direct execution.", e); + runnable.run(); + } + } +} diff --git a/powertools-batch/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-batch/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..51764f87f --- /dev/null +++ b/powertools-batch/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.batch.internal.BatchUserAgentInterceptor diff --git a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java index 9e2c211e2..51131ae3f 100644 --- a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java @@ -20,18 +20,36 @@ import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import com.amazonaws.services.lambda.runtime.tests.annotations.Event; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; + +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedTest; import org.mockito.Mock; import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; -public class DdbBatchProcessorTest { +class DdbBatchProcessorTest { @Mock private Context context; + private final List<String> threadList = Collections.synchronizedList(new ArrayList<>()); + + @AfterEach + public void clear() { + threadList.clear(); + } + private void processMessageSucceeds(DynamodbEvent.DynamodbStreamRecord record, Context context) { // Great success + // Printing to satisfy pmd_analyse + System.out.println("Great success, record: " + record + ", context: " + context); } private void processMessageFailsForFixedMessage(DynamodbEvent.DynamodbStreamRecord record, Context context) { @@ -40,9 +58,55 @@ private void processMessageFailsForFixedMessage(DynamodbEvent.DynamodbStreamReco } } + private void processMessageInParallelSucceeds(DynamodbEvent.DynamodbStreamRecord record, Context context) { + String thread = Thread.currentThread().getName(); + if (!threadList.contains(thread)) { + threadList.add(thread); + } + try { + Thread.sleep(500); // simulate some processing + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void processMessageInParallelFailsForFixedMessage(DynamodbEvent.DynamodbStreamRecord record, Context context) { + String thread = Thread.currentThread().getName(); + if (!threadList.contains(thread)) { + threadList.add(thread); + } + try { + Thread.sleep(500); // simulate some processing + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (record.getDynamodb().getSequenceNumber().equals("4421584500000000017450439091")) { + throw new RuntimeException("fake exception"); + } + } + + private StreamsEventResponse testParallelBatchExecution(DynamodbEvent event, + BiConsumer<DynamodbEvent.DynamodbStreamRecord, Context> messageHandler, + Executor executor) { + // Arrange + BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(messageHandler); + + // Act + StreamsEventResponse dynamodbBatchResponse; + if (executor == null) { + dynamodbBatchResponse = handler.processBatchInParallel(event, context); + } else { + dynamodbBatchResponse = handler.processBatchInParallel(event, context, executor); + } + + return dynamodbBatchResponse; + } + @ParameterizedTest @Event(value = "dynamo_event.json", type = DynamodbEvent.class) - public void batchProcessingSucceedsAndReturns(DynamodbEvent event) { + void batchProcessingSucceedsAndReturns(DynamodbEvent event) { // Arrange BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() .withDynamoDbBatchHandler() @@ -52,12 +116,22 @@ public void batchProcessingSucceedsAndReturns(DynamodbEvent event) { StreamsEventResponse dynamodbBatchResponse = handler.processBatch(event, context); // Assert - assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(0); + assertThat(dynamodbBatchResponse.getBatchItemFailures()).isEmpty(); + } + + @ParameterizedTest + @Event(value = "dynamo_event_big.json", type = DynamodbEvent.class) + void parallelBatchProcessingSucceedsAndReturns(DynamodbEvent event) { + StreamsEventResponse dynamodbBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, null); + + // Assert + assertThat(dynamodbBatchResponse.getBatchItemFailures()).isEmpty(); + assertThat(threadList).hasSizeGreaterThan(1); } @ParameterizedTest @Event(value = "dynamo_event.json", type = DynamodbEvent.class) - public void shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEvent event) { + void shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEvent event) { // Arrange BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() .withDynamoDbBatchHandler() @@ -72,9 +146,21 @@ public void shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEve assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); } + @ParameterizedTest + @Event(value = "dynamo_event_big.json", type = DynamodbEvent.class) + void parallelBatchProcessing_shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEvent event) { + StreamsEventResponse dynamodbBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, null); + + // Assert + assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(1); + StreamsEventResponse.BatchItemFailure batchItemFailure = dynamodbBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); + assertThat(threadList).hasSizeGreaterThan(1); + } + @ParameterizedTest @Event(value = "dynamo_event.json", type = DynamodbEvent.class) - public void failingFailureHandlerShouldntFailBatch(DynamodbEvent event) { + void failingFailureHandlerShouldntFailBatch(DynamodbEvent event) { // Arrange AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() @@ -92,7 +178,7 @@ public void failingFailureHandlerShouldntFailBatch(DynamodbEvent event) { // Assert assertThat(dynamodbBatchResponse).isNotNull(); - assertThat(dynamodbBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(1); assertThat(wasCalledAndFailed.get()).isTrue(); StreamsEventResponse.BatchItemFailure batchItemFailure = dynamodbBatchResponse.getBatchItemFailures().get(0); assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); @@ -100,7 +186,7 @@ public void failingFailureHandlerShouldntFailBatch(DynamodbEvent event) { @ParameterizedTest @Event(value = "dynamo_event.json", type = DynamodbEvent.class) - public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(DynamodbEvent event) { + void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(DynamodbEvent event) { // Arrange AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() @@ -118,10 +204,38 @@ public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(DynamodbE // Assert assertThat(dynamodbBatchResponse).isNotNull(); - assertThat(dynamodbBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(1); assertThat(wasCalledAndFailed.get()).isTrue(); StreamsEventResponse.BatchItemFailure batchItemFailure = dynamodbBatchResponse.getBatchItemFailures().get(0); assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); } + @ParameterizedTest + @Event(value = "dynamo_event_big.json", type = DynamodbEvent.class) + void parallelBatchProcessingWithExecutorSucceedsAndReturns(DynamodbEvent event) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + StreamsEventResponse dynamodbBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, executor); + executor.shutdown(); + + // Assert + assertThat(dynamodbBatchResponse.getBatchItemFailures()).isEmpty(); + assertThat(threadList).hasSizeGreaterThan(1); + } + + @ParameterizedTest + @Event(value = "dynamo_event_big.json", type = DynamodbEvent.class) + void parallelBatchProcessingWithExecutor_shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEvent event) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + StreamsEventResponse dynamodbBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, executor); + executor.shutdown(); + + // Assert + assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(1); + StreamsEventResponse.BatchItemFailure batchItemFailure = dynamodbBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); + assertThat(threadList).hasSizeGreaterThan(1); + } + } diff --git a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/KinesisBatchProcessorTest.java b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/KinesisBatchProcessorTest.java index d78638e1d..32acde6f0 100644 --- a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/KinesisBatchProcessorTest.java +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/KinesisBatchProcessorTest.java @@ -20,17 +20,33 @@ import com.amazonaws.services.lambda.runtime.events.KinesisEvent; import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import com.amazonaws.services.lambda.runtime.tests.annotations.Event; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; + +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedTest; import org.mockito.Mock; import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; import software.amazon.lambda.powertools.batch.model.Product; -public class KinesisBatchProcessorTest { +class KinesisBatchProcessorTest { @Mock private Context context; + private final List<String> threadList = Collections.synchronizedList(new ArrayList<>()); + + @AfterEach + public void clear() { + threadList.clear(); + } + private void processMessageSucceeds(KinesisEvent.KinesisEventRecord record, Context context) { // Great success } @@ -42,6 +58,53 @@ private void processMessageFailsForFixedMessage(KinesisEvent.KinesisEventRecord } } + private void processMessageInParallelSucceeds(KinesisEvent.KinesisEventRecord record, Context context) { + String thread = Thread.currentThread().getName(); + if (!threadList.contains(thread)) { + threadList.add(thread); + } + try { + Thread.sleep(500); // simulate some processing + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void processMessageInParallelFailsForFixedMessage(KinesisEvent.KinesisEventRecord record, Context context) { + String thread = Thread.currentThread().getName(); + if (!threadList.contains(thread)) { + threadList.add(thread); + } + try { + Thread.sleep(500); // simulate some processing + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (record.getKinesis().getSequenceNumber() + .equals("49545115243490985018280067714973144582180062593244200961")) { + throw new RuntimeException("fake exception"); + } + } + + private StreamsEventResponse testParallelBatchExecution(KinesisEvent event, + BiConsumer<KinesisEvent.KinesisEventRecord, Context> messageHandler, + Executor executor) { + // Arrange + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithRawMessageHandler(messageHandler); + + // Act + StreamsEventResponse kinesisBatchResponse; + if (executor == null) { + kinesisBatchResponse = handler.processBatchInParallel(event, context); + } else { + kinesisBatchResponse = handler.processBatchInParallel(event, context, executor); + } + + return kinesisBatchResponse; + } + // A handler that throws an exception for _one_ of the deserialized products in the same messages public void processMessageFailsForFixedProduct(Product product, Context context) { if (product.getId() == 1234) { @@ -51,7 +114,7 @@ public void processMessageFailsForFixedProduct(Product product, Context context) @ParameterizedTest @Event(value = "kinesis_event.json", type = KinesisEvent.class) - public void batchProcessingSucceedsAndReturns(KinesisEvent event) { + void batchProcessingSucceedsAndReturns(KinesisEvent event) { // Arrange BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() .withKinesisBatchHandler() @@ -61,12 +124,22 @@ public void batchProcessingSucceedsAndReturns(KinesisEvent event) { StreamsEventResponse kinesisBatchResponse = handler.processBatch(event, context); // Assert - assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(0); + assertThat(kinesisBatchResponse.getBatchItemFailures()).isEmpty(); + } + + @ParameterizedTest + @Event(value = "kinesis_event_big.json", type = KinesisEvent.class) + void batchProcessingInParallelSucceedsAndReturns(KinesisEvent event) { + StreamsEventResponse kinesisBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, null); + + // Assert + assertThat(kinesisBatchResponse.getBatchItemFailures()).isEmpty(); + assertThat(threadList).hasSizeGreaterThan(1); } @ParameterizedTest @Event(value = "kinesis_event.json", type = KinesisEvent.class) - public void shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event) { + void shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event) { // Arrange BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() .withKinesisBatchHandler() @@ -82,9 +155,22 @@ public void shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEven "49545115243490985018280067714973144582180062593244200961"); } + @ParameterizedTest + @Event(value = "kinesis_event_big.json", type = KinesisEvent.class) + void batchProcessingInParallel_shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event) { + StreamsEventResponse kinesisBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, null); + + // Assert + assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(1); + StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( + "49545115243490985018280067714973144582180062593244200961"); + assertThat(threadList).hasSizeGreaterThan(1); + } + @ParameterizedTest @Event(value = "kinesis_event.json", type = KinesisEvent.class) - public void shouldAddMessageToBatchFailure_whenException_withProduct(KinesisEvent event) { + void shouldAddMessageToBatchFailure_whenException_withProduct(KinesisEvent event) { // Arrange BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() .withKinesisBatchHandler() @@ -102,7 +188,7 @@ public void shouldAddMessageToBatchFailure_whenException_withProduct(KinesisEven @ParameterizedTest @Event(value = "kinesis_event.json", type = KinesisEvent.class) - public void failingFailureHandlerShouldntFailBatch(KinesisEvent event) { + void failingFailureHandlerShouldntFailBatch(KinesisEvent event) { // Arrange AtomicBoolean wasCalled = new AtomicBoolean(false); BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() @@ -118,7 +204,7 @@ public void failingFailureHandlerShouldntFailBatch(KinesisEvent event) { // Assert assertThat(kinesisBatchResponse).isNotNull(); - assertThat(kinesisBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(1); assertThat(wasCalled.get()).isTrue(); StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( @@ -127,7 +213,7 @@ public void failingFailureHandlerShouldntFailBatch(KinesisEvent event) { @ParameterizedTest @Event(value = "kinesis_event.json", type = KinesisEvent.class) - public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(KinesisEvent event) { + void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(KinesisEvent event) { // Arrange AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() @@ -146,11 +232,40 @@ public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(KinesisEv // Assert assertThat(kinesisBatchResponse).isNotNull(); - assertThat(kinesisBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(1); assertThat(wasCalledAndFailed.get()).isTrue(); StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( "49545115243490985018280067714973144582180062593244200961"); } + @ParameterizedTest + @Event(value = "kinesis_event_big.json", type = KinesisEvent.class) + void batchProcessingInParallelWithExecutorSucceedsAndReturns(KinesisEvent event) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + StreamsEventResponse kinesisBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, executor); + executor.shutdown(); + + // Assert + assertThat(kinesisBatchResponse.getBatchItemFailures()).isEmpty(); + assertThat(threadList).hasSizeGreaterThan(1); + } + + @ParameterizedTest + @Event(value = "kinesis_event_big.json", type = KinesisEvent.class) + void batchProcessingInParallelWithExecutor_shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event) { + ExecutorService executor = Executors.newFixedThreadPool(2); + + StreamsEventResponse kinesisBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, executor); + executor.shutdown(); + + // Assert + assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(1); + StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( + "49545115243490985018280067714973144582180062593244200961"); + assertThat(threadList).hasSizeGreaterThan(1); + } + } diff --git a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/SQSBatchProcessorTest.java b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/SQSBatchProcessorTest.java index 2f9429fa3..f13196fc4 100644 --- a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/SQSBatchProcessorTest.java +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/SQSBatchProcessorTest.java @@ -20,20 +20,48 @@ import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.tests.annotations.Event; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; + +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedTest; import org.mockito.Mock; import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; import software.amazon.lambda.powertools.batch.model.Product; -public class SQSBatchProcessorTest { +class SQSBatchProcessorTest { @Mock private Context context; + private final List<String> threadList = Collections.synchronizedList(new ArrayList<>()); + + @AfterEach + public void clear() { + threadList.clear(); + } + // A handler that works private void processMessageSucceeds(SQSEvent.SQSMessage sqsMessage) { } + private void processMessageInParallelSucceeds(SQSEvent.SQSMessage sqsMessage, Context context) { + String thread = Thread.currentThread().getName(); + if (!threadList.contains(thread)) { + threadList.add(thread); + } + try { + Thread.sleep(500); // simulate some processing + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + // A handler that throws an exception for _one_ of the sample messages private void processMessageFailsForFixedMessage(SQSEvent.SQSMessage message, Context context) { if (message.getMessageId().equals("e9144555-9a4f-4ec3-99a0-34ce359b4b54")) { @@ -41,16 +69,50 @@ private void processMessageFailsForFixedMessage(SQSEvent.SQSMessage message, Con } } + private void processMessageInParallelFailsForFixedMessage(SQSEvent.SQSMessage message, Context context) { + String thread = Thread.currentThread().getName(); + if (!threadList.contains(thread)) { + threadList.add(thread); + } + try { + Thread.sleep(500); // simulate some processing + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (message.getMessageId().equals("e9144555-9a4f-4ec3-99a0-34ce359b4b54")) { + throw new RuntimeException("fake exception"); + } + } + // A handler that throws an exception for _one_ of the deserialized products in the same messages - public void processMessageFailsForFixedProduct(Product product, Context context) { + private void processMessageFailsForFixedProduct(Product product, Context context) { if (product.getId() == 12345) { throw new RuntimeException("fake exception"); } } + private SQSBatchResponse testParallelBatchExecution(SQSEvent event, + BiConsumer<SQSEvent.SQSMessage, Context> messageHandler, + Executor executor) { + // Arrange + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(messageHandler); + + // Act + SQSBatchResponse sqsBatchResponse; + if (executor == null) { + sqsBatchResponse = handler.processBatchInParallel(event, context); + } else { + sqsBatchResponse = handler.processBatchInParallel(event, context, executor); + } + + return sqsBatchResponse; + } + @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void batchProcessingSucceedsAndReturns(SQSEvent event) { + void batchProcessingSucceedsAndReturns(SQSEvent event) { // Arrange BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() .withSqsBatchHandler() @@ -60,13 +122,22 @@ public void batchProcessingSucceedsAndReturns(SQSEvent event) { SQSBatchResponse sqsBatchResponse = handler.processBatch(event, context); // Assert - assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(0); + assertThat(sqsBatchResponse.getBatchItemFailures()).isEmpty(); } + @ParameterizedTest + @Event(value = "sqs_event_big.json", type = SQSEvent.class) + void parallelBatchProcessingSucceedsAndReturns(SQSEvent event) { + SQSBatchResponse sqsBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, null); + + // Assert + assertThat(sqsBatchResponse.getBatchItemFailures()).isEmpty(); + assertThat(threadList).hasSizeGreaterThan(1); + } @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent event) { + void shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent event) { // Arrange BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() .withSqsBatchHandler() @@ -81,9 +152,21 @@ public void shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent ev assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); } + @ParameterizedTest + @Event(value = "sqs_event_big.json", type = SQSEvent.class) + void parallelBatchProcessing_shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent event) { + SQSBatchResponse sqsBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, null); + + // Assert + assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(1); + SQSBatchResponse.BatchItemFailure batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); + assertThat(threadList).hasSizeGreaterThan(1); + } + @ParameterizedTest @Event(value = "sqs_fifo_event.json", type = SQSEvent.class) - public void shouldAddMessageToBatchFailure_whenException_withSQSFIFO(SQSEvent event) { + void shouldAddMessageToBatchFailure_whenException_withSQSFIFO(SQSEvent event) { // Arrange BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() .withSqsBatchHandler() @@ -103,7 +186,7 @@ public void shouldAddMessageToBatchFailure_whenException_withSQSFIFO(SQSEvent ev @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void shouldAddMessageToBatchFailure_whenException_withProduct(SQSEvent event) { + void shouldAddMessageToBatchFailure_whenException_withProduct(SQSEvent event) { // Arrange BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() @@ -121,7 +204,7 @@ public void shouldAddMessageToBatchFailure_whenException_withProduct(SQSEvent ev @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void failingFailureHandlerShouldntFailBatch(SQSEvent event) { + void failingFailureHandlerShouldntFailBatch(SQSEvent event) { // Arrange AtomicBoolean wasCalled = new AtomicBoolean(false); BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() @@ -144,7 +227,7 @@ public void failingFailureHandlerShouldntFailBatch(SQSEvent event) { @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(SQSEvent event) { + void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(SQSEvent event) { // Arrange AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() @@ -167,5 +250,31 @@ public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(SQSEvent assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); } + @ParameterizedTest + @Event(value = "sqs_event_big.json", type = SQSEvent.class) + void parallelBatchProcessingWithExecutorSucceedsAndReturns(SQSEvent event) { + ExecutorService executor = Executors.newFixedThreadPool(2); + SQSBatchResponse sqsBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, executor); + executor.shutdown(); + + // Assert + assertThat(sqsBatchResponse.getBatchItemFailures()).isEmpty(); + assertThat(threadList).hasSizeGreaterThan(1); + } + + @ParameterizedTest + @Event(value = "sqs_event_big.json", type = SQSEvent.class) + void parallelBatchProcessingWithExecutor_shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent event) { + ExecutorService executor = Executors.newFixedThreadPool(2); + SQSBatchResponse sqsBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, executor); + executor.shutdown(); + + // Assert + assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(1); + SQSBatchResponse.BatchItemFailure batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); + assertThat(threadList).hasSizeGreaterThan(1); + } + } diff --git a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptorTest.java b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptorTest.java new file mode 100644 index 000000000..2841a1724 --- /dev/null +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.batch.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class BatchUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/BATCH/"); + } +} diff --git a/powertools-batch/src/test/resources/dynamo_event_big.json b/powertools-batch/src/test/resources/dynamo_event_big.json new file mode 100644 index 000000000..fa0a75c24 --- /dev/null +++ b/powertools-batch/src/test/resources/dynamo_event_big.json @@ -0,0 +1,376 @@ +{ + "Records": [ + { + "eventID": "c4ca4238a0b923820dcc509a6f75849b", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439001", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "userIdentity": { + "principalId": "dynamodb.amazonaws.com", + "type": "Service" + } + }, + { + "eventID": "c81e728d9d4c2f636f067f89cc14862c", + "eventName": "MODIFY", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439092", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3", + "eventName": "REMOVE", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439093", + "SizeBytes": 38, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "c4ca4238a0b923820dcc509a6f75849b", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439031", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "userIdentity": { + "principalId": "dynamodb.amazonaws.com", + "type": "Service" + } + }, + { + "eventID": "c81e728d9d4c2f636f067f89cc14862c", + "eventName": "MODIFY", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439092", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3", + "eventName": "REMOVE", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439093", + "SizeBytes": 38, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "c4ca4238a0b923820dcc509a6f75849b", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439001", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "userIdentity": { + "principalId": "dynamodb.amazonaws.com", + "type": "Service" + } + }, + { + "eventID": "c81e728d9d4c2f636f067f89cc14862c", + "eventName": "MODIFY", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439092", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3", + "eventName": "REMOVE", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439093", + "SizeBytes": 38, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "c4ca4238a0b923820dcc509a6f75849b", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439031", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "userIdentity": { + "principalId": "dynamodb.amazonaws.com", + "type": "Service" + } + }, + { + "eventID": "c81e728d9d4c2f636f067f89cc14862c", + "eventName": "MODIFY", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439091", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3", + "eventName": "REMOVE", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1428537600, + "SequenceNumber": "4421584500000000017450439093", + "SizeBytes": 38, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + } + ] +} \ No newline at end of file diff --git a/powertools-batch/src/test/resources/kinesis_event_big.json b/powertools-batch/src/test/resources/kinesis_event_big.json new file mode 100644 index 000000000..57f702d27 --- /dev/null +++ b/powertools-batch/src/test/resources/kinesis_event_big.json @@ -0,0 +1,224 @@ +{ + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200962", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200962", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200963", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200963", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200964", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200964", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200965", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200965", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + },{ + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200966", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200966", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200967", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200967", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200968", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200968", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200969", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200969", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200971", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200971", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200981", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200981", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200992", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200992", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244210961", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244210961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + } + ] +} \ No newline at end of file diff --git a/powertools-batch/src/test/resources/sqs_event_big.json b/powertools-batch/src/test/resources/sqs_event_big.json new file mode 100644 index 000000000..f5c83f442 --- /dev/null +++ b/powertools-batch/src/test/resources/sqs_event_big.json @@ -0,0 +1,429 @@ +{ + "Records": [ + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1234,\n \"name\": \"product\",\n \"price\": 42\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "f9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "14e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1235,\n \"name\": \"product\",\n \"price\": 43\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "14e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "g9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "15e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1236,\n \"name\": \"product\",\n \"price\": 44\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "15e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "c9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "16e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1237,\n \"name\": \"product\",\n \"price\": 45\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "16e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "b4144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1238,\n \"name\": \"product\",\n \"price\": 486\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "a2144552-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "14e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1239,\n \"name\": \"product\",\n \"price\": 430\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "14e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "32144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "15e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1240,\n \"name\": \"product\",\n \"price\": 445\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "15e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "a9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "16e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1241,\n \"name\": \"product\",\n \"price\": 45\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "16e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b354", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1242,\n \"name\": \"product\",\n \"price\": 42\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "e9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "14e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1243,\n \"name\": \"product\",\n \"price\": 43\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "14e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "19144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "15e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1244,\n \"name\": \"product\",\n \"price\": 44\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "15e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "c9144555-9a4f-4ec3-99a0-34ce3a9b4b54", + "receiptHandle": "16e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1245,\n \"name\": \"product\",\n \"price\": 45\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "16e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "b4144555-9a4f-4ec3-99a5-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1247,\n \"name\": \"product\",\n \"price\": 486\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "a2144555-9a4f-4ec3-97a0-34ce359b4b54", + "receiptHandle": "14e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1248,\n \"name\": \"product\",\n \"price\": 430\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "14e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "3k144555-9a4f-4ec2-99a0-34ce359b4b54", + "receiptHandle": "15e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1249,\n \"name\": \"product\",\n \"price\": 445\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "15e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "h9144555-9aaf-4ec3-99a0-34ce359b4b54", + "receiptHandle": "16e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1250,\n \"name\": \"product\",\n \"price\": 45\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "16e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1234,\n \"name\": \"product\",\n \"price\": 42\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "f9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "14e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1235,\n \"name\": \"product\",\n \"price\": 43\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "14e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "g9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "15e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1236,\n \"name\": \"product\",\n \"price\": 44\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "15e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "c9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "16e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1237,\n \"name\": \"product\",\n \"price\": 45\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "16e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "b4144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1238,\n \"name\": \"product\",\n \"price\": 486\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "a2144552-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "14e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1239,\n \"name\": \"product\",\n \"price\": 430\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "14e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "32144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "15e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1240,\n \"name\": \"product\",\n \"price\": 445\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "15e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "a9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "16e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1241,\n \"name\": \"product\",\n \"price\": 45\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "16e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b354", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1242,\n \"name\": \"product\",\n \"price\": 42\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975706495", + "SenderId": "AROAIFU437PVZ5L2J53F5", + "ApproximateFirstReceiveTimestamp": "1601975706499" + }, + "messageAttributes": { + }, + "md5OfBody": "13e7f7851d2eaa5c01f208ebadbf1e72", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + } + ] +} \ No newline at end of file diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index a122e7ac8..cb06dc1f3 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -14,8 +14,8 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>powertools-cloudformation</artifactId> @@ -24,36 +24,14 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> - <name>Powertools for AWS Lambda (Java)library Cloudformation</name> + <name>Powertools for AWS Lambda (Java) - Cloudformation</name> <description> A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda (Java) Team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> <dependencies> <dependency> @@ -64,6 +42,10 @@ <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> @@ -80,6 +62,11 @@ <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + </dependency> <!-- Test dependencies --> <dependency> @@ -102,24 +89,112 @@ <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>com.github.tomakehurst</groupId> - <artifactId>wiremock-jre8</artifactId> + <groupId>org.wiremock</groupId> + <artifactId>wiremock</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> <scope>test</scope> </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-cloudformation</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--enable-url-protocols=http</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> - </plugins> + <resources> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> </build> -</project> \ No newline at end of file +</project> diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java index 2f020aa25..cf6fad827 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java @@ -36,6 +36,7 @@ import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.utils.StringInputStream; +import software.amazon.awssdk.utils.StringUtils; /** * Client for sending responses to AWS CloudFormation custom resources by way of a response URL, which is an Amazon S3 @@ -45,7 +46,7 @@ * <p> * This class is thread-safe provided the SdkHttpClient instance used is also thread-safe. */ -class CloudFormationResponse { +public class CloudFormationResponse { private static final Logger LOG = LoggerFactory.getLogger(CloudFormationResponse.class); private final SdkHttpClient client; @@ -148,7 +149,9 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, ObjectNode node = body.toObjectNode(null); return new StringInputStream(node.toString()); } else { - + if (!StringUtils.isBlank(resp.getReason())) { + reason = resp.getReason(); + } String physicalResourceId = resp.getPhysicalResourceId() != null ? resp.getPhysicalResourceId() : event.getPhysicalResourceId() != null ? event.getPhysicalResourceId() : context.getLogStreamName(); diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java index fe18000d4..8c782d957 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import software.amazon.awssdk.utils.StringUtils; /** * Models the arbitrary data to be sent to the custom resource in response to a CloudFormation event. This object @@ -30,12 +31,22 @@ public class Response { private final Status status; private final String physicalResourceId; private final boolean noEcho; + private final String reason; private Response(JsonNode jsonNode, Status status, String physicalResourceId, boolean noEcho) { this.jsonNode = jsonNode; this.status = status; this.physicalResourceId = physicalResourceId; this.noEcho = noEcho; + this.reason = null; + } + + private Response(JsonNode jsonNode, Status status, String physicalResourceId, boolean noEcho, String reason) { + this.jsonNode = jsonNode; + this.status = status; + this.physicalResourceId = physicalResourceId; + this.noEcho = noEcho; + this.reason = reason; } /** @@ -47,23 +58,6 @@ public static Builder builder() { return new Builder(); } - /** - * Creates a failed Response with no physicalResourceId set. Powertools for AWS Lambda (Java) will set the physicalResourceId to the - * Lambda LogStreamName - * <p> - * The value returned for a PhysicalResourceId can change custom resource update operations. If the value returned - * is the same, it is considered a normal update. If the value returned is different, AWS CloudFormation recognizes - * the update as a replacement and sends a delete request to the old resource. For more information, - * see AWS::CloudFormation::CustomResource. - * - * @return a failed Response with no value. - * @deprecated this method is not safe. Provide a physicalResourceId. - */ - @Deprecated - public static Response failed() { - return new Response(null, Status.FAILED, null, false); - } - /** * Creates a failed Response with a given physicalResourceId. * @@ -80,23 +74,6 @@ public static Response failed(String physicalResourceId) { return new Response(null, Status.FAILED, physicalResourceId, false); } - /** - * Creates a successful Response with no physicalResourceId set. Powertools for AWS Lambda (Java) will set the physicalResourceId to the - * Lambda LogStreamName - * <p> - * The value returned for a PhysicalResourceId can change custom resource update operations. If the value returned - * is the same, it is considered a normal update. If the value returned is different, AWS CloudFormation recognizes - * the update as a replacement and sends a delete request to the old resource. For more information, - * see AWS::CloudFormation::CustomResource. - * - * @return a success Response with no physicalResourceId value. - * @deprecated this method is not safe. Provide a physicalResourceId. - */ - @Deprecated - public static Response success() { - return new Response(null, Status.SUCCESS, null, false); - } - /** * Creates a successful Response with a given physicalResourceId. * @@ -149,6 +126,15 @@ public boolean isNoEcho() { return noEcho; } + /** + * The reason for the failure. + * + * @return a potentially null reason + */ + public String getReason() { + return reason; + } + /** * Includes all Response attributes, including its value in JSON format * @@ -161,6 +147,7 @@ public String toString() { attributes.put("Status", status); attributes.put("PhysicalResourceId", physicalResourceId); attributes.put("NoEcho", noEcho); + attributes.put("Reason", reason); return attributes.entrySet().stream() .map(entry -> entry.getKey() + " = " + entry.getValue()) .collect(Collectors.joining(",", "[", "]")); @@ -182,6 +169,7 @@ public static class Builder { private Status status; private String physicalResourceId; private boolean noEcho; + private String reason; private Builder() { } @@ -263,6 +251,20 @@ public Builder noEcho(boolean noEcho) { return this; } + /** + * Reason for the response. + * Reason is optional for Success responses, but required for Failed responses. + * If not provided it will be replaced with cloudwatch log stream name. + * + * @param reason if null, the default reason will be used + * @return a reference to this builder + */ + + public Builder reason(String reason) { + this.reason = reason; + return this; + } + /** * Builds a Response object for the value. * @@ -277,6 +279,9 @@ public Response build() { node = mapper.valueToTree(value); } Status responseStatus = this.status != null ? this.status : Status.SUCCESS; + if (StringUtils.isNotBlank(this.reason)) { + return new Response(node, responseStatus, physicalResourceId, noEcho, reason); + } return new Response(node, responseStatus, physicalResourceId, noEcho); } } diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptor.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptor.java new file mode 100644 index 000000000..c225512d1 --- /dev/null +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.cloudformation.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-cloudformation module is on the classpath. + */ +public final class CloudformationUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("cloudformation"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/reflect-config.json b/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/reflect-config.json new file mode 100644 index 000000000..092ef9b54 --- /dev/null +++ b/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/reflect-config.json @@ -0,0 +1,436 @@ +[ +{ + "name":"[Lcom.fasterxml.jackson.databind.deser.BeanDeserializerModifier;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.deser.Deserializers;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.deser.KeyDeserializers;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.deser.ValueInstantiators;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.ser.BeanSerializerModifier;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.ser.Serializers;" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.RequestHandler", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"canEqual","parameterTypes":["java.lang.Object"] }, {"name":"getLogicalResourceId","parameterTypes":[] }, {"name":"getOldResourceProperties","parameterTypes":[] }, {"name":"getPhysicalResourceId","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }, {"name":"getRequestType","parameterTypes":[] }, {"name":"getResourceProperties","parameterTypes":[] }, {"name":"getResourceType","parameterTypes":[] }, {"name":"getResponseUrl","parameterTypes":[] }, {"name":"getServiceToken","parameterTypes":[] }, {"name":"getStackId","parameterTypes":[] }, {"name":"setLogicalResourceId","parameterTypes":["java.lang.String"] }, {"name":"setOldResourceProperties","parameterTypes":["java.util.Map"] }, {"name":"setPhysicalResourceId","parameterTypes":["java.lang.String"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setRequestType","parameterTypes":["java.lang.String"] }, {"name":"setResourceProperties","parameterTypes":["java.util.Map"] }, {"name":"setResourceType","parameterTypes":["java.lang.String"] }, {"name":"setResponseUrl","parameterTypes":["java.lang.String"] }, {"name":"setServiceToken","parameterTypes":["java.lang.String"] }, {"name":"setStackId","parameterTypes":["java.lang.String"] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.jayway.jsonpath.spi.cache.CacheProvider", + "fields":[{"name":"cache"}] +}, +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ARCFOURCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESedeCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.FileNotFoundException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Boolean", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.Byte", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPackageName","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Cloneable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Double", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.Float", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.Integer", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.Long", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addOpens","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.Short", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.Thread", + "fields":[{"name":"threadLocalRandomProbe"}], + "methods":[{"name":"isVirtual","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }, {"name":"privateLookupIn","parameterTypes":["java.lang.Class","java.lang.invoke.MethodHandles$Lookup"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.management.ManagementFactory", + "methods":[{"name":"getRuntimeMXBean","parameterTypes":[] }] +}, +{ + "name":"java.lang.management.RuntimeMXBean", + "methods":[{"name":"getInputArguments","parameterTypes":[] }, {"name":"getUptime","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedParameterizedType", + "methods":[{"name":"getAnnotatedActualTypeArguments","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.security.AlgorithmParametersSpi" +}, +{ + "name":"java.security.KeyStoreSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.HashSet", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"java.util.concurrent.Callable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.util.concurrent.Executors", + "methods":[{"name":"newVirtualThreadPerTaskExecutor","parameterTypes":[] }] +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.Striped64", + "fields":[{"name":"base"}, {"name":"cellsBusy"}] +}, +{ + "name":"java.util.function.Consumer", + "queryAllPublicMethods":true +}, +{ + "name":"javax.security.auth.x500.X500Principal", + "fields":[{"name":"thisX500Name"}], + "methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.Metadata" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.eclipse.jetty.http.pathmap.PathSpecSet", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.eclipse.jetty.servlets.CrossOriginFilter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.eclipse.jetty.util.AsciiLowerCaseSet", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.eclipse.jetty.util.TypeUtil", + "methods":[{"name":"getClassLoaderLocation","parameterTypes":["java.lang.Class"] }, {"name":"getCodeSourceLocation","parameterTypes":["java.lang.Class"] }, {"name":"getModuleLocation","parameterTypes":["java.lang.Class"] }, {"name":"getSystemClassLoaderLocation","parameterTypes":["java.lang.Class"] }] +}, +{ + "name":"software.amazon.awssdk.http.Abortable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"abort","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.http.ExecutableHttpRequest", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"call","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.http.HttpExecuteResponse", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"httpResponse","parameterTypes":[] }, {"name":"responseBody","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.http.SdkHttpClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"clientName","parameterTypes":[] }, {"name":"prepareRequest","parameterTypes":["software.amazon.awssdk.http.HttpExecuteRequest"] }] +}, +{ + "name":"software.amazon.awssdk.utils.SdkAutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"close","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"onSendFailure","parameterTypes":["com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent","com.amazonaws.services.lambda.runtime.Context","software.amazon.lambda.powertools.cloudformation.Response","java.lang.Exception"] }] +}, +{ + "name":"software.amazon.lambda.powertools.cloudformation.CloudFormationResponse", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"headers","parameterTypes":["int"] }, {"name":"send","parameterTypes":["com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent","com.amazonaws.services.lambda.runtime.Context"] }, {"name":"send","parameterTypes":["com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent","com.amazonaws.services.lambda.runtime.Context","software.amazon.lambda.powertools.cloudformation.Response"] }] +}, +{ + "name":"software.amazon.lambda.powertools.cloudformation.CloudFormationResponse$ResponseBody", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getLogicalResourceId","parameterTypes":[] }, {"name":"getPhysicalResourceId","parameterTypes":[] }, {"name":"getReason","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }, {"name":"getStackId","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }, {"name":"isNoEcho","parameterTypes":[] }] +}, +{ + "name":"sun.misc.SharedSecrets" +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.NetscapeCertTypeExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.PrivateKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"software.amazon.lambda.powertools.cloudformation.internal.CloudformationUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/resource-config.json b/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/resource-config.json new file mode 100644 index 000000000..cbbfb270d --- /dev/null +++ b/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/resource-config.json @@ -0,0 +1,43 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.util.spi.ResourceBundleControlProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qassets/swagger-ui/index.html\\E" + }, { + "pattern":"\\Qassets\\E" + }, { + "pattern":"\\Qhelpers.nashorn.js\\E" + }, { + "pattern":"\\Qkeystore\\E" + }, { + "pattern":"\\Qorg/apache/hc/client5/version.properties\\E" + }, { + "pattern":"\\Qorg/eclipse/jetty/http/encoding.properties\\E" + }, { + "pattern":"\\Qorg/eclipse/jetty/http/mime.properties\\E" + }, { + "pattern":"\\Qorg/eclipse/jetty/version/build.properties\\E" + }, { + "pattern":"\\Qorg/publicsuffix/list/effective_tld_names.dat\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }]}, + "bundles":[{ + "name":"jakarta.servlet.LocalStrings", + "locales":[""] + }, { + "name":"jakarta.servlet.http.LocalStrings", + "locales":[""] + }] +} diff --git a/powertools-cloudformation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-cloudformation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..172ca1d2c --- /dev/null +++ b/powertools-cloudformation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.cloudformation.internal.CloudformationUserAgentInterceptor diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java index 1e399ef6f..9d0669d43 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java @@ -16,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; @@ -25,14 +24,17 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; import java.io.IOException; + import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + import software.amazon.awssdk.http.SdkHttpClient; -import software.amazon.lambda.powertools.cloudformation.Response.Status; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; public class AbstractCustomResourceHandlerTest { @@ -68,11 +70,11 @@ void defaultAndCustomSdkHttpClients() { } @ParameterizedTest - @CsvSource(value = {"Create,1,0,0", "Update,0,1,0", "Delete,0,0,1"}, delimiter = ',') + @CsvSource(value = { "Create,1,0,0", "Update,0,1,0", "Delete,0,0,1" }, delimiter = ',') void eventsDelegateToCorrectHandlerMethod(String eventType, int createCount, int updateCount, int deleteCount) { AbstractCustomResourceHandler handler = spy(new NoOpCustomResourceHandler()); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); handler.handleRequest(eventOfType(eventType), context); verify(handler, times(createCount)).create(any(), eq(context)); @@ -84,7 +86,7 @@ void eventsDelegateToCorrectHandlerMethod(String eventType, int createCount, int void eventOfUnknownRequestTypeSendEmptySuccess() { AbstractCustomResourceHandler handler = spy(new NoOpCustomResourceHandler()); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); CloudFormationCustomResourceEvent event = eventOfType("UNKNOWN"); handler.handleRequest(event, context); @@ -96,16 +98,9 @@ void eventOfUnknownRequestTypeSendEmptySuccess() { @Test void defaultStatusResponseSendsSuccess() { - ExpectedStatusResourceHandler handler = spy(new ExpectedStatusResourceHandler(Status.SUCCESS) { - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return Response.builder() - .value("whatever") - .build(); - } - }); - - Context context = mock(Context.class); + SuccessResponseHandler handler = spy(new SuccessResponseHandler()); + + Context context = new TestLambdaContext(); CloudFormationCustomResourceEvent event = eventOfType("Create"); Response response = handler.handleRequest(event, context); @@ -116,17 +111,9 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte @Test void explicitResponseWithStatusSuccessSendsSuccess() { - ExpectedStatusResourceHandler handler = spy(new ExpectedStatusResourceHandler(Status.SUCCESS) { - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return Response.builder() - .value("whatever") - .status(Status.SUCCESS) - .build(); - } - }); - - Context context = mock(Context.class); + ExplicitSuccessResponseHandler handler = spy(new ExplicitSuccessResponseHandler()); + + Context context = new TestLambdaContext(); CloudFormationCustomResourceEvent event = eventOfType("Create"); Response response = handler.handleRequest(event, context); @@ -137,17 +124,9 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte @Test void explicitResponseWithStatusFailedSendsFailure() { - ExpectedStatusResourceHandler handler = spy(new ExpectedStatusResourceHandler(Status.FAILED) { - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return Response.builder() - .value("whatever") - .status(Status.FAILED) - .build(); - } - }); - - Context context = mock(Context.class); + FailedResponseHandler handler = spy(new FailedResponseHandler()); + + Context context = new TestLambdaContext(); CloudFormationCustomResourceEvent event = eventOfType("Create"); Response response = handler.handleRequest(event, context); @@ -158,14 +137,9 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte @Test void exceptionWhenGeneratingResponseSendsFailure() { - ExpectedStatusResourceHandler handler = spy(new ExpectedStatusResourceHandler(Status.FAILED) { - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - throw new RuntimeException("This exception is intentional for testing"); - } - }); - - Context context = mock(Context.class); + ExceptionThrowingHandler handler = spy(new ExceptionThrowingHandler()); + + Context context = new TestLambdaContext(); CloudFormationCustomResourceEvent event = eventOfType("Create"); Response response = handler.handleRequest(event, context); @@ -178,14 +152,9 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte @Test void exceptionWhenSendingResponseInvokesOnSendFailure() { // a custom handler that builds response successfully but fails to send it - FailToSendResponseHandler handler = spy(new FailToSendResponseHandler() { - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return Response.builder().value("Failure happens on send").build(); - } - }); - - Context context = mock(Context.class); + SuccessfulSendHandler handler = spy(new SuccessfulSendHandler()); + + Context context = new TestLambdaContext(); CloudFormationCustomResourceEvent event = eventOfType("Create"); Response response = handler.handleRequest(event, context); @@ -197,15 +166,11 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte @Test void bothResponseGenerationAndSendFail() { - // a custom handler that fails to build response _and_ fails to send a FAILED response - FailToSendResponseHandler handler = spy(new FailToSendResponseHandler() { - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - throw new RuntimeException("This exception is intentional for testing"); - } - }); - - Context context = mock(Context.class); + // a custom handler that fails to build response _and_ fails to send a FAILED + // response + FailedSendHandler handler = spy(new FailedSendHandler()); + + Context context = new TestLambdaContext(); CloudFormationCustomResourceEvent event = eventOfType("Create"); Response response = handler.handleRequest(event, context); @@ -214,91 +179,4 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte .onSendFailure(eq(event), eq(context), isNull(), any(IOException.class)); } - /** - * Bare-bones implementation that returns null for abstract methods. - */ - static class NullCustomResourceHandler extends AbstractCustomResourceHandler { - NullCustomResourceHandler() { - } - - NullCustomResourceHandler(SdkHttpClient client) { - super(client); - } - - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return null; - } - - @Override - protected Response update(CloudFormationCustomResourceEvent event, Context context) { - return null; - } - - @Override - protected Response delete(CloudFormationCustomResourceEvent event, Context context) { - return null; - } - } - - /** - * Uses a mocked CloudFormationResponse to avoid sending actual HTTP requests. - */ - static class NoOpCustomResourceHandler extends NullCustomResourceHandler { - - NoOpCustomResourceHandler() { - super(mock(SdkHttpClient.class)); - } - - @Override - protected CloudFormationResponse buildResponseClient() { - return mock(CloudFormationResponse.class); - } - } - - /** - * Creates a handler that will expect the Response to be sent with an expected status. Will throw an AssertionError - * if the method is sent with an unexpected status. - */ - static class ExpectedStatusResourceHandler extends NoOpCustomResourceHandler { - private final Status expectedStatus; - - ExpectedStatusResourceHandler(Status expectedStatus) { - this.expectedStatus = expectedStatus; - } - - @Override - protected CloudFormationResponse buildResponseClient() { - // create a CloudFormationResponse that fails if invoked with unexpected status - CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); - try { - when(cfnResponse.send(any(), any(), argThat(resp -> resp.getStatus() != expectedStatus))) - .thenThrow(new AssertionError("Expected response's status to be " + expectedStatus)); - } catch (IOException | CustomResourceResponseException e) { - // this should never happen - throw new RuntimeException("Unexpected mocking exception", e); - } - return cfnResponse; - } - } - - /** - * Always fails to send the response - */ - static class FailToSendResponseHandler extends NoOpCustomResourceHandler { - @Override - protected CloudFormationResponse buildResponseClient() { - CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); - try { - when(cfnResponse.send(any(), any())) - .thenThrow(new IOException("Intentional send failure")); - when(cfnResponse.send(any(), any(), any())) - .thenThrow(new IOException("Intentional send failure")); - } catch (IOException | CustomResourceResponseException e) { - // this should never happen - throw new RuntimeException("Unexpected mocking exception", e); - } - return cfnResponse; - } - } } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java index ce45d3afc..316913bf2 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java @@ -23,62 +23,40 @@ import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.assertj.core.api.Assertions.assertThat; -import com.amazonaws.services.lambda.runtime.ClientContext; -import com.amazonaws.services.lambda.runtime.CognitoIdentity; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.LambdaLogger; -import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; import java.util.UUID; + import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; + +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; + import software.amazon.lambda.powertools.cloudformation.handlers.NoPhysicalResourceIdSetHandler; import software.amazon.lambda.powertools.cloudformation.handlers.PhysicalResourceIdSetHandler; import software.amazon.lambda.powertools.cloudformation.handlers.RuntimeExceptionThrownHandler; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; @WireMockTest public class CloudFormationIntegrationTest { public static final String PHYSICAL_RESOURCE_ID = UUID.randomUUID().toString(); - public static final String LOG_STREAM_NAME = "FakeLogStreamName"; - - private static CloudFormationCustomResourceEvent updateEventWithPhysicalResourceId(int httpPort, - String physicalResourceId) { - CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = baseEvent(httpPort); - - builder.withPhysicalResourceId(physicalResourceId); - builder.withRequestType("Update"); - - return builder.build(); - } - - private static CloudFormationCustomResourceEvent deleteEventWithPhysicalResourceId(int httpPort, - String physicalResourceId) { - CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = baseEvent(httpPort); - - builder.withPhysicalResourceId(physicalResourceId); - builder.withRequestType("Delete"); - - return builder.build(); - } + public static final String LOG_STREAM_NAME = "test-log-stream"; private static CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder baseEvent(int httpPort) { - CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = - CloudFormationCustomResourceEvent.builder() - .withResponseUrl("http://localhost:" + httpPort + "/") - .withStackId("123") - .withRequestId("234") - .withLogicalResourceId("345"); - - return builder; + return CloudFormationCustomResourceEvent + .builder() + .withResponseUrl("http://localhost:" + httpPort + "/") + .withStackId("123") + .withRequestId("234") + .withLogicalResourceId("345"); } @ParameterizedTest - @ValueSource(strings = {"Update", "Delete"}) + @ValueSource(strings = { "Update", "Delete" }) void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(String requestType, - WireMockRuntimeInfo wmRuntimeInfo) { + WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); @@ -89,18 +67,17 @@ void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(St .withRequestType(requestType) .build(); - handler.handleRequest(event, new FakeContext()); + handler.handleRequest(event, new TestLambdaContext()); verify(putRequestedFor(urlPathMatching("/")) .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) - .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]")) - ); + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]"))); } @ParameterizedTest - @ValueSource(strings = {"Update", "Delete"}) + @ValueSource(strings = { "Update", "Delete" }) void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDeleting(String requestType, - WireMockRuntimeInfo wmRuntimeInfo) { + WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); RuntimeExceptionThrownHandler handler = new RuntimeExceptionThrownHandler(); @@ -111,12 +88,11 @@ void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDele .withRequestType(requestType) .build(); - handler.handleRequest(event, new FakeContext()); + handler.handleRequest(event, new TestLambdaContext()); verify(putRequestedFor(urlPathMatching("/")) .withRequestBody(matchingJsonPath("[?(@.Status == 'FAILED')]")) - .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]")) - ); + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]"))); } @Test @@ -127,16 +103,15 @@ void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMo CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) .withRequestType("Create") .build(); - handler.handleRequest(createEvent, new FakeContext()); + handler.handleRequest(createEvent, new TestLambdaContext()); verify(putRequestedFor(urlPathMatching("/")) .withRequestBody(matchingJsonPath("[?(@.Status == 'FAILED')]")) - .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + LOG_STREAM_NAME + "')]")) - ); + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + LOG_STREAM_NAME + "')]"))); } @ParameterizedTest - @ValueSource(strings = {"Update", "Delete"}) + @ValueSource(strings = { "Update", "Delete" }) void physicalResourceIdSetFromRequestOnUpdateOrDeleteWhenCustomerDoesntProvideAPhysicalResourceId( String requestType, WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); @@ -149,13 +124,12 @@ void physicalResourceIdSetFromRequestOnUpdateOrDeleteWhenCustomerDoesntProvideAP .withRequestType(requestType) .build(); - Response response = handler.handleRequest(event, new FakeContext()); + Response response = handler.handleRequest(event, new TestLambdaContext()); assertThat(response).isNotNull(); verify(putRequestedFor(urlPathMatching("/")) .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) - .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]")) - ); + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]"))); } @Test @@ -166,17 +140,16 @@ void createNewResourceBecausePhysicalResourceIdNotSetByCustomerOnCreate(WireMock CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) .withRequestType("Create") .build(); - Response response = handler.handleRequest(createEvent, new FakeContext()); + Response response = handler.handleRequest(createEvent, new TestLambdaContext()); assertThat(response).isNotNull(); verify(putRequestedFor(urlPathMatching("/")) .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) - .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + LOG_STREAM_NAME + "')]")) - ); + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + LOG_STREAM_NAME + "')]"))); } @ParameterizedTest - @ValueSource(strings = {"Create", "Update", "Delete"}) + @ValueSource(strings = { "Create", "Update", "Delete" }) void physicalResourceIdReturnedFromSuccessToCloudformation(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { String physicalResourceId = UUID.randomUUID().toString(); @@ -185,17 +158,16 @@ void physicalResourceIdReturnedFromSuccessToCloudformation(String requestType, W CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) .withRequestType(requestType) .build(); - Response response = handler.handleRequest(createEvent, new FakeContext()); + Response response = handler.handleRequest(createEvent, new TestLambdaContext()); assertThat(response).isNotNull(); verify(putRequestedFor(urlPathMatching("/")) .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) - .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + physicalResourceId + "')]")) - ); + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + physicalResourceId + "')]"))); } @ParameterizedTest - @ValueSource(strings = {"Create", "Update", "Delete"}) + @ValueSource(strings = { "Create", "Update", "Delete" }) void physicalResourceIdReturnedFromFailedToCloudformation(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { String physicalResourceId = UUID.randomUUID().toString(); @@ -204,69 +176,12 @@ void physicalResourceIdReturnedFromFailedToCloudformation(String requestType, Wi CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) .withRequestType(requestType) .build(); - Response response = handler.handleRequest(createEvent, new FakeContext()); + Response response = handler.handleRequest(createEvent, new TestLambdaContext()); assertThat(response).isNotNull(); verify(putRequestedFor(urlPathMatching("/")) .withRequestBody(matchingJsonPath("[?(@.Status == 'FAILED')]")) - .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + physicalResourceId + "')]")) - ); + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + physicalResourceId + "')]"))); } - private static class FakeContext implements Context { - @Override - public String getAwsRequestId() { - return null; - } - - @Override - public String getLogGroupName() { - return null; - } - - @Override - public String getLogStreamName() { - return LOG_STREAM_NAME; - } - - @Override - public String getFunctionName() { - return null; - } - - @Override - public String getFunctionVersion() { - return null; - } - - @Override - public String getInvokedFunctionArn() { - return null; - } - - @Override - public CognitoIdentity getIdentity() { - return null; - } - - @Override - public ClientContext getClientContext() { - return null; - } - - @Override - public int getRemainingTimeInMillis() { - return 0; - } - - @Override - public int getMemoryLimitInMB() { - return 0; - } - - @Override - public LambdaLogger getLogger() { - return null; - } - } } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java index 51f0e95f9..0cc65f884 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java @@ -20,15 +20,18 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; -import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.io.InputStream; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; + import org.junit.jupiter.api.Test; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import com.fasterxml.jackson.databind.node.ObjectNode; + import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.ExecutableHttpRequest; import software.amazon.awssdk.http.HttpExecuteRequest; @@ -37,11 +40,13 @@ import software.amazon.awssdk.utils.IoUtils; import software.amazon.awssdk.utils.StringInputStream; import software.amazon.lambda.powertools.cloudformation.CloudFormationResponse.ResponseBody; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; -public class CloudFormationResponseTest { +class CloudFormationResponseTest { /** - * Creates a mock CloudFormationCustomResourceEvent with a non-null response URL. + * Creates a mock CloudFormationCustomResourceEvent with a non-null response + * URL. */ static CloudFormationCustomResourceEvent mockCloudFormationCustomResourceEvent() { CloudFormationCustomResourceEvent event = mock(CloudFormationCustomResourceEvent.class); @@ -50,15 +55,15 @@ static CloudFormationCustomResourceEvent mockCloudFormationCustomResourceEvent() } /** - * Creates a CloudFormationResponse that does not make actual HTTP requests. The HTTP response body is the request + * Creates a CloudFormationResponse that does not make actual HTTP requests. The + * HTTP response body is the request * body. */ static CloudFormationResponse testableCloudFormationResponse() { SdkHttpClient client = mock(SdkHttpClient.class); ExecutableHttpRequest executableRequest = mock(ExecutableHttpRequest.class); - when(client.prepareRequest(any(HttpExecuteRequest.class))).thenAnswer(args -> - { + when(client.prepareRequest(any(HttpExecuteRequest.class))).thenAnswer(args -> { HttpExecuteRequest request = args.getArgument(0, HttpExecuteRequest.class); assertThat(request.contentStreamProvider()).isPresent(); @@ -89,7 +94,7 @@ void eventRequiredToSend() { SdkHttpClient client = mock(SdkHttpClient.class); CloudFormationResponse response = new CloudFormationResponse(client); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); assertThatThrownBy(() -> response.send(null, context)) .isInstanceOf(CustomResourceResponseException.class); } @@ -99,7 +104,7 @@ void contextRequiredToSend() { SdkHttpClient client = mock(SdkHttpClient.class); CloudFormationResponse response = new CloudFormationResponse(client); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); assertThatThrownBy(() -> response.send(null, context)) .isInstanceOf(CustomResourceResponseException.class); } @@ -110,8 +115,9 @@ void eventResponseUrlRequiredToSend() { CloudFormationResponse response = new CloudFormationResponse(client); CloudFormationCustomResourceEvent event = mock(CloudFormationCustomResourceEvent.class); - Context context = mock(Context.class); - // not a CustomResourceResponseException since the URL is not part of the response but + Context context = new TestLambdaContext(); + // not a CustomResourceResponseException since the URL is not part of the + // response but // rather the location the response is sent to assertThatThrownBy(() -> response.send(event, context)) .isInstanceOf(RuntimeException.class); @@ -122,8 +128,7 @@ void customPhysicalResponseId() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); when(event.getPhysicalResourceId()).thenReturn("This-Is-Ignored"); - Context context = mock(Context.class); - when(context.getLogStreamName()).thenReturn("My-Log-Stream-Name"); + Context context = new TestLambdaContext(); String customPhysicalResourceId = "Custom-Physical-Resource-ID"; ResponseBody body = new ResponseBody( @@ -135,7 +140,7 @@ void customPhysicalResponseId() { @Test void responseBodyWithNullDataNode() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); @@ -143,7 +148,7 @@ void responseBodyWithNullDataNode() { String expectedJson = "{" + "\"Status\":\"FAILED\"," + - "\"Reason\":\"See the details in CloudWatch Log Stream: null\"," + + "\"Reason\":\"See the details in CloudWatch Log Stream: test-log-stream\"," + "\"PhysicalResourceId\":null," + "\"StackId\":null," + "\"RequestId\":null," + @@ -157,7 +162,7 @@ void responseBodyWithNullDataNode() { @Test void responseBodyWithNonNullDataNode() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); ObjectNode dataNode = ResponseBody.MAPPER.createObjectNode(); dataNode.put("foo", "bar"); dataNode.put("baz", 10); @@ -168,7 +173,7 @@ void responseBodyWithNonNullDataNode() { String expectedJson = "{" + "\"Status\":\"FAILED\"," + - "\"Reason\":\"See the details in CloudWatch Log Stream: null\"," + + "\"Reason\":\"See the details in CloudWatch Log Stream: test-log-stream\"," + "\"PhysicalResourceId\":null," + "\"StackId\":null," + "\"RequestId\":null," + @@ -182,7 +187,7 @@ void responseBodyWithNonNullDataNode() { @Test void defaultStatusIsSuccess() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); ResponseBody body = new ResponseBody( event, null, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); @@ -192,7 +197,7 @@ void defaultStatusIsSuccess() { @Test void customStatus() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); ResponseBody body = new ResponseBody( event, Response.Status.FAILED, null, false, @@ -203,21 +208,18 @@ void customStatus() { @Test void reasonIncludesLogStreamName() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - - String logStreamName = "My-Log-Stream-Name"; - Context context = mock(Context.class); - when(context.getLogStreamName()).thenReturn(logStreamName); + Context context = new TestLambdaContext(); ResponseBody body = new ResponseBody( event, Response.Status.SUCCESS, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); - assertThat(body.getReason()).contains(logStreamName); + assertThat(body.getReason()).contains(context.getLogStreamName()); } @Test - public void sendWithNoResponseData() throws Exception { + void sendWithNoResponseData() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); HttpExecuteResponse response = cfnResponse.send(event, context); @@ -225,8 +227,8 @@ public void sendWithNoResponseData() throws Exception { String actualJson = responseAsString(response); String expectedJson = "{" + "\"Status\":\"SUCCESS\"," + - "\"Reason\":\"See the details in CloudWatch Log Stream: null\"," + - "\"PhysicalResourceId\":null," + + "\"Reason\":\"See the details in CloudWatch Log Stream: test-log-stream\"," + + "\"PhysicalResourceId\":\"test-log-stream\"," + "\"StackId\":null," + "\"RequestId\":null," + "\"LogicalResourceId\":null," + @@ -237,9 +239,9 @@ public void sendWithNoResponseData() throws Exception { } @Test - public void sendWithNonNullResponseData() throws Exception { + void sendWithNonNullResponseData() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); Map<String, String> responseData = new LinkedHashMap<>(); @@ -251,8 +253,8 @@ public void sendWithNonNullResponseData() throws Exception { String actualJson = responseAsString(response); String expectedJson = "{" + "\"Status\":\"SUCCESS\"," + - "\"Reason\":\"See the details in CloudWatch Log Stream: null\"," + - "\"PhysicalResourceId\":null," + + "\"Reason\":\"See the details in CloudWatch Log Stream: test-log-stream\"," + + "\"PhysicalResourceId\":\"test-log-stream\"," + "\"StackId\":null," + "\"RequestId\":null," + "\"LogicalResourceId\":null," + @@ -265,15 +267,15 @@ public void sendWithNonNullResponseData() throws Exception { @Test void responseBodyStreamNullResponseDefaultsToSuccessStatus() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); StringInputStream stream = cfnResponse.responseBodyStream(event, context, null); String expectedJson = "{" + "\"Status\":\"SUCCESS\"," + - "\"Reason\":\"See the details in CloudWatch Log Stream: null\"," + - "\"PhysicalResourceId\":null," + + "\"Reason\":\"See the details in CloudWatch Log Stream: test-log-stream\"," + + "\"PhysicalResourceId\":\"test-log-stream\"," + "\"StackId\":null," + "\"RequestId\":null," + "\"LogicalResourceId\":null," + @@ -286,15 +288,15 @@ void responseBodyStreamNullResponseDefaultsToSuccessStatus() throws Exception { @Test void responseBodyStreamSuccessResponse() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); - StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.success()); + StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.success(null)); String expectedJson = "{" + "\"Status\":\"SUCCESS\"," + - "\"Reason\":\"See the details in CloudWatch Log Stream: null\"," + - "\"PhysicalResourceId\":null," + + "\"Reason\":\"See the details in CloudWatch Log Stream: test-log-stream\"," + + "\"PhysicalResourceId\":\"test-log-stream\"," + "\"StackId\":null," + "\"RequestId\":null," + "\"LogicalResourceId\":null," + @@ -307,15 +309,38 @@ void responseBodyStreamSuccessResponse() throws Exception { @Test void responseBodyStreamFailedResponse() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); - StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.failed()); + StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.failed(null)); String expectedJson = "{" + "\"Status\":\"FAILED\"," + - "\"Reason\":\"See the details in CloudWatch Log Stream: null\"," + - "\"PhysicalResourceId\":null," + + "\"Reason\":\"See the details in CloudWatch Log Stream: test-log-stream\"," + + "\"PhysicalResourceId\":\"test-log-stream\"," + + "\"StackId\":null," + + "\"RequestId\":null," + + "\"LogicalResourceId\":null," + + "\"NoEcho\":false," + + "\"Data\":null" + + "}"; + assertThat(stream.getString()).isEqualTo(expectedJson); + } + + @Test + void responseBodyStreamFailedResponseWithReason() throws Exception { + CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); + Context context = new TestLambdaContext(); + CloudFormationResponse cfnResponse = testableCloudFormationResponse(); + String failureReason = "Failed test reason"; + Response failedResponseWithReason = Response.builder().status(Response.Status.FAILED).reason(failureReason) + .build(); + StringInputStream stream = cfnResponse.responseBodyStream(event, context, failedResponseWithReason); + + String expectedJson = "{" + + "\"Status\":\"FAILED\"," + + "\"Reason\":\"" + failureReason + "\"," + + "\"PhysicalResourceId\":\"test-log-stream\"," + "\"StackId\":null," + "\"RequestId\":null," + "\"LogicalResourceId\":null," + diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExceptionThrowingHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExceptionThrowingHandler.java new file mode 100644 index 000000000..dd2d1c853 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExceptionThrowingHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + +import software.amazon.lambda.powertools.cloudformation.Response.Status; + +public class ExceptionThrowingHandler extends ExpectedStatusResourceHandler { + public ExceptionThrowingHandler() { + super(Status.FAILED); + } + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + throw new RuntimeException("This exception is intentional for testing"); + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExpectedStatusResourceHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExpectedStatusResourceHandler.java new file mode 100644 index 000000000..7992c712c --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExpectedStatusResourceHandler.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import org.mockito.ArgumentMatcher; + +import software.amazon.lambda.powertools.cloudformation.Response.Status; + +/** + * Creates a handler that will expect the Response to be sent with an expected + * status. Will throw an AssertionError + * if the method is sent with an unexpected status. + */ +public class ExpectedStatusResourceHandler extends NoOpCustomResourceHandler { + private final Status expectedStatus; + + public ExpectedStatusResourceHandler(Status expectedStatus) { + this.expectedStatus = expectedStatus; + } + + @Override + CloudFormationResponse buildResponseClient() { + // create a CloudFormationResponse that fails if invoked with unexpected status + CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); + try { + when(cfnResponse.send(any(), any(), org.mockito.ArgumentMatchers.argThat(new ArgumentMatcher<Response>() { + @Override + public boolean matches(Response resp) { + return resp != null && resp.getStatus() != expectedStatus; + } + }))).thenThrow(new AssertionError("Expected response's status to be " + expectedStatus)); + } catch (IOException | CustomResourceResponseException e) { + // this should never happen + throw new RuntimeException("Unexpected mocking exception", e); + } + return cfnResponse; + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExplicitSuccessResponseHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExplicitSuccessResponseHandler.java new file mode 100644 index 000000000..2b11f8020 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExplicitSuccessResponseHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + +import software.amazon.lambda.powertools.cloudformation.Response.Status; + +public class ExplicitSuccessResponseHandler extends ExpectedStatusResourceHandler { + public ExplicitSuccessResponseHandler() { + super(Status.SUCCESS); + } + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return Response.builder().value("whatever").status(Status.SUCCESS).build(); + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailToSendResponseHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailToSendResponseHandler.java new file mode 100644 index 000000000..218994c56 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailToSendResponseHandler.java @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +/** + * Always fails to send the response + */ +public class FailToSendResponseHandler extends NoOpCustomResourceHandler { + @Override + CloudFormationResponse buildResponseClient() { + CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); + try { + when(cfnResponse.send(any(), any())) + .thenThrow(new IOException("Intentional send failure")); + when(cfnResponse.send(any(), any(), any())) + .thenThrow(new IOException("Intentional send failure")); + } catch (IOException | CustomResourceResponseException e) { + // this should never happen + throw new RuntimeException("Unexpected mocking exception", e); + } + return cfnResponse; + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedResponseHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedResponseHandler.java new file mode 100644 index 000000000..787713535 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedResponseHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + +import software.amazon.lambda.powertools.cloudformation.Response.Status; + +public class FailedResponseHandler extends ExpectedStatusResourceHandler { + public FailedResponseHandler() { + super(Status.FAILED); + } + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return Response.builder().value("whatever").status(Status.FAILED).build(); + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedSendHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedSendHandler.java new file mode 100644 index 000000000..fe88d8895 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedSendHandler.java @@ -0,0 +1,25 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + +public class FailedSendHandler extends FailToSendResponseHandler { + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + throw new RuntimeException("This exception is intentional for testing"); + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NoOpCustomResourceHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NoOpCustomResourceHandler.java new file mode 100644 index 000000000..0271c36f5 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NoOpCustomResourceHandler.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import static org.mockito.Mockito.mock; + +import software.amazon.awssdk.http.SdkHttpClient; + +/** + * Uses a mocked CloudFormationResponse to avoid sending actual HTTP requests. + */ +public class NoOpCustomResourceHandler extends NullCustomResourceHandler { + + public NoOpCustomResourceHandler() { + super(mock(SdkHttpClient.class)); + } + + @Override + CloudFormationResponse buildResponseClient() { + return mock(CloudFormationResponse.class); + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NullCustomResourceHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NullCustomResourceHandler.java new file mode 100644 index 000000000..a44e2d57e --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NullCustomResourceHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + +import software.amazon.awssdk.http.SdkHttpClient; + +/** + * Bare-bones implementation that returns null for abstract methods. + */ +public class NullCustomResourceHandler extends AbstractCustomResourceHandler { + public NullCustomResourceHandler() { + } + + public NullCustomResourceHandler(SdkHttpClient client) { + super(client); + } + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return null; + } + + @Override + protected Response update(CloudFormationCustomResourceEvent event, Context context) { + return null; + } + + @Override + protected Response delete(CloudFormationCustomResourceEvent event, Context context) { + return null; + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java index 37fe73d0f..726bcbeee 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java @@ -16,13 +16,15 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategies; import java.util.HashMap; import java.util.Map; + import org.junit.jupiter.api.Test; -public class ResponseTest { +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; + +class ResponseTest { @Test void defaultValues() { @@ -33,11 +35,13 @@ void defaultValues() { assertThat(response.getStatus()).isEqualTo(Response.Status.SUCCESS); assertThat(response.getPhysicalResourceId()).isNull(); assertThat(response.isNoEcho()).isFalse(); + assertThat(response.getReason()).isNull(); assertThat(response.toString()).contains("JSON = null"); assertThat(response.toString()).contains("Status = SUCCESS"); assertThat(response.toString()).contains("PhysicalResourceId = null"); assertThat(response.toString()).contains("NoEcho = false"); + assertThat(response.toString()).contains("Reason = null"); } @Test @@ -61,6 +65,27 @@ void explicitNullValues() { assertThat(response.toString()).contains("NoEcho = false"); } + @Test + void explicitReasonWithDefaultValues() { + String reason = "test"; + Response response = Response.builder() + .reason(reason) + .build(); + assertThat(response).isNotNull(); + assertThat(response.getJsonNode()).isNull(); + assertThat(response.getStatus()).isEqualTo(Response.Status.SUCCESS); + assertThat(response.getPhysicalResourceId()).isNull(); + assertThat(response.isNoEcho()).isFalse(); + assertThat(response.getReason()).isNotNull(); + assertThat(response.getReason()).isEqualTo(reason); + + assertThat(response.toString()).contains("JSON = null"); + assertThat(response.toString()).contains("Status = SUCCESS"); + assertThat(response.toString()).contains("PhysicalResourceId = null"); + assertThat(response.toString()).contains("NoEcho = false"); + assertThat(response.toString()).contains("Reason = " + reason); + } + @Test void customNonJsonRelatedValues() { Response response = Response.builder() @@ -92,7 +117,7 @@ void jsonMapValueWithDefaultObjectMapper() { String expected = "{\"foo\":\"bar\"}"; assertThat(response.getJsonNode()).isNotNull(); - assertThat(response.getJsonNode().toString()).isEqualTo(expected); + assertThat(response.getJsonNode()).hasToString(expected); assertThat(response.toString()).contains("JSON = " + expected); } @@ -105,7 +130,7 @@ void jsonObjectValueWithDefaultObjectMapper() { .build(); String expected = "{\"PropertyWithLongName\":\"test\"}"; - assertThat(response.getJsonNode().toString()).isEqualTo(expected); + assertThat(response.getJsonNode()).hasToString(expected); assertThat(response.toString()).contains("JSON = " + expected); } @@ -119,7 +144,7 @@ void jsonObjectValueWithNullObjectMapper() { .build(); String expected = "{\"PropertyWithLongName\":\"test\"}"; - assertThat(response.getJsonNode().toString()).isEqualTo(expected); + assertThat(response.getJsonNode()).hasToString(expected); assertThat(response.toString()).contains("JSON = " + expected); } @@ -135,7 +160,7 @@ void jsonObjectValueWithCustomObjectMapper() { .build(); String expected = "{\"property-with-long-name\":10}"; - assertThat(response.getJsonNode().toString()).isEqualTo(expected); + assertThat(response.getJsonNode()).hasToString(expected); assertThat(response.toString()).contains("JSON = " + expected); } @@ -154,13 +179,13 @@ void jsonObjectValueWithPostConfiguredObjectMapper() { customMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); String expected = "{\"property-with-long-name\":10}"; - assertThat(response.getJsonNode().toString()).isEqualTo(expected); + assertThat(response.getJsonNode()).hasToString(expected); assertThat(response.toString()).contains("JSON = " + expected); } @Test void successFactoryMethod() { - Response response = Response.success(); + Response response = Response.success(null); assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(Response.Status.SUCCESS); @@ -168,7 +193,7 @@ void successFactoryMethod() { @Test void failedFactoryMethod() { - Response response = Response.failed(); + Response response = Response.failed(null); assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(Response.Status.FAILED); diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessResponseHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessResponseHandler.java new file mode 100644 index 000000000..18538bc9d --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessResponseHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + +import software.amazon.lambda.powertools.cloudformation.Response.Status; + +public class SuccessResponseHandler extends ExpectedStatusResourceHandler { + public SuccessResponseHandler() { + super(Status.SUCCESS); + } + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return Response.builder().value("whatever").build(); + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessfulSendHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessfulSendHandler.java new file mode 100644 index 000000000..074d1499e --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessfulSendHandler.java @@ -0,0 +1,25 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.cloudformation; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + +public class SuccessfulSendHandler extends FailToSendResponseHandler { + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return Response.builder().value("Failure happens on send").build(); + } +} diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java index 2bbda309f..4fb14110c 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java @@ -16,6 +16,7 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler; import software.amazon.lambda.powertools.cloudformation.Response; @@ -23,16 +24,16 @@ public class NoPhysicalResourceIdSetHandler extends AbstractCustomResourceHandle @Override protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return Response.success(); + return Response.success(null); } @Override protected Response update(CloudFormationCustomResourceEvent event, Context context) { - return Response.success(); + return Response.success(null); } @Override protected Response delete(CloudFormationCustomResourceEvent event, Context context) { - return Response.success(); + return Response.success(null); } } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java index c6bd56b76..a01319342 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java @@ -16,6 +16,7 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler; import software.amazon.lambda.powertools.cloudformation.Response; diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java index d5a11e895..10e3801c2 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java @@ -16,6 +16,7 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; + import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler; import software.amazon.lambda.powertools.cloudformation.Response; diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptorTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptorTest.java new file mode 100644 index 000000000..ee500d006 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.cloudformation.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class CloudformationUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/CLOUDFORMATION/"); + } +} diff --git a/powertools-cloudformation/src/test/resources/simplelogger.properties b/powertools-cloudformation/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..e8ba3a5fa --- /dev/null +++ b/powertools-cloudformation/src/test/resources/simplelogger.properties @@ -0,0 +1,7 @@ +org.slf4j.simpleLogger.logFile=target/cloudformation-test.log +org.slf4j.simpleLogger.defaultLogLevel=warn +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS +org.slf4j.simpleLogger.showThreadName=false +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml new file mode 100644 index 000000000..75ef10beb --- /dev/null +++ b/powertools-common/pom.xml @@ -0,0 +1,189 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2023 Amazon.com, Inc. or its affiliates. + ~ Licensed under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <artifactId>powertools-common</artifactId> + <packaging>jar</packaging> + + <parent> + <artifactId>powertools-parent</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>2.9.0</version> + </parent> + + <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> + <description>Internal utilities shared by the Powertools for AWS Lambda (Java) modules. Do not use directly in your project.</description> + + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>utils-lite</artifactId> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-common</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <resources> + <resource> + <directory>src/main/resources-filtered</directory> + <filtering>true</filtering> + </resource> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java new file mode 100644 index 000000000..ca599429e --- /dev/null +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java @@ -0,0 +1,91 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.common.internal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.net.URL; +import java.net.URLConnection; +import java.util.Enumeration; + +/** + * Used to preload classes to support automatic priming for SnapStart + */ +public final class ClassPreLoader { + public static final String CLASSES_FILE = "classesloaded.txt"; + + private ClassPreLoader() { + // Hide default constructor + } + + /** + * Initializes the classes listed in the classesloaded resource + */ + public static void preloadClasses() { + try { + Enumeration<URL> files = ClassPreLoader.class.getClassLoader().getResources(CLASSES_FILE); + // If there are multiple files, preload classes from all of them + while (files.hasMoreElements()) { + URL url = files.nextElement(); + URLConnection conn = url.openConnection(); + conn.setUseCaches(false); + InputStream is = conn.getInputStream(); + preloadClassesFromStream(is); + } + } catch (IOException ignored) { + // No action is required if preloading fails for any reason + } + } + + /** + * Loads the list of classes passed as a stream + * + * @param is + */ + private static void preloadClassesFromStream(InputStream is) { + try (is; + InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); + BufferedReader reader = new BufferedReader(isr)) { + String line; + while ((line = reader.readLine()) != null) { + int idx = line.indexOf('#'); + if (idx != -1) { + line = line.substring(0, idx); + } + final String className = line.stripTrailing(); + if (!className.isBlank()) { + loadClassIfFound(className); + } + } + } catch (Exception ignored) { + // No action is required if preloading fails for any reason + } + } + + /** + * Initializes the class with given name if found, ignores otherwise + * + * @param className + */ + private static void loadClassIfFound(String className) { + try { + Class.forName(className, true, ClassPreLoader.class.getClassLoader()); + } catch (ClassNotFoundException e) { + // No action is required if the class with given name cannot be found + } + } +} \ No newline at end of file diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java similarity index 74% rename from powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java rename to powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java index d0f94260b..4c4e8e9db 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java @@ -12,20 +12,22 @@ * */ -package software.amazon.lambda.powertools.core.internal; +package software.amazon.lambda.powertools.common.internal; + +public final class LambdaConstants { + private LambdaConstants() { + // Constant holder class + } -public class LambdaConstants { public static final String LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME"; public static final String AWS_REGION_ENV = "AWS_REGION"; - // Also you can use AWS_LAMBDA_INITIALIZATION_TYPE to distinguish between on-demand and SnapStart initialization - // it's not recommended to use this env variable to initialize SDK clients or other resources. - @Deprecated - public static final String AWS_LAMBDA_INITIALIZATION_TYPE = "AWS_LAMBDA_INITIALIZATION_TYPE"; - @Deprecated - public static final String ON_DEMAND = "on-demand"; public static final String X_AMZN_TRACE_ID = "_X_AMZN_TRACE_ID"; + public static final String XRAY_TRACE_HEADER = "com.amazonaws.xray.traceHeader"; + public static final String AWS_LAMBDA_X_TRACE_ID = "AWS_LAMBDA_X_TRACE_ID"; public static final String AWS_SAM_LOCAL = "AWS_SAM_LOCAL"; public static final String ROOT_EQUALS = "Root="; public static final String POWERTOOLS_SERVICE_NAME = "POWERTOOLS_SERVICE_NAME"; public static final String SERVICE_UNDEFINED = "service_undefined"; + public static final String AWS_LAMBDA_INITIALIZATION_TYPE = "AWS_LAMBDA_INITIALIZATION_TYPE"; + public static final String ON_DEMAND_INVOCATION_TYPE = "on-demand"; } diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java similarity index 62% rename from powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java rename to powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java index e9e220e41..15bff15d6 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java @@ -12,26 +12,31 @@ * */ -package software.amazon.lambda.powertools.core.internal; +package software.amazon.lambda.powertools.common.internal; import static java.util.Optional.empty; import static java.util.Optional.of; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; +import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getProperty; +import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import java.io.InputStream; import java.io.OutputStream; import java.util.Optional; + import org.aspectj.lang.ProceedingJoinPoint; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; + +import software.amazon.awssdk.utilslite.SdkInternalThreadLocal; + public final class LambdaHandlerProcessor { - // SERVICE_NAME cannot be final for testing purposes - private static String SERVICE_NAME = calculateServiceName(); + // serviceName cannot be final for testing purposes + private static String serviceName = calculateServiceName(); - private static Boolean IS_COLD_START = null; + private static Boolean isColdStart = null; private LambdaHandlerProcessor() { // Hide default constructor @@ -39,13 +44,21 @@ private LambdaHandlerProcessor() { private static String calculateServiceName() { return null != getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME) - ? getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME) : LambdaConstants.SERVICE_UNDEFINED; + ? getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME) + : LambdaConstants.SERVICE_UNDEFINED; } public static boolean isHandlerMethod(final ProceedingJoinPoint pjp) { return placedOnRequestHandler(pjp) || placedOnStreamHandler(pjp); } + /** + * The class needs to implement RequestHandler interface + * The function needs to have exactly two arguments + * The second argument needs to be of type com.amazonaws.services.lambda.runtime.Context + * @param pjp + * @return + */ public static boolean placedOnRequestHandler(final ProceedingJoinPoint pjp) { return RequestHandler.class.isAssignableFrom(pjp.getSignature().getDeclaringType()) && pjp.getArgs().length == 2 @@ -61,7 +74,6 @@ public static boolean placedOnStreamHandler(final ProceedingJoinPoint pjp) { } public static Context extractContext(final ProceedingJoinPoint pjp) { - if (placedOnRequestHandler(pjp)) { return (Context) pjp.getArgs()[1]; } else if (placedOnStreamHandler(pjp)) { @@ -72,20 +84,27 @@ public static Context extractContext(final ProceedingJoinPoint pjp) { } public static String serviceName() { - return SERVICE_NAME; + return serviceName; } // Method used for testing purposes protected static void resetServiceName() { - SERVICE_NAME = calculateServiceName(); + serviceName = calculateServiceName(); } public static boolean isColdStart() { - return IS_COLD_START == null; + if (isColdStart != null) { + return isColdStart; + } + + String initType = System.getenv(LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE); + isColdStart = LambdaConstants.ON_DEMAND_INVOCATION_TYPE.equals(initType); + + return isColdStart; } public static void coldStartDone() { - IS_COLD_START = false; + isColdStart = false; } public static boolean isSamLocal() { @@ -93,9 +112,20 @@ public static boolean isSamLocal() { } public static Optional<String> getXrayTraceId() { - final String X_AMZN_TRACE_ID = getenv(LambdaConstants.X_AMZN_TRACE_ID); - if (X_AMZN_TRACE_ID != null) { - return of(X_AMZN_TRACE_ID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")); + // Try SdkInternalThreadLocal first + String traceId = SdkInternalThreadLocal.get(LambdaConstants.AWS_LAMBDA_X_TRACE_ID); + + // Fallback to environment based approach + if (traceId == null) { + traceId = getenv(LambdaConstants.X_AMZN_TRACE_ID); + } + // For the Java Lambda 17+ runtime, the Trace ID is set as a System Property + if (traceId == null) { + traceId = getProperty(LambdaConstants.XRAY_TRACE_HEADER); + } + + if (traceId != null) { + return of(traceId.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")); } return empty(); } diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/SystemWrapper.java similarity index 82% rename from powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java rename to powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/SystemWrapper.java index 30f72232f..6dc4e9d9f 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/SystemWrapper.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.core.internal; +package software.amazon.lambda.powertools.common.internal; public class SystemWrapper { private SystemWrapper() { @@ -21,4 +21,8 @@ private SystemWrapper() { public static String getenv(String name) { return System.getenv(name); } + + public static String getProperty(String name) { + return System.getProperty(name); + } } diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/UserAgentConfigurator.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java similarity index 52% rename from powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/UserAgentConfigurator.java rename to powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java index 354305d33..27b69d5ad 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/UserAgentConfigurator.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java @@ -12,33 +12,33 @@ * */ -package software.amazon.lambda.powertools.core.internal; +package software.amazon.lambda.powertools.common.internal; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; +import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; -import java.io.FileInputStream; import java.io.IOException; -import java.net.URL; +import java.io.InputStream; +import java.util.Locale; import java.util.Properties; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** * Can be used to create a string that can server as a User-Agent suffix in requests made with the AWS SDK clients */ -public class UserAgentConfigurator { - +public final class UserAgentConfigurator { public static final String NA = "NA"; public static final String VERSION_KEY = "powertools.version"; public static final String PT_FEATURE_VARIABLE = "${PT_FEATURE}"; public static final String PT_EXEC_ENV_VARIABLE = "${PT_EXEC_ENV}"; public static final String VERSION_PROPERTIES_FILENAME = "version.properties"; public static final String AWS_EXECUTION_ENV = "AWS_EXECUTION_ENV"; + private static final String SDK_USER_AGENT_APP_ID = "sdk.ua.appId"; private static final Logger LOG = LoggerFactory.getLogger(UserAgentConfigurator.class); private static final String NO_OP = "no-op"; - private static String ptVersion = getProjectVersion(); - private static String userAgentPattern = "PT/" + PT_FEATURE_VARIABLE + "/" + ptVersion + " PTEnv/" + private static final String POWERTOOLS_VERSION = getProjectVersion(); + private static final String USER_AGENT_PATTERN = "PT/" + PT_FEATURE_VARIABLE + "/" + POWERTOOLS_VERSION + " PTENV/" + PT_EXEC_ENV_VARIABLE; private UserAgentConfigurator() { @@ -54,7 +54,6 @@ static String getProjectVersion() { return getVersionFromProperties(VERSION_PROPERTIES_FILENAME, VERSION_KEY); } - /** * Retrieves the project version from a properties file. * The file should be in the resources folder. @@ -65,27 +64,73 @@ static String getProjectVersion() { * @return the version of the project as configured in the given properties file */ static String getVersionFromProperties(String propertyFileName, String versionKey) { - - URL propertiesFileURI = Thread.currentThread().getContextClassLoader().getResource(propertyFileName); - if (propertiesFileURI != null) { - try (FileInputStream fis = new FileInputStream(propertiesFileURI.getPath())) { + try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(propertyFileName)) { + if (is != null) { Properties properties = new Properties(); - properties.load(fis); + properties.load(is); String version = properties.getProperty(versionKey); if (version != null && !version.isEmpty()) { return version; } - } catch (IOException e) { - LOG.warn("Unable to read {} file. Using default version.", propertyFileName); - LOG.debug("Exception:", e); } + } catch (IOException e) { + LOG.warn("Unable to read {} file. Using default version.", propertyFileName); + LOG.debug("Exception:", e); } return NA; } + /** + * Configures the AWS SDK to use Powertools user agent by setting the sdk.ua.appId system property. + * Preserves any user-provided value and replaces any existing Powertools user agent. + * Enforces a 50 character limit to comply with AWS SDK recommendations. + * This should be called during library initialization to ensure the user agent is properly configured. + */ + public static void configureUserAgent(String ptFeature) { + try { + String existingValue = System.getProperty(SDK_USER_AGENT_APP_ID); + String powertoolsUserAgent = getUserAgent(ptFeature); + String newValue; + + if (existingValue == null || existingValue.isEmpty()) { + newValue = powertoolsUserAgent; + } else { + String userValue = extractUserValue(existingValue); + if (userValue.isEmpty()) { + newValue = powertoolsUserAgent; + } else { + newValue = userValue + "/" + powertoolsUserAgent; + } + } + + if (newValue.length() <= 50) { + System.setProperty(SDK_USER_AGENT_APP_ID, newValue); + } + } catch (Exception e) { + // We don't re-raise since we don't want to break the user if something in this logic doesn't work + LOG.warn("Unable to configure user agent system property", e); + } + } + + /** + * Extracts the user-provided value from the existing user agent string by removing any Powertools user agent. + * A Powertools user agent follows the pattern "PT/{FEATURE}/{VERSION} PTENV/{ENV}". + * + * @param existingValue the existing user agent string + * @return the user-provided value without Powertools user agent, or empty string if none exists + */ + static String extractUserValue(String existingValue) { + if (existingValue == null || existingValue.isEmpty()) { + return ""; + } + // Remove Powertools user agent pattern: PT/{FEATURE}/{VERSION} PTENV/{ENV} + String result = existingValue.replaceAll("/?PT/[^/]+/[^ ]+ PTENV/[^ ]+", ""); + return result.trim(); + } + /** * Retrieves the user agent string for the Powertools for AWS Lambda. - * It follows the pattern PT/{PT_FEATURE}/{PT_VERSION} PTEnv/{PT_EXEC_ENV} + * It follows the pattern PT/{PT_FEATURE}/{PT_VERSION} PTENV/{PT_EXEC_ENV} * The version of the project is automatically retrieved. * The PT_EXEC_ENV is automatically retrieved from the AWS_EXECUTION_ENV environment variable. * If it AWS_EXECUTION_ENV is not set, PT_EXEC_ENV defaults to "NA" @@ -96,16 +141,15 @@ static String getVersionFromProperties(String propertyFileName, String versionKe * @return the user agent string */ public static String getUserAgent(String ptFeature) { - String awsExecutionEnv = getenv(AWS_EXECUTION_ENV); String ptExecEnv = awsExecutionEnv != null ? awsExecutionEnv : NA; - String userAgent = userAgentPattern.replace(PT_EXEC_ENV_VARIABLE, ptExecEnv); + String userAgent = USER_AGENT_PATTERN.replace(PT_EXEC_ENV_VARIABLE, ptExecEnv); if (ptFeature == null || ptFeature.isEmpty()) { ptFeature = NO_OP; } return userAgent - .replace(PT_FEATURE_VARIABLE, ptFeature) + .replace(PT_FEATURE_VARIABLE, ptFeature.toUpperCase(Locale.ROOT)) .replace(PT_EXEC_ENV_VARIABLE, ptExecEnv); } } diff --git a/powertools-core/src/main/resources-filtered/version.properties b/powertools-common/src/main/resources-filtered/version.properties similarity index 100% rename from powertools-core/src/main/resources-filtered/version.properties rename to powertools-common/src/main/resources-filtered/version.properties diff --git a/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/jni-config.json b/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/jni-config.json new file mode 100644 index 000000000..8ea90d67f --- /dev/null +++ b/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/jni-config.json @@ -0,0 +1,22 @@ +[ +{ + "name":"java.lang.String", + "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"org.apache.maven.surefire.booter.ForkedBooter", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"sun.instrument.InstrumentationImpl", + "methods":[{"name":"<init>","parameterTypes":["long","boolean","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/reflect-config.json b/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/reflect-config.json new file mode 100644 index 000000000..54afdb74e --- /dev/null +++ b/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/reflect-config.json @@ -0,0 +1,189 @@ +[ +{ + "name":"com.amazonaws.services.lambda.runtime.Context", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getAwsRequestId","parameterTypes":[] }, {"name":"getClientContext","parameterTypes":[] }, {"name":"getFunctionName","parameterTypes":[] }, {"name":"getFunctionVersion","parameterTypes":[] }, {"name":"getIdentity","parameterTypes":[] }, {"name":"getInvokedFunctionArn","parameterTypes":[] }, {"name":"getLogGroupName","parameterTypes":[] }, {"name":"getLogStreamName","parameterTypes":[] }, {"name":"getLogger","parameterTypes":[] }, {"name":"getMemoryLimitInMB","parameterTypes":[] }, {"name":"getRemainingTimeInMillis","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.Closeable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.io.Flushable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.io.InputStream", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"available","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"mark","parameterTypes":["int"] }, {"name":"markSupported","parameterTypes":[] }, {"name":"read","parameterTypes":[] }, {"name":"read","parameterTypes":["byte[]"] }, {"name":"read","parameterTypes":["byte[]","int","int"] }, {"name":"readAllBytes","parameterTypes":[] }, {"name":"readNBytes","parameterTypes":["int"] }, {"name":"readNBytes","parameterTypes":["byte[]","int","int"] }, {"name":"reset","parameterTypes":[] }, {"name":"skip","parameterTypes":["long"] }, {"name":"skipNBytes","parameterTypes":["long"] }, {"name":"transferTo","parameterTypes":["java.io.OutputStream"] }] +}, +{ + "name":"java.io.OutputStream", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"close","parameterTypes":[] }, {"name":"flush","parameterTypes":[] }, {"name":"write","parameterTypes":["int"] }, {"name":"write","parameterTypes":["byte[]"] }, {"name":"write","parameterTypes":["byte[]","int","int"] }] +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String"] }, {"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessEnvironment", + "fields":[{"name":"theCaseInsensitiveEnvironment"}, {"name":"theEnvironment"}] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.aspectj.lang.JoinPoint", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getArgs","parameterTypes":[] }, {"name":"getKind","parameterTypes":[] }, {"name":"getSignature","parameterTypes":[] }, {"name":"getSourceLocation","parameterTypes":[] }, {"name":"getStaticPart","parameterTypes":[] }, {"name":"getTarget","parameterTypes":[] }, {"name":"getThis","parameterTypes":[] }, {"name":"toLongString","parameterTypes":[] }, {"name":"toShortString","parameterTypes":[] }] +}, +{ + "name":"org.aspectj.lang.ProceedingJoinPoint", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"proceed","parameterTypes":[] }, {"name":"proceed","parameterTypes":["java.lang.Object[]"] }, {"name":"set$AroundClosure","parameterTypes":["org.aspectj.runtime.internal.AroundClosure"] }, {"name":"stack$AroundClosure","parameterTypes":["org.aspectj.runtime.internal.AroundClosure"] }] +}, +{ + "name":"org.aspectj.lang.Signature", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getDeclaringType","parameterTypes":[] }, {"name":"getDeclaringTypeName","parameterTypes":[] }, {"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"toLongString","parameterTypes":[] }, {"name":"toShortString","parameterTypes":[] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +} +] diff --git a/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/resource-config.json b/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/resource-config.json new file mode 100644 index 000000000..6fb5eb95e --- /dev/null +++ b/powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/resource-config.json @@ -0,0 +1,21 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.configuration.Configuration\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.presentation.Representation\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, { + "pattern":"\\Qversion.properties\\E" + }]}, + "bundles":[] +} diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java new file mode 100644 index 000000000..03991688e --- /dev/null +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java @@ -0,0 +1,34 @@ +package software.amazon.lambda.powertools.common.internal; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class ClassPreLoaderTest { + + // Making this volatile so the Thread Context doesn't need any special handling + static volatile boolean dummyClassLoaded = false; + + /** + * Dummy class to be loaded by ClassPreLoader in test. + * <b>The class name is referenced in <i>powertools-common/src/test/resources/classesloaded.txt</i></b> + * This class is used to verify that the ClassPreLoader can load valid classes. + * The static block sets a flag to indicate that the class has been loaded. + */ + static class DummyClass { + static { + dummyClassLoaded = true; + } + } + @Test + void preloadClasses_shouldIgnoreInvalidClassesAndLoadValidClasses() { + + dummyClassLoaded = false; + // powertools-common/src/test/resources/classesloaded.txt has a class that does not exist + // Verify that the missing class did not throw any exception + assertDoesNotThrow(ClassPreLoader::preloadClasses); + + // When the classloaded.txt is a mixed bag of valid and invalid classes, Valid class must load + assertTrue(dummyClassLoaded, "DummyClass should be loaded"); + } +} \ No newline at end of file diff --git a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java similarity index 60% rename from powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java rename to powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java index dc8f49580..0726a9e77 100644 --- a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java @@ -12,34 +12,43 @@ * */ -package software.amazon.lambda.powertools.core.internal; +package software.amazon.lambda.powertools.common.internal; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import java.io.InputStream; import java.io.OutputStream; import java.util.Optional; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; +import org.junitpioneer.jupiter.ClearEnvironmentVariable; +import org.junitpioneer.jupiter.ClearSystemProperty; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; + +import software.amazon.awssdk.utilslite.SdkInternalThreadLocal; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; class LambdaHandlerProcessorTest { - private Signature signature = mock(Signature.class); - private ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); + @AfterEach + void cleanup() { + SdkInternalThreadLocal.clear(); + } @Test void isHandlerMethod_shouldRecognizeRequestHandler() { - Object[] args = {new Object(), mock(Context.class)}; + Context context = new TestLambdaContext(); + Object[] args = { new Object(), context }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)).isTrue(); @@ -47,7 +56,7 @@ void isHandlerMethod_shouldRecognizeRequestHandler() { @Test void isHandlerMethod_shouldRecognizeRequestStreamHandler() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + Object[] args = { mock(InputStream.class), mock(OutputStream.class), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)).isTrue(); @@ -64,7 +73,7 @@ void isHandlerMethod_shouldReturnFalse() { @Test void placedOnRequestHandler_shouldRecognizeRequestHandler() { - Object[] args = {new Object(), mock(Context.class)}; + Object[] args = { new Object(), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); assertThat(LambdaHandlerProcessor.placedOnRequestHandler(pjpMock)).isTrue(); @@ -72,7 +81,7 @@ void placedOnRequestHandler_shouldRecognizeRequestHandler() { @Test void placedOnStreamHandler_shouldRecognizeRequestStreamHandler() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + Object[] args = { mock(InputStream.class), mock(OutputStream.class), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); assertThat(LambdaHandlerProcessor.placedOnStreamHandler(pjpMock)).isTrue(); @@ -80,7 +89,7 @@ void placedOnStreamHandler_shouldRecognizeRequestStreamHandler() { @Test void placedOnRequestHandler_shouldInvalidateOnWrongNoOfArgs() { - Object[] args = {new Object()}; + Object[] args = { new Object() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); boolean isPlacedOnRequestHandler = LambdaHandlerProcessor.placedOnRequestHandler(pjpMock); @@ -90,7 +99,7 @@ void placedOnRequestHandler_shouldInvalidateOnWrongNoOfArgs() { @Test void placedOnRequestHandler_shouldInvalidateOnWrongTypeOfArgs() { - Object[] args = {new Object(), new Object()}; + Object[] args = { new Object(), new Object() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); boolean isPlacedOnRequestHandler = LambdaHandlerProcessor.placedOnRequestHandler(pjpMock); @@ -100,7 +109,7 @@ void placedOnRequestHandler_shouldInvalidateOnWrongTypeOfArgs() { @Test void placedOnStreamHandler_shouldInvalidateOnWrongNoOfArgs() { - Object[] args = {new Object()}; + Object[] args = { new Object() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); @@ -110,7 +119,7 @@ void placedOnStreamHandler_shouldInvalidateOnWrongNoOfArgs() { @Test void placedOnStreamHandler_shouldInvalidateOnWrongTypeOfArgs() { - Object[] args = {new Object(), new Object(), new Object()}; + Object[] args = { new Object(), new Object(), new Object() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); @@ -120,7 +129,7 @@ void placedOnStreamHandler_shouldInvalidateOnWrongTypeOfArgs() { @Test void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidOutputStreamArg() { - Object[] args = {mock(InputStream.class), new Object(), mock(Context.class)}; + Object[] args = { mock(InputStream.class), new Object(), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); @@ -130,7 +139,7 @@ void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidOutputStreamArg() @Test void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidContextArg() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), new Object()}; + Object[] args = { mock(InputStream.class), mock(OutputStream.class), new Object() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); @@ -139,32 +148,46 @@ void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidContextArg() { } @Test + @SetEnvironmentVariable(key = LambdaConstants.X_AMZN_TRACE_ID, value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\"") void getXrayTraceId_present() { String traceID = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""; - try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { - mockedSystemWrapper.when(() -> getenv(LambdaConstants.X_AMZN_TRACE_ID)).thenReturn(traceID); - Optional xRayTraceId = LambdaHandlerProcessor.getXrayTraceId(); + Optional<String> xRayTraceId = LambdaHandlerProcessor.getXrayTraceId(); - assertThat(xRayTraceId.isPresent()).isTrue(); - assertThat(traceID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")).isEqualTo(xRayTraceId.get()); - } + assertThat(xRayTraceId).isPresent(); + assertThat(traceID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")).isEqualTo(xRayTraceId.get()); } @Test + @ClearEnvironmentVariable(key = LambdaConstants.X_AMZN_TRACE_ID) void getXrayTraceId_notPresent() { - try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { - mockedSystemWrapper.when(() -> getenv(LambdaConstants.X_AMZN_TRACE_ID)).thenReturn(null); - boolean isXRayTraceIdPresent = LambdaHandlerProcessor.getXrayTraceId().isPresent(); + boolean isXRayTraceIdPresent = LambdaHandlerProcessor.getXrayTraceId().isPresent(); + + assertThat(isXRayTraceIdPresent).isFalse(); + } - assertThat(isXRayTraceIdPresent).isFalse(); - } + @Test + @ClearEnvironmentVariable(key = LambdaConstants.X_AMZN_TRACE_ID) + @ClearSystemProperty(key = LambdaConstants.XRAY_TRACE_HEADER) + void getXrayTraceId_fromSdkInternalThreadLocal() { + // Verify no trace ID initially + assertThat(LambdaHandlerProcessor.getXrayTraceId()).isEmpty(); + + // Set trace ID in SdkInternalThreadLocal + String expectedTraceId = "1-5759e988-bd862e3fe1be46a994272793"; + SdkInternalThreadLocal.put(LambdaConstants.AWS_LAMBDA_X_TRACE_ID, + "Root=" + expectedTraceId + ";Parent=53995c3f42cd8ad8;Sampled=1"); + + // Verify trace ID is now present + Optional<String> traceId = LambdaHandlerProcessor.getXrayTraceId(); + assertThat(traceId).isPresent(); + assertThat(traceId.get()).isEqualTo(expectedTraceId); } @Test void extractContext_fromRequestHandler() { - Object[] args = {new Object(), mock(Context.class)}; + Object[] args = { new Object(), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); Context context = LambdaHandlerProcessor.extractContext(pjpMock); @@ -174,7 +197,7 @@ void extractContext_fromRequestHandler() { @Test void extractContext_fromStreamRequestHandler() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + Object[] args = { mock(InputStream.class), mock(OutputStream.class), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); Context context = LambdaHandlerProcessor.extractContext(pjpMock); @@ -184,7 +207,7 @@ void extractContext_fromStreamRequestHandler() { @Test void extractContext_notKnownHandler() { - Object[] args = {new Object()}; + Object[] args = { new Object() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(Object.class, args); Context context = LambdaHandlerProcessor.extractContext(pjpMock); @@ -193,6 +216,7 @@ void extractContext_notKnownHandler() { } @Test + @SetEnvironmentVariable(key = LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE, value = LambdaConstants.ON_DEMAND_INVOCATION_TYPE) void isColdStart() { boolean isColdStart = LambdaHandlerProcessor.isColdStart(); @@ -209,43 +233,39 @@ void isColdStart_coldStartDone() { } @Test + @SetEnvironmentVariable(key = LambdaConstants.AWS_SAM_LOCAL, value = "true") void isSamLocal() { - try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { - mockedSystemWrapper.when(() -> getenv(LambdaConstants.AWS_SAM_LOCAL)).thenReturn("true"); - boolean isSamLocal = LambdaHandlerProcessor.isSamLocal(); + boolean isSamLocal = LambdaHandlerProcessor.isSamLocal(); - assertThat(isSamLocal).isTrue(); - } + assertThat(isSamLocal).isTrue(); } @Test + @SetEnvironmentVariable(key = LambdaConstants.POWERTOOLS_SERVICE_NAME, value = "MyService") void serviceName() { - try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { - String expectedServiceName = "MyService"; - mockedSystemWrapper.when(() -> getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)) - .thenReturn(expectedServiceName); + String expectedServiceName = "MyService"; + String actualServiceName = LambdaHandlerProcessor.serviceName(); - String actualServiceName = LambdaHandlerProcessor.serviceName(); - - assertThat(actualServiceName).isEqualTo(expectedServiceName); - } + assertThat(actualServiceName).isEqualTo(expectedServiceName); } @Test + @ClearEnvironmentVariable(key = LambdaConstants.POWERTOOLS_SERVICE_NAME) void serviceName_Undefined() { LambdaHandlerProcessor.resetServiceName(); - try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { - mockedSystemWrapper.when(() -> getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)).thenReturn(null); - - assertThat(LambdaHandlerProcessor.serviceName()).isEqualTo(LambdaConstants.SERVICE_UNDEFINED); - } + assertThat(LambdaHandlerProcessor.serviceName()).isEqualTo(LambdaConstants.SERVICE_UNDEFINED); } - private ProceedingJoinPoint mockRequestHandlerPjp(Class handlerClass, Object[] handlerArgs) { + private ProceedingJoinPoint mockRequestHandlerPjp(Class<?> handlerClass, Object[] handlerArgs) { + ProceedingJoinPoint pjp = mock(ProceedingJoinPoint.class); + Signature signature = mock(Signature.class); + when(signature.getDeclaringType()).thenReturn(handlerClass); - when(pjpMock.getArgs()).thenReturn(handlerArgs); - when(pjpMock.getSignature()).thenReturn(signature); - return pjpMock; + when(signature.getName()).thenReturn("handleRequest"); + when(pjp.getSignature()).thenReturn(signature); + when(pjp.getArgs()).thenReturn(handlerArgs); + + return pjp; } -} \ No newline at end of file +} diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java new file mode 100644 index 000000000..33050d8b4 --- /dev/null +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java @@ -0,0 +1,230 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.common.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.common.internal.UserAgentConfigurator.AWS_EXECUTION_ENV; +import static software.amazon.lambda.powertools.common.internal.UserAgentConfigurator.VERSION_KEY; +import static software.amazon.lambda.powertools.common.internal.UserAgentConfigurator.VERSION_PROPERTIES_FILENAME; +import static software.amazon.lambda.powertools.common.internal.UserAgentConfigurator.getVersionFromProperties; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Pattern; + +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +class UserAgentConfiguratorTest { + + private static final String SEM_VER_PATTERN = "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"; + private static final String VERSION = UserAgentConfigurator.getProjectVersion(); + + @Test + void testGetVersion() { + + assertThat(VERSION) + .isNotNull() + .isNotEmpty(); + assertThat(Pattern.matches(SEM_VER_PATTERN, VERSION)).isTrue(); + } + + @Test + void testGetVersionFromProperties_WrongKey() { + String version = getVersionFromProperties(VERSION_PROPERTIES_FILENAME, "some invalid key"); + + assertThat(version) + .isNotNull() + .isEqualTo("NA"); + } + + @Test + void testGetVersionFromProperties_FileNotExist() { + String version = getVersionFromProperties("some file", VERSION_KEY); + + assertThat(version) + .isNotNull() + .isEqualTo("NA"); + } + + @Test + void testGetVersionFromProperties_InvalidFile() throws IOException { + Path tempFile = Files.createTempFile("unreadable", ".properties"); + File f = tempFile.toFile(); + f.setReadable(false); + + String version = getVersionFromProperties(f.getName(), VERSION_KEY); + + assertThat(version).isEqualTo("NA"); + + // Cleanup + f.setReadable(true); + Files.deleteIfExists(tempFile); + } + + @Test + void testGetVersionFromProperties_EmptyVersion() { + String version = getVersionFromProperties("test.properties", VERSION_KEY); + + assertThat(version).isEqualTo("NA"); + } + + @Test + void testGetUserAgent() { + String userAgent = UserAgentConfigurator.getUserAgent("test-feature"); + + assertThat(userAgent) + .isNotNull() + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); + + } + + @Test + void testGetUserAgent_NoFeature() { + String userAgent = UserAgentConfigurator.getUserAgent(""); + + assertThat(userAgent) + .isNotNull() + .isEqualTo("PT/NO-OP/" + VERSION + " PTENV/NA"); + } + + @Test + void testGetUserAgent_NullFeature() { + String userAgent = UserAgentConfigurator.getUserAgent(null); + + assertThat(userAgent) + .isNotNull() + .isEqualTo("PT/NO-OP/" + VERSION + " PTENV/NA"); + } + + @Test + @SetEnvironmentVariable(key = AWS_EXECUTION_ENV, value = "AWS_Lambda_java8") + void testGetUserAgent_SetAWSExecutionEnv() { + String userAgent = UserAgentConfigurator.getUserAgent("test-feature"); + + assertThat(userAgent) + .isNotNull() + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/AWS_Lambda_java8"); + } + + @Test + void testConfigureUserAgent() { + System.clearProperty("sdk.ua.appId"); + UserAgentConfigurator.configureUserAgent("test-feature"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); + } + + @Test + void testConfigureUserAgent_WithExistingUserValue() { + System.setProperty("sdk.ua.appId", "UserValueABC123"); + UserAgentConfigurator.configureUserAgent("test-feature"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("UserValueABC123/PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); + } + + @Test + void testConfigureUserAgent_ReplacePowertoolsUserAgent() { + System.setProperty("sdk.ua.appId", "PT/BATCH/" + VERSION + " PTENV/NA"); + UserAgentConfigurator.configureUserAgent("logging-log4j"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/LOGGING-LOG4J/" + VERSION + " PTENV/NA"); + } + + @Test + void testConfigureUserAgent_PreserveUserValueAndReplacePowertools() { + System.setProperty("sdk.ua.appId", "UserValue/PT/BATCH/" + VERSION + " PTENV/NA"); + UserAgentConfigurator.configureUserAgent("tracing"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("UserValue/PT/TRACING/" + VERSION + " PTENV/NA"); + } + + @Test + void testConfigureUserAgent_ExceedsLimit() { + System.setProperty("sdk.ua.appId", "VeryLongUserValueThatExceedsTheLimitWhenCombined"); + UserAgentConfigurator.configureUserAgent("test-feature"); + + // Should not update if it would exceed 50 characters + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("VeryLongUserValueThatExceedsTheLimitWhenCombined"); + } + + @Test + void testExtractUserValue_NoUserValue() { + String result = UserAgentConfigurator.extractUserValue("PT/BATCH/" + VERSION + " PTENV/NA"); + assertThat(result).isEmpty(); + } + + @Test + void testExtractUserValue_WithUserValue() { + String result = UserAgentConfigurator.extractUserValue("UserValue/PT/BATCH/" + VERSION + " PTENV/NA"); + assertThat(result).isEqualTo("UserValue"); + } + + @Test + void testExtractUserValue_EmptyString() { + String result = UserAgentConfigurator.extractUserValue(""); + assertThat(result).isEmpty(); + } + + @Test + void testExtractUserValue_NullString() { + String result = UserAgentConfigurator.extractUserValue(null); + assertThat(result).isEmpty(); + } + + @Test + void testExtractUserValue_OnlyUserValue() { + String result = UserAgentConfigurator.extractUserValue("MyCustomValue"); + assertThat(result).isEqualTo("MyCustomValue"); + } + + @Test + void testConfigureUserAgent_WithEmptyExistingValue() { + System.setProperty("sdk.ua.appId", ""); + UserAgentConfigurator.configureUserAgent("test-feature"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); + } + + @Test + @SetEnvironmentVariable(key = AWS_EXECUTION_ENV, value = "AWS_Lambda_java11") + void testConfigureUserAgent_MultipleUtilities() { + System.clearProperty("sdk.ua.appId"); + + // First utility + UserAgentConfigurator.configureUserAgent("batch"); + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/BATCH/" + VERSION + " PTENV/AWS_Lambda_java11"); + + // Second utility - should replace, not append + UserAgentConfigurator.configureUserAgent("logging-log4j"); + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/LOGGING-LOG4J/" + VERSION + " PTENV/AWS_Lambda_java11"); + + // Third utility - should replace again + UserAgentConfigurator.configureUserAgent("tracing"); + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/TRACING/" + VERSION + " PTENV/AWS_Lambda_java11"); + } + +} diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java new file mode 100644 index 000000000..6b66b66b7 --- /dev/null +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java @@ -0,0 +1,77 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.common.stubs; + +import com.amazonaws.services.lambda.runtime.ClientContext; +import com.amazonaws.services.lambda.runtime.CognitoIdentity; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.LambdaLogger; + +public class TestLambdaContext implements Context { + @Override + public String getAwsRequestId() { + return "test-request-id"; + } + + @Override + public String getLogGroupName() { + return "test-log-group"; + } + + @Override + public String getLogStreamName() { + return "test-log-stream"; + } + + @Override + public String getFunctionName() { + return "test-function"; + } + + @Override + public String getFunctionVersion() { + return "1"; + } + + @Override + public String getInvokedFunctionArn() { + return "arn:aws:lambda:us-east-1:123456789012:function:test"; + } + + @Override + public CognitoIdentity getIdentity() { + return null; + } + + @Override + public ClientContext getClientContext() { + return null; + } + + @Override + public int getRemainingTimeInMillis() { + return 30000; + } + + @Override + public int getMemoryLimitInMB() { + return 128; + } + + @Override + public LambdaLogger getLogger() { + return null; + } +} diff --git a/powertools-common/src/test/resources/classesloaded.txt b/powertools-common/src/test/resources/classesloaded.txt new file mode 100644 index 000000000..498ffdf69 --- /dev/null +++ b/powertools-common/src/test/resources/classesloaded.txt @@ -0,0 +1,2 @@ +software.amazon.lambda.powertools.common.internal.NonExistingClass +software.amazon.lambda.powertools.common.internal.ClassPreLoaderTest$DummyClass \ No newline at end of file diff --git a/powertools-core/src/test/resources/test.properties b/powertools-common/src/test/resources/test.properties similarity index 100% rename from powertools-core/src/test/resources/test.properties rename to powertools-common/src/test/resources/test.properties diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml deleted file mode 100644 index 78cd735b9..000000000 --- a/powertools-core/pom.xml +++ /dev/null @@ -1,126 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ Copyright 2023 Amazon.com, Inc. or its affiliates. - ~ Licensed under the Apache License, Version 2.0 (the - ~ "License"); you may not use this file except in compliance - ~ with the License. You may obtain a copy of the License at - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - ~ - --> - -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <artifactId>powertools-core</artifactId> - <packaging>jar</packaging> - - <parent> - <artifactId>powertools-parent</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> - </parent> - - <name>Powertools for AWS Lambda (Java) library Core</name> - <description> - A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. - </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> - - <dependencies> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> - <version>${log4j.version}</version> - </dependency> - - <!-- Test dependencies --> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjweaver</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <resources> - <resource> - <directory>src/main/resources-filtered</directory> - <filtering>true</filtering> - </resource> - </resources> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> - </plugins> - </build> - -</project> \ No newline at end of file diff --git a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java deleted file mode 100644 index 2d75bdb3a..000000000 --- a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.core.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mockStatic; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; -import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurator.AWS_EXECUTION_ENV; -import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurator.VERSION_KEY; -import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurator.VERSION_PROPERTIES_FILENAME; -import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurator.getVersionFromProperties; - -import java.io.File; -import java.util.Objects; -import java.util.regex.Pattern; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; - -class UserAgentConfiguratorTest { - - private static final String SEM_VER_PATTERN = - "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"; - private static final String VERSION = UserAgentConfigurator.getProjectVersion(); - - - @Test - void testGetVersion() { - - assertThat(VERSION) - .isNotNull() - .isNotEmpty(); - assertThat(Pattern.matches(SEM_VER_PATTERN, VERSION)).isTrue(); - } - - @Test - void testGetVersionFromProperties_WrongKey() { - String version = getVersionFromProperties(VERSION_PROPERTIES_FILENAME, "some invalid key"); - - assertThat(version) - .isNotNull() - .isEqualTo("NA"); - } - - @Test - void testGetVersionFromProperties_FileNotExist() { - String version = getVersionFromProperties("some file", VERSION_KEY); - - assertThat(version) - .isNotNull() - .isEqualTo("NA"); - } - - @Test - void testGetVersionFromProperties_InvalidFile() { - File f = new File(Objects.requireNonNull(Thread.currentThread().getContextClassLoader() - .getResource("unreadable.properties")).getPath()); - f.setReadable(false); - - String version = getVersionFromProperties("unreadable.properties", VERSION_KEY); - - assertThat(version).isEqualTo("NA"); - } - - @Test - void testGetVersionFromProperties_EmptyVersion() { - String version = getVersionFromProperties("test.properties", VERSION_KEY); - - assertThat(version).isEqualTo("NA"); - } - - @Test - void testGetUserAgent() { - String userAgent = UserAgentConfigurator.getUserAgent("test-feature"); - - assertThat(userAgent) - .isNotNull() - .isEqualTo("PT/test-feature/" + VERSION + " PTEnv/NA"); - - } - - @Test - void testGetUserAgent_NoFeature() { - String userAgent = UserAgentConfigurator.getUserAgent(""); - - assertThat(userAgent) - .isNotNull() - .isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA"); - } - - @Test - void testGetUserAgent_NullFeature() { - String userAgent = UserAgentConfigurator.getUserAgent(null); - - assertThat(userAgent) - .isNotNull() - .isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA"); - } - - @Test - void testGetUserAgent_SetAWSExecutionEnv() { - try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { - mockedSystemWrapper.when(() -> getenv(AWS_EXECUTION_ENV)).thenReturn("AWS_Lambda_java8"); - String userAgent = UserAgentConfigurator.getUserAgent("test-feature"); - - assertThat(userAgent) - .isNotNull() - .isEqualTo("PT/test-feature/" + VERSION + " PTEnv/AWS_Lambda_java8"); - } - } - -} diff --git a/powertools-core/src/test/resources/unreadable.properties b/powertools-core/src/test/resources/unreadable.properties deleted file mode 100644 index 42ff4693f..000000000 --- a/powertools-core/src/test/resources/unreadable.properties +++ /dev/null @@ -1,2 +0,0 @@ -# This is intentionally left empty -# It used during testing and is set to un-readable to fulfil the test purposes. \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 995121e2a..3e89aadd2 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) batch</name> + <name>E2E test handler – Batch</name> <dependencies> <dependency> @@ -19,7 +19,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -29,14 +29,14 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-serialization</artifactId> </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> - </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>dynamodb</artifactId> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 64f5a02c2..36142d3f5 100644 --- a/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -14,7 +14,17 @@ package software.amazon.lambda.powertools.e2e; -import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; @@ -24,47 +34,22 @@ import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; -import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.util.IOUtils; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; import software.amazon.lambda.powertools.e2e.model.Product; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.utilities.JsonConfig; - -import javax.management.Attribute; - public class Function implements RequestHandler<InputStream, Object> { - private final static Logger LOGGER = LogManager.getLogger(Function.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Function.class); private final BatchMessageHandler<SQSEvent, SQSBatchResponse> sqsHandler; private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> kinesisHandler; private final BatchMessageHandler<DynamodbEvent, StreamsEventResponse> ddbHandler; private final String ddbOutputTable; - private DynamoDbClient ddbClient; public Function() { sqsHandler = new BatchMessageHandlerBuilder() @@ -85,8 +70,7 @@ public Function() { private void processProductMessage(Product p, Context c) { LOGGER.info("Processing product " + p); - // TODO - write product details to output table - ddbClient = DynamoDbClient.builder() + DynamoDbClient ddbClient = DynamoDbClient.builder() .build(); Map<String, AttributeValue> results = new HashMap<>(); results.put("functionName", AttributeValue.builder() @@ -110,7 +94,7 @@ private void processProductMessage(Product p, Context c) { private void processDdbMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord, Context context) { LOGGER.info("Processing DynamoDB Stream Record" + dynamodbStreamRecord); - ddbClient = DynamoDbClient.builder() + DynamoDbClient ddbClient = DynamoDbClient.builder() .build(); String id = dynamodbStreamRecord.getDynamodb().getKeys().get("id").getS(); @@ -134,26 +118,25 @@ public Object createResult(String input, Context context) { LOGGER.info(input); - PojoSerializer<SQSEvent> serializer = - LambdaEventSerializers.serializerFor(SQSEvent.class, this.getClass().getClassLoader()); + PojoSerializer<SQSEvent> serializer = LambdaEventSerializers.serializerFor(SQSEvent.class, + this.getClass().getClassLoader()); SQSEvent event = serializer.fromJson(input); - if (event.getRecords().get(0).getEventSource().equals("aws:sqs")) { + if ("aws:sqs".equals(event.getRecords().get(0).getEventSource())) { LOGGER.info("Running for SQS"); - LOGGER.info(event); return sqsHandler.processBatch(event, context); } - PojoSerializer<KinesisEvent> kinesisSerializer = - LambdaEventSerializers.serializerFor(KinesisEvent.class, this.getClass().getClassLoader()); + PojoSerializer<KinesisEvent> kinesisSerializer = LambdaEventSerializers.serializerFor(KinesisEvent.class, + this.getClass().getClassLoader()); KinesisEvent kinesisEvent = kinesisSerializer.fromJson(input); - if (kinesisEvent.getRecords().get(0).getEventSource().equals("aws:kinesis")) { + if ("aws:kinesis".equals(kinesisEvent.getRecords().get(0).getEventSource())) { LOGGER.info("Running for Kinesis"); return kinesisHandler.processBatch(kinesisEvent, context); } // Well, let's try dynamo - PojoSerializer<DynamodbEvent> ddbSerializer = - LambdaEventSerializers.serializerFor(DynamodbEvent.class, this.getClass().getClassLoader()); + PojoSerializer<DynamodbEvent> ddbSerializer = LambdaEventSerializers.serializerFor(DynamodbEvent.class, + this.getClass().getClassLoader()); LOGGER.info("Running for DynamoDB"); DynamodbEvent ddbEvent = ddbSerializer.fromJson(input); return ddbHandler.processBatch(ddbEvent, context); @@ -164,8 +147,8 @@ public Object handleRequest(InputStream inputStream, Context context) { String input = new BufferedReader( new InputStreamReader(inputStream, StandardCharsets.UTF_8)) - .lines() - .collect(Collectors.joining("\n")); + .lines() + .collect(Collectors.joining("\n")); return createResult(input, context); } diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml new file mode 100644 index 000000000..b5669b21f --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -0,0 +1,60 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>e2e-test-handler-idempotency-functional</artifactId> + <packaging>jar</packaging> + <name>E2E test handler – Idempotency Functional</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-dynamodb</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..fec7459c1 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.TimeZone; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; + +public class Function implements RequestHandler<Input, String> { + + public Function() { + this(DynamoDbClient + .builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv("AWS_REGION"))) + .build()); + } + + public Function(DynamoDbClient client) { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withExpiration(Duration.of(10, ChronoUnit.SECONDS)) + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withDynamoDbClient(client) + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build()) + .configure(); + } + + public String handleRequest(Input input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent(this::processRequest, input, String.class); + } + + private String processRequest(Input input) { + DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); + return dtf.format(Instant.now()); + } +} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java similarity index 69% rename from powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java rename to powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index fbb4289d8..0d14b749e 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -12,15 +12,16 @@ * */ -package software.amazon.lambda.powertools.sqs.exception; +package software.amazon.lambda.powertools.e2e; -/** - * Indicates that a message has been skipped due to the batch it is - * within failing. - */ -public class SkippedMessageDueToFailedBatchException extends Exception { +public class Input { + private String message; - public SkippedMessageDueToFailedBatchException() { + public String getMessage() { + return message; } + public void setMessage(String message) { + this.message = message; + } } diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/logging/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/log4j2.xml rename to powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml new file mode 100644 index 000000000..21a658e6c --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -0,0 +1,60 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>e2e-test-handler-idempotency-generics</artifactId> + <packaging>jar</packaging> + <name>E2E test handler – Idempotency Generics</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-dynamodb</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..09e39d1eb --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.core.type.TypeReference; + +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; + +public class Function implements RequestHandler<Input, String> { + + public Function() { + this(DynamoDbClient + .builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv("AWS_REGION"))) + .build()); + } + + public Function(DynamoDbClient client) { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withExpiration(Duration.of(10, ChronoUnit.SECONDS)) + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withDynamoDbClient(client) + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build()) + .configure(); + } + + public String handleRequest(Input input, Context context) { + Idempotency.registerLambdaContext(context); + + // This is just to test the generic type support using TypeReference. + // We return the same String to run the same assertions as other idempotency E2E handlers. + Map<String, String> result = Idempotency.makeIdempotent( + this::processRequest, + input, + new TypeReference<Map<String, String>>() {}); + + return result.get("timestamp"); + } + + private Map<String, String> processRequest(Input input) { + DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); + Map<String, String> result = new HashMap<>(); + result.put("timestamp", dtf.format(Instant.now())); + return result; + } +} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java similarity index 66% rename from powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java rename to powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 557aa214d..0d14b749e 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -12,15 +12,16 @@ * */ -package software.amazon.lambda.powertools.sqs; +package software.amazon.lambda.powertools.e2e; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; +public class Input { + private String message; -public class SampleSqsHandler implements SqsMessageHandler<Object> { - private int counter; + public String getMessage() { + return message; + } - @Override - public String process(SQSEvent.SQSMessage message) { - return String.valueOf(counter++); + public void setMessage(String message) { + this.message = message; } } diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="JsonAppender"/> + </Root> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 25dfbfabf..921599bdb 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -1,35 +1,42 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) idempotency</name> + <name>E2E test handler – Idempotency</name> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> </dependency> - </dependencies> <build> @@ -44,7 +51,7 @@ <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-core</artifactId> </aspectLibrary> <aspectLibrary> <groupId>software.amazon.lambda</groupId> @@ -66,4 +73,18 @@ </plugin> </plugins> </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index e4c2f2b9a..038704931 100644 --- a/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -27,7 +27,7 @@ import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.Idempotent; -import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; import software.amazon.lambda.powertools.logging.Logging; @@ -60,4 +60,4 @@ public String handleRequest(Input input, Context context) { DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); return dtf.format(Instant.now()); } -} \ No newline at end of file +} diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml new file mode 100644 index 000000000..ddfe39a5e --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -0,0 +1,46 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>e2e-test-handler-largemessage-functional</artifactId> + <packaging>jar</packaging> + <name>E2E test handler – Large message functional</name> + + <dependencies> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>dynamodb</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-large-messages</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..05a336500 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import static software.amazon.lambda.powertools.logging.PowertoolsLogging.withLogging; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.utils.BinaryUtils; +import software.amazon.awssdk.utils.Md5Utils; +import software.amazon.lambda.powertools.largemessages.LargeMessages; + +public class Function implements RequestHandler<SQSEvent, SQSBatchResponse> { + + private static final String TABLE_FOR_ASYNC_TESTS = System.getenv("TABLE_FOR_ASYNC_TESTS"); + private DynamoDbClient client; + + public Function() { + if (client == null) { + client = DynamoDbClient.builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv("AWS_REGION"))) + .build(); + } + } + + public SQSBatchResponse handleRequest(SQSEvent event, Context context) { + return withLogging(context, () -> { + for (SQSMessage message : event.getRecords()) { + LargeMessages.processLargeMessage(message, msg -> processRawMessage(msg, context)); + } + return SQSBatchResponse.builder().build(); + }); + } + + private Void processRawMessage(SQSMessage sqsMessage, Context context) { + String bodyMD5 = md5(sqsMessage.getBody()); + if (!sqsMessage.getMd5OfBody().equals(bodyMD5)) { + throw new SecurityException( + String.format("message digest does not match, expected %s, got %s", sqsMessage.getMd5OfBody(), + bodyMD5)); + } + + Map<String, AttributeValue> item = new HashMap<>(); + item.put("functionName", AttributeValue.builder().s(context.getFunctionName()).build()); + item.put("id", AttributeValue.builder().s(sqsMessage.getMessageId()).build()); + item.put("bodyMD5", AttributeValue.builder().s(bodyMD5).build()); + item.put("bodySize", + AttributeValue.builder().n(String.valueOf(sqsMessage.getBody().getBytes(StandardCharsets.UTF_8).length)) + .build()); + + client.putItem(PutItemRequest.builder().tableName(TABLE_FOR_ASYNC_TESTS).item(item).build()); + + return null; + } + + private String md5(String message) { + return BinaryUtils.toHex(Md5Utils.computeMD5Hash(message.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="JsonAppender"/> + </Root> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index c626f5f64..bee253988 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) large message</name> + <name>E2E test handler – Large message</name> <dependencies> <dependency> @@ -23,15 +23,15 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> </dependency> </dependencies> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 9635efd87..5ef7e1963 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,17 +5,17 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) idempotency with large messages</name> + <name>E2E test handler – Large message idempotent</name> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> @@ -23,17 +23,16 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> </dependency> - </dependencies> <build> @@ -48,7 +47,7 @@ <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-core</artifactId> </aspectLibrary> <aspectLibrary> <groupId>software.amazon.lambda</groupId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/largemessage_idempotent/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 5269b37c9..6c9d4e8cf 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -37,7 +37,7 @@ import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.IdempotencyKey; import software.amazon.lambda.powertools.idempotency.Idempotent; -import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; import software.amazon.lambda.powertools.largemessages.LargeMessage; import software.amazon.lambda.powertools.logging.Logging; diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml new file mode 100644 index 000000000..4ec6e5008 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -0,0 +1,60 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>e2e-test-handler-logging-functional</artifactId> + <packaging>jar</packaging> + <name>E2E test handler – Logging Functional</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java b/powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java similarity index 50% rename from powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java rename to powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index e3a0fa22e..78ab9ba4b 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -12,24 +12,29 @@ * */ -package software.amazon.lambda.powertools.metrics.handlers; +package software.amazon.lambda.powertools.e2e; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.metrics.Metrics; -public class PowertoolsMetricsColdStartEnabledHandler implements RequestHandler<Object, Object> { +import software.amazon.lambda.powertools.logging.PowertoolsLogging; - @Override - @Metrics(namespace = "ExampleApplication", service = "booking", captureColdStart = true) - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("Metric1", 1, Unit.BYTES); +public class Function implements RequestHandler<Input, String> { + private static final Logger LOG = LoggerFactory.getLogger(Function.class); - return null; + public String handleRequest(Input input, Context context) { + return PowertoolsLogging.withLogging(context, () -> { + input.getKeys().forEach(MDC::put); + LOG.info(input.getMessage()); + + // Flush buffer manually since we buffer at INFO level to test log buffering + PowertoolsLogging.flushBuffer(); + + return "OK"; + }); } } diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java similarity index 97% rename from powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java rename to powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index cc449922e..66fd49ddc 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -20,9 +20,6 @@ public class Input { private String message; private Map<String, String> keys; - public Input() { - } - public String getMessage() { return message; } diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/log4j2.xml new file mode 100644 index 000000000..28e03a9e0 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <!-- We buffer everything to implicitly test buffer flushing --> + <BufferingAppender name="BufferedAppender" bufferAtVerbosity="INFO"> + <AppenderRef ref="JsonAppender" /> + </BufferingAppender> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="BufferedAppender" /> + </Root> + </Loggers> +</Configuration> diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml new file mode 100644 index 000000000..022f029e6 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -0,0 +1,82 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>e2e-test-handler-logging-log4j</artifactId> + <packaging>jar</packaging> + <name>E2E test handler – Logging Log4j</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging-log4j/src/main/java/software/amazon/lambda/powertools/e2e/Function.java similarity index 72% rename from powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java rename to powertools-e2e-tests/handlers/logging-log4j/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 62ebabc6e..94520c447 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -14,23 +14,27 @@ package software.amazon.lambda.powertools.e2e; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; + import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.logging.PowertoolsLogging; public class Function implements RequestHandler<Input, String> { - - private static final Logger LOG = LogManager.getLogger(Function.class); + private static final Logger LOG = LoggerFactory.getLogger(Function.class); @Logging public String handleRequest(Input input, Context context) { - - LoggingUtils.appendKeys(input.getKeys()); + input.getKeys().forEach(MDC::put); LOG.info(input.getMessage()); + // Flush buffer manually since we buffer at INFO level to test log buffering + PowertoolsLogging.flushBuffer(); + return "OK"; } -} \ No newline at end of file +} diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging-log4j/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..66fd49ddc --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import java.util.Map; + +public class Input { + private String message; + private Map<String, String> keys; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Map<String, String> getKeys() { + return keys; + } + + public void setKeys(Map<String, String> keys) { + this.keys = keys; + } +} diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/log4j2.xml new file mode 100644 index 000000000..28e03a9e0 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <!-- We buffer everything to implicitly test buffer flushing --> + <BufferingAppender name="BufferedAppender" bufferAtVerbosity="INFO"> + <AppenderRef ref="JsonAppender" /> + </BufferingAppender> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="BufferedAppender" /> + </Root> + </Loggers> +</Configuration> diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml new file mode 100644 index 000000000..f8458db25 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -0,0 +1,82 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>e2e-test-handler-logging-logback</artifactId> + <packaging>jar</packaging> + <name>E2E test handler – Logging Logback</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-logback</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..94520c447 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.PowertoolsLogging; + +public class Function implements RequestHandler<Input, String> { + private static final Logger LOG = LoggerFactory.getLogger(Function.class); + + @Logging + public String handleRequest(Input input, Context context) { + input.getKeys().forEach(MDC::put); + LOG.info(input.getMessage()); + + // Flush buffer manually since we buffer at INFO level to test log buffering + PowertoolsLogging.flushBuffer(); + + return "OK"; + } +} diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..66fd49ddc --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import java.util.Map; + +public class Input { + private String message; + private Map<String, String> keys; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Map<String, String> getKeys() { + return keys; + } + + public void setKeys(Map<String, String> keys) { + this.keys = keys; + } +} diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..a603a9398 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlogback.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/logback.xml b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/logback.xml new file mode 100644 index 000000000..0a5e4d146 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/logback.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <appender name="JsonAppender" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"/> + </appender> + + <!-- We buffer everything to implicitly test buffer flushing --> + <appender name="BufferedAppender" class="software.amazon.lambda.powertools.logging.logback.BufferingAppender"> + <bufferAtVerbosity>INFO</bufferAtVerbosity> + <appender-ref ref="JsonAppender"/> + </appender> + + <root level="INFO"> + <appender-ref ref="BufferedAppender"/> + </root> +</configuration> \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 35db53899..ddc6ae1bd 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -1,16 +1,16 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) Parameters</name> + <name>E2E test handler – Metrics</name> <dependencies> <dependency> @@ -21,7 +21,18 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> @@ -54,4 +65,18 @@ </plugin> </plugins> </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index d9cf575c3..7244b3212 100644 --- a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -16,25 +16,36 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; -import software.amazon.lambda.powertools.metrics.MetricsUtils; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import java.time.Instant; public class Function implements RequestHandler<Input, String> { - MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); + Metrics metrics = MetricsFactory.getMetricsInstance(); - @Metrics(captureColdStart = true) + @FlushMetrics(captureColdStart = true) public String handleRequest(Input input, Context context) { + Instant currentTimeTruncatedPlusThirty = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES) + .toInstant(ZoneOffset.UTC).plusSeconds(30); + metrics.setTimestamp(currentTimeTruncatedPlusThirty); + DimensionSet dimensionSet = new DimensionSet(); input.getDimensions().forEach((key, value) -> dimensionSet.addDimension(key, value)); - metricsLogger.putDimensions(dimensionSet); + metrics.addDimension(dimensionSet); - input.getMetrics().forEach((key, value) -> metricsLogger.putMetric(key, value, Unit.COUNT)); + input.getMetrics().forEach((key, value) -> metrics.addMetric(key, value, MetricUnit.COUNT, + input.getHighResolution().equalsIgnoreCase("true") ? MetricResolution.HIGH + : MetricResolution.STANDARD)); return "OK"; } -} \ No newline at end of file +} diff --git a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 18c4eb747..054c2867a 100644 --- a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -21,8 +21,7 @@ public class Input { private Map<String, String> dimensions; - public Input() { - } + private String highResolution; public Map<String, Double> getMetrics() { return metrics; @@ -32,6 +31,14 @@ public void setMetrics(Map<String, Double> metrics) { this.metrics = metrics; } + public String getHighResolution() { + return highResolution; + } + + public void setHighResolution(String highResolution) { + this.highResolution = highResolution; + } + public Map<String, String> getDimensions() { return dimensions; } diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 410cdfb5e..fb2deb2aa 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -1,31 +1,42 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> <packaging>jar</packaging> - <name>A Lambda function using powertools logging</name> + <name>E2E test handler – Parameters</name> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> + <artifactId>powertools-parameters-appconfig</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> @@ -58,4 +69,18 @@ </plugin> </plugins> </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 3a83a1b05..1e151825e 100644 --- a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -17,14 +17,18 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.parameters.AppConfigProvider; -import software.amazon.lambda.powertools.parameters.ParamManager; +import software.amazon.lambda.powertools.parameters.appconfig.AppConfigProvider; public class Function implements RequestHandler<Input, String> { @Logging public String handleRequest(Input input, Context context) { - AppConfigProvider provider = ParamManager.getAppConfigProvider(input.getEnvironment(), input.getApp()); + AppConfigProvider provider = AppConfigProvider.builder() + .withApplication(input.getApp()) + .withEnvironment(input.getEnvironment()) + .build(); + + //(input.getEnvironment(), input.getApp()); return provider.get(input.getKey()); } diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 6e82c7aec..477b49dc0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -1,36 +1,45 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.17.0-SNAPSHOT</lambda.powertools.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - - <lambda.java.core>1.2.2</lambda.java.core> - <lambda.java.serialization>1.1.2</lambda.java.serialization> - <lambda.java.events>3.11.2</lambda.java.events> - <maven.shade.version>3.5.0</maven.shade.version> - <aspectj.plugin.version>1.13.1</aspectj.plugin.version> - <maven.compiler.version>3.11.0</maven.compiler.version> - <aws.sdk.version>2.20.108</aws.sdk.version> - <log4j.version>2.20.0</log4j.version> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <lambda.java.core>1.4.0</lambda.java.core> + <lambda.java.serialization>1.1.6</lambda.java.serialization> + <lambda.java.events>3.16.1</lambda.java.events> + <maven.shade.version>3.6.1</maven.shade.version> + <aspectj.plugin.version>1.14.1</aspectj.plugin.version> + <maven.compiler.version>3.14.1</maven.compiler.version> + <aws.sdk.version>2.40.9</aws.sdk.version> + <aspectj.version>1.9.20.1</aspectj.version> + <maven.deploy.skip>true</maven.deploy.skip> </properties> <modules> - <module>logging</module> + <module>batch</module> + <module>largemessage</module> + <module>largemessage-functional</module> + <module>largemessage_idempotent</module> + <module>logging-log4j</module> + <module>logging-logback</module> + <module>logging-functional</module> <module>tracing</module> <module>metrics</module> <module>idempotency</module> + <module>idempotency-functional</module> + <module>idempotency-generics</module> <module>parameters</module> + <module>validation-alb-event</module> + <module>validation-apigw-event</module> </modules> <dependencyManagement> @@ -42,41 +51,65 @@ <type>pom</type> <scope>import</scope> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-logback</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>${lambda.powertools.version}</version> + <version>${project.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>${lambda.powertools.version}</version> + <version>${project.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>${lambda.powertools.version}</version> + <version>${project.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>${lambda.powertools.version}</version> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-dynamodb</artifactId> + <version>${project.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - <version>${lambda.powertools.version}</version> + <artifactId>powertools-parameters-appconfig</artifactId> + <version>${project.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-large-messages</artifactId> - <version>${lambda.powertools.version}</version> + <version>${project.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-batch</artifactId> - <version>${lambda.powertools.version}</version> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-validation</artifactId> + <version>${project.version}</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -94,9 +127,9 @@ <version>${lambda.java.serialization}</version> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> - <version>${log4j.version}</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.7</version> </dependency> </dependencies> </dependencyManagement> @@ -121,17 +154,16 @@ <configuration> <transformers> <transformer - implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"> - </transformer> + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> </transformers> </configuration> </execution> </executions> <dependencies> <dependency> - <groupId>io.github.edwgiz</groupId> - <artifactId>log4j-maven-shade-plugin-extensions</artifactId> - <version>2.17.2</version> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.2.0</version> </dependency> </dependencies> </plugin> @@ -145,96 +177,98 @@ <useIncrementalCompilation>false</useIncrementalCompilation> </configuration> </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>${aspectj.plugin.version}</version> + <configuration> + <verbose>true</verbose> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <Xlint>ignore</Xlint> + <encoding>${project.build.sourceEncoding}</encoding> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + <goal>test-compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>handler</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <arg>--enable-url-protocols=http</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> </plugins> </pluginManagement> </build> <profiles> + <!-- https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md --> <profile> - <id>jdk8</id> + <id>jdk11to16</id> <activation> - <jdk>(,11)</jdk> <!-- < 11 --> + <jdk>[11,16]</jdk> </activation> <properties> <aspectj.version>1.9.7</aspectj.version> </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </dependencyManagement> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <Xlint>ignore</Xlint> - <encoding>${project.build.sourceEncoding}</encoding> - </configuration> - <executions> - <execution> - <phase>process-sources</phase> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - <!-- Enforce aspectJ 1.9.7 --> - <dependencies> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjtools</artifactId> - <version>${aspectj.version}</version> - </dependency> - </dependencies> - </plugin> - </plugins> - </pluginManagement> - </build> </profile> <profile> - <id>jdk11plus</id> + <id>jdk17to20</id> <activation> - <jdk>[11,)</jdk> <!-- >= 11 --> + <jdk>[17,20]</jdk> </activation> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.plugin.version}</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <Xlint>ignore</Xlint> - <encoding>${project.build.sourceEncoding}</encoding> - </configuration> - <executions> - <execution> - <phase>process-sources</phase> - <goals> - <goal>compile</goal> - <goal>test-compile</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </pluginManagement> - </build> + <properties> + <aspectj.version>1.9.20.1</aspectj.version> + </properties> + </profile> + <profile> + <id>jdk21</id> + <activation> + <jdk>[21,)</jdk> + </activation> + <properties> + <aspectj.version>1.9.21</aspectj.version> + </properties> + </profile> + <profile> + <id>jdk25</id> + <activation> + <jdk>[25,)</jdk> + </activation> + <properties> + <aspectj.version>1.9.25</aspectj.version> + </properties> </profile> </profiles> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 252009aa9..9874ce986 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -1,16 +1,16 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> <packaging>jar</packaging> - <name>A Lambda function using powertools tracing</name> + <name>E2E test handler – Tracing</name> <dependencies> <dependency> @@ -21,7 +21,18 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> @@ -54,4 +65,18 @@ </plugin> </plugins> </build> + + <profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 397e34a85..0f140a20d 100644 --- a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -52,4 +52,4 @@ private String buildMessage(String message, String funcName) { } return String.format("%s (%s)", message, funcName); } -} \ No newline at end of file +} diff --git a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 92078d0b3..ed89f4498 100644 --- a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -17,9 +17,6 @@ public class Input { private String message; - public Input() { - } - public String getMessage() { return message; } diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"<init>","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"<init>","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..467af67a0 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,62 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true + } +] diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml similarity index 81% rename from powertools-e2e-tests/handlers/logging/pom.xml rename to powertools-e2e-tests/handlers/validation-alb-event/pom.xml index 4b613f2bf..14dbb9b13 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,23 +5,26 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.9.0</version> </parent> - <artifactId>e2e-test-handler-logging</artifactId> + <artifactId>e2e-test-handler-validation-alb-event</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) logging</name> + <name>E2E test handler – Validation ALB event</name> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-validation</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> @@ -36,7 +39,7 @@ <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-validation</artifactId> </aspectLibrary> </aspectLibraries> </configuration> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/validation-alb-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..d221ee153 --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-alb-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; + +import software.amazon.lambda.powertools.validation.Validation; + +public class Function + implements RequestHandler<ApplicationLoadBalancerRequestEvent, ApplicationLoadBalancerResponseEvent> { + @Validation(inboundSchema = "classpath:/validation/inbound_schema.json", outboundSchema = "classpath:/validation/outbound_schema.json") + public ApplicationLoadBalancerResponseEvent handleRequest(ApplicationLoadBalancerRequestEvent input, + Context context) { + ApplicationLoadBalancerResponseEvent response = new ApplicationLoadBalancerResponseEvent(); + response.setBody(input.getBody()); + response.setStatusCode(200); + response.setIsBase64Encoded(false); + return response; + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="JsonAppender"/> + </Root> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/inbound_schema.json b/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/inbound_schema.json new file mode 100644 index 000000000..3665879eb --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/inbound_schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/product.json", + "type": "object", + "title": "Product schema", + "description": "JSON schema to validate Products", + "default": {}, + "examples": [ + { + "id": 43242, + "name": "FooBar XY", + "price": 258 + } + ], + "required": [ + "price" + ], + "properties": { + "price": { + "$id": "#/properties/price", + "type": "number", + "title": "Price of the product", + "description": "Positive price of the product", + "default": 0, + "exclusiveMinimum": 0, + "examples": [ + 258.99 + ] + } + }, + "additionalProperties": true +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/outbound_schema.json b/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/outbound_schema.json new file mode 100644 index 000000000..b1f14d025 --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/outbound_schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/product.json", + "type": "object", + "title": "Product schema", + "description": "JSON schema to validate Products", + "default": {}, + "examples": [ + { + "id": 43242, + "name": "FooBar XY", + "price": 258 + } + ], + "required": [ + "price" + ], + "properties": { + "price": { + "$id": "#/properties/price", + "type": "number", + "title": "Price of the product", + "description": "Positive price of the product", + "default": 0, + "exclusiveMaximum": 1000, + "examples": [ + 258.99 + ] + } + }, + "additionalProperties": true +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml new file mode 100644 index 000000000..290e47b13 --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -0,0 +1,60 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>e2e-test-handler-validation-apigw-event</artifactId> + <packaging>jar</packaging> + <name>E2E test handler – Validation API Gateway event</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-validation</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-validation</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..5ac8951d8 --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + +import software.amazon.lambda.powertools.validation.Validation; + +public class Function implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + @Validation(inboundSchema = "classpath:/validation/inbound_schema.json", outboundSchema = "classpath:/validation/outbound_schema.json") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent(); + response.setBody(input.getBody()); + response.setStatusCode(200); + response.setIsBase64Encoded(false); + return response; + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="JsonAppender"/> + </Root> + <Logger name="JsonLogger" level="INFO" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/inbound_schema.json b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/inbound_schema.json new file mode 100644 index 000000000..3665879eb --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/inbound_schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/product.json", + "type": "object", + "title": "Product schema", + "description": "JSON schema to validate Products", + "default": {}, + "examples": [ + { + "id": 43242, + "name": "FooBar XY", + "price": 258 + } + ], + "required": [ + "price" + ], + "properties": { + "price": { + "$id": "#/properties/price", + "type": "number", + "title": "Price of the product", + "description": "Positive price of the product", + "default": 0, + "exclusiveMinimum": 0, + "examples": [ + 258.99 + ] + } + }, + "additionalProperties": true +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/outbound_schema.json b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/outbound_schema.json new file mode 100644 index 000000000..b1f14d025 --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/outbound_schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/product.json", + "type": "object", + "title": "Product schema", + "description": "JSON schema to validate Products", + "default": {}, + "examples": [ + { + "id": 43242, + "name": "FooBar XY", + "price": 258 + } + ], + "required": [ + "price" + ], + "properties": { + "price": { + "$id": "#/properties/price", + "type": "number", + "title": "Price of the product", + "description": "Positive price of the product", + "default": 0, + "exclusiveMaximum": 1000, + "examples": [ + 258.99 + ] + } + }, + "additionalProperties": true +} \ No newline at end of file diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 534b65d3b..fec4dec92 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -14,27 +14,24 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> - <name>Powertools for AWS Lambda (Java)library End-to-end tests</name> - <description>Powertools for AWS Lambda (Java)End-To-End Tests</description> + <name>Powertools for AWS Lambda (Java) - End-to-end tests</name> + <description>Powertools for AWS Lambda (Java) – End-To-End Tests</description> <properties> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.69</constructs.version> - <cdk.version>2.91.0</cdk.version> - - <!-- Don't deploy the e2e tests --> - <maven.deploy.skip>true</maven.deploy.skip> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <constructs.version>10.4.3</constructs.version> + <cdk.version>2.224.0</cdk.version> </properties> <dependencies> @@ -43,14 +40,12 @@ <artifactId>log4j-slf4j2-impl</artifactId> <scope>test</scope> </dependency> - <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>lambda</artifactId> <version>${aws.sdk.version}</version> <scope>test</scope> </dependency> - <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>dynamodb</artifactId> @@ -69,72 +64,67 @@ <version>${aws.sdk.version}</version> <scope>test</scope> </dependency> - <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>xray</artifactId> <version>${aws.sdk.version}</version> <scope>test</scope> </dependency> - <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>sqs</artifactId> <version>${aws.sdk.version}</version> <scope>test</scope> </dependency> - <dependency> <groupId>com.amazonaws</groupId> <artifactId>amazon-sqs-java-extended-client-lib</artifactId> - <version>2.0.3</version> + <version>2.1.2</version> <scope>test</scope> </dependency> - <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> <scope>test</scope> </dependency> - <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> - <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.13.0</version> + <version>2.21.0</version> </dependency> - <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> - + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>com.evanlennick</groupId> - <artifactId>retry4j</artifactId> - <version>0.15.0</version> + <groupId>io.github.resilience4j</groupId> + <artifactId>resilience4j-retry</artifactId> + <!-- 2.x. not compatible with Java 11 anymore --> + <version>1.7.1</version> <scope>test</scope> </dependency> - <dependency> <groupId>software.amazon.awscdk</groupId> <artifactId>aws-cdk-lib</artifactId> <version>${cdk.version}</version> <scope>test</scope> </dependency> - <dependency> <groupId>software.constructs</groupId> <artifactId>constructs</artifactId> @@ -162,7 +152,17 @@ <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> - <version>2.1</version> + <version>2.5</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> <scope>test</scope> </dependency> <dependency> @@ -177,26 +177,36 @@ </dependency> </dependencies> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.11.0</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <encoding>UTF-8</encoding> - </configuration> - </plugin> - </plugins> - </build> - <profiles> + <profile> + <id>default</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <build> + <plugins> + <!-- Don't deploy the e2e tests --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + </plugins> + </build> + </profile> <profile> <id>e2e</id> <build> @@ -204,7 +214,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>3.1.2</version> <executions> <execution> <goals> @@ -214,7 +223,8 @@ </execution> </executions> <configuration> - <skipAfterFailureCount>1</skipAfterFailureCount> <!-- no need to continue / deploy more resources and lose time --> + <skipAfterFailureCount>1</skipAfterFailureCount> <!-- no need to continue / deploy more + resources and lose time --> <includes> <include>**/*E2ET.java</include> </includes> @@ -223,6 +233,39 @@ </plugins> </build> </profile> + <profile> + <id>e2e-graal</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> + <configuration> + <skipAfterFailureCount>1</skipAfterFailureCount> + <includes> + <!-- Add additional E2E tests here when adding new GraalVM support to utilities. --> + <include>**/MetricsE2ET.java</include> + <include>**/LoggingE2ET.java</include> + <include>**/ParametersE2ET.java</include> + <include>**/TracingE2ET.java</include> + <include>**/IdempotencyE2ET.java</include> + </includes> + <systemPropertyVariables> + <graalvm.enabled>true</graalvm.enabled> + </systemPropertyVariables> + </configuration> + </plugin> + </plugins> + </build> + </profile> </profiles> -</project> \ No newline at end of file +</project> diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/BatchE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/BatchE2ET.java index c5f74594d..181d5e583 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/BatchE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/BatchE2ET.java @@ -15,10 +15,7 @@ package software.amazon.lambda.powertools; import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -26,11 +23,16 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -44,18 +46,18 @@ import software.amazon.awssdk.services.kinesis.KinesisClient; import software.amazon.awssdk.services.kinesis.model.PutRecordsRequest; import software.amazon.awssdk.services.kinesis.model.PutRecordsRequestEntry; -import software.amazon.awssdk.services.kinesis.model.PutRecordsResponse; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; +import software.amazon.lambda.powertools.testutils.DataNotReadyException; import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.RetryUtils; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class BatchE2ET { +class BatchE2ET { private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); private static final Region region = Region.of(System.getProperty("AWS_DEFAULT_REGION", "eu-west-1")); private static Infrastructure infrastructure; - private static String functionName; private static String queueUrl; private static String kinesisStreamName; @@ -71,13 +73,12 @@ public BatchE2ET() { testProducts = Arrays.asList( new Product(1, "product1", 1.23), new Product(2, "product2", 4.56), - new Product(3, "product3", 6.78) - ); + new Product(3, "product3", 6.78)); } @BeforeAll @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { + static void setup() { String random = UUID.randomUUID().toString().substring(0, 6); String queueName = "batchqueue" + random; kinesisStreamName = "batchstream" + random; @@ -94,7 +95,6 @@ public static void setup() { .build(); Map<String, String> outputs = infrastructure.deploy(); - functionName = outputs.get(FUNCTION_NAME_OUTPUT); queueUrl = outputs.get("QueueURL"); kinesisStreamName = outputs.get("KinesisStreamName"); outputTable = outputs.get("TableNameForAsyncTests"); @@ -117,21 +117,21 @@ public static void setup() { } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @AfterEach - public void cleanUpTest() { + void cleanUpTest() { // Delete everything in the output table ScanResponse items = ddbClient.scan(ScanRequest.builder() .tableName(outputTable) .build()); for (Map<String, AttributeValue> item : items.items()) { - HashMap<String, AttributeValue> key = new HashMap<String, AttributeValue>() { + Map<String, AttributeValue> key = new HashMap<>() { { put("functionName", AttributeValue.builder() .s(item.get("functionName").s()) @@ -150,7 +150,7 @@ public void cleanUpTest() { } @Test - public void sqsBatchProcessingSucceeds() throws InterruptedException { + void sqsBatchProcessingSucceeds() { List<SendMessageBatchRequestEntry> entries = testProducts.stream() .map(p -> { try { @@ -169,17 +169,23 @@ public void sqsBatchProcessingSucceeds() throws InterruptedException { .entries(entries) .queueUrl(queueUrl) .build()); - Thread.sleep(30000); // wait for function to be executed // THEN - ScanResponse items = ddbClient.scan(ScanRequest.builder() - .tableName(outputTable) - .build()); - validateAllItemsHandled(items); + ScanRequest scanRequest = ScanRequest.builder().tableName(outputTable).build(); + RetryUtils.withRetry(() -> { + ScanResponse items = ddbClient.scan(scanRequest); + if (!areAllTestProductsPresent(items)) { + throw new DataNotReadyException("sqs-batch-processing not complete yet"); + } + return null; + }, "sqs-batch-processing", DataNotReadyException.class).get(); + + ScanResponse finalItems = ddbClient.scan(scanRequest); + assertThat(areAllTestProductsPresent(finalItems)).isTrue(); } @Test - public void kinesisBatchProcessingSucceeds() throws InterruptedException { + void kinesisBatchProcessingSucceeds() { List<PutRecordsRequestEntry> entries = testProducts.stream() .map(p -> { try { @@ -194,21 +200,27 @@ public void kinesisBatchProcessingSucceeds() throws InterruptedException { .collect(Collectors.toList()); // WHEN - PutRecordsResponse result = kinesisClient.putRecords(PutRecordsRequest.builder() + kinesisClient.putRecords(PutRecordsRequest.builder() .streamName(kinesisStreamName) .records(entries) .build()); - Thread.sleep(30000); // wait for function to be executed // THEN - ScanResponse items = ddbClient.scan(ScanRequest.builder() - .tableName(outputTable) - .build()); - validateAllItemsHandled(items); + ScanRequest scanRequest = ScanRequest.builder().tableName(outputTable).build(); + RetryUtils.withRetry(() -> { + ScanResponse items = ddbClient.scan(scanRequest); + if (!areAllTestProductsPresent(items)) { + throw new DataNotReadyException("kinesis-batch-processing not complete yet"); + } + return null; + }, "kinesis-batch-processing", DataNotReadyException.class).get(); + + ScanResponse finalItems = ddbClient.scan(scanRequest); + assertThat(areAllTestProductsPresent(finalItems)).isTrue(); } @Test - public void ddbStreamsBatchProcessingSucceeds() throws InterruptedException { + void ddbStreamsBatchProcessingSucceeds() { // GIVEN String theId = "my-test-id"; @@ -223,39 +235,43 @@ public void ddbStreamsBatchProcessingSucceeds() throws InterruptedException { } }) .build()); - Thread.sleep(90000); // wait for function to be executed // THEN - ScanResponse items = ddbClient.scan(ScanRequest.builder() - .tableName(outputTable) - .build()); + ScanRequest scanRequest = ScanRequest.builder().tableName(outputTable).build(); + RetryUtils.withRetry(() -> { + ScanResponse items = ddbClient.scan(scanRequest); + if (items.count() != 1) { + throw new DataNotReadyException("DDB streams processing not complete yet"); + } + return null; + }, "ddb-streams-batch-processing", DataNotReadyException.class).get(); - assertThat(items.count()).isEqualTo(1); - assertThat(items.items().get(0).get("id").s()).isEqualTo(theId); + ScanResponse finalItems = ddbClient.scan(scanRequest); + assertThat(finalItems.count()).isEqualTo(1); + assertThat(finalItems.items().get(0).get("id").s()).isEqualTo(theId); } - private void validateAllItemsHandled(ScanResponse items) { + private boolean areAllTestProductsPresent(ScanResponse items) { for (Product p : testProducts) { boolean foundIt = false; for (Map<String, AttributeValue> a : items.items()) { if (a.get("id").s().equals(Long.toString(p.id))) { foundIt = true; + break; } } - assertThat(foundIt).isTrue(); + if (!foundIt) { + return false; + } } + return true; } class Product { private long id; - private String name; - private double price; - public Product() { - } - public Product(long id, String name, double price) { this.id = id; this.name = name; diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java index 242d1a2db..9ced0e4fb 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java @@ -21,40 +21,45 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; + import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -public class IdempotencyE2ET { - private static Infrastructure infrastructure; - private static String functionName; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class IdempotencyE2ET { + private Infrastructure infrastructure; + private String functionName; - @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { + private void setupInfrastructure(String pathToFunction) { String random = UUID.randomUUID().toString().substring(0, 6); infrastructure = Infrastructure.builder() - .testName(IdempotencyE2ET.class.getSimpleName()) - .pathToFunction("idempotency") + .testName(IdempotencyE2ET.class.getSimpleName() + "-" + pathToFunction) + .pathToFunction(pathToFunction) .idempotencyTable("idempo" + random) .build(); Map<String, String> outputs = infrastructure.deploy(); functionName = outputs.get(FUNCTION_NAME_OUTPUT); } - @AfterAll - public static void tearDown() { + @AfterEach + void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } - @Test - public void test_ttlNotExpired_sameResult_ttlExpired_differentResult() throws InterruptedException { + @ParameterizedTest + @ValueSource(strings = { "idempotency", "idempotency-functional", "idempotency-generics" }) + @Timeout(value = 15, unit = TimeUnit.MINUTES) + void test_ttlNotExpired_sameResult_ttlExpired_differentResult(String pathToFunction) throws InterruptedException { + setupInfrastructure(pathToFunction); // GIVEN String event = "{\"message\":\"TTL 10sec\"}"; @@ -65,6 +70,7 @@ public void test_ttlNotExpired_sameResult_ttlExpired_differentResult() throws In // Second invocation (should get same result) InvocationResult result2 = invokeFunction(functionName, event); + // Function idempotency record expiration is set to 10 seconds Thread.sleep(12000); // Third invocation (should get different result) diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java index 548a710b8..0de2dca60 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java @@ -3,8 +3,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; -import com.amazon.sqs.javamessaging.AmazonSQSExtendedClient; -import com.amazon.sqs.javamessaging.ExtendedClientConfiguration; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -14,14 +12,20 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; + import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazon.sqs.javamessaging.AmazonSQSExtendedClient; +import com.amazon.sqs.javamessaging.ExtendedClientConfiguration; + import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -33,9 +37,12 @@ import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; +import software.amazon.lambda.powertools.testutils.DataNotReadyException; import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.RetryUtils; -public class LargeMessageE2ET { +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class LargeMessageE2ET { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageE2ET.class); private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); @@ -50,25 +57,34 @@ public class LargeMessageE2ET { .region(region) .build(); - private static Infrastructure infrastructure; - private static String functionName; - private static String bucketName; - private static String queueUrl; - private static String tableName; + private Infrastructure infrastructure; + private String functionName; + private String bucketName; + private String queueUrl; + private String tableName; private String messageId; + private String currentPathToFunction; + + private void setupInfrastructure(String pathToFunction) { + // Do not re-deploy the same function + if (pathToFunction.equals(currentPathToFunction)) { + return; + } + + // Destroy any existing infrastructure before re-deploying + if (infrastructure != null) { + infrastructure.destroy(); + } - @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { String random = UUID.randomUUID().toString().substring(0, 6); bucketName = "largemessagebucket" + random; String queueName = "largemessagequeue" + random; infrastructure = Infrastructure.builder() - .testName(LargeMessageE2ET.class.getSimpleName()) + .testName(LargeMessageE2ET.class.getSimpleName() + "-" + pathToFunction) .queue(queueName) .largeMessagesBucket(bucketName) - .pathToFunction("largemessage") + .pathToFunction(pathToFunction) .timeoutInSeconds(60) .build(); @@ -76,19 +92,24 @@ public static void setup() { functionName = outputs.get(FUNCTION_NAME_OUTPUT); queueUrl = outputs.get("QueueURL"); tableName = outputs.get("TableNameForAsyncTests"); + currentPathToFunction = pathToFunction; - LOG.info("Testing '" + LargeMessageE2ET.class.getSimpleName() + "'"); + LOG.info("Testing '{}' with {}", LargeMessageE2ET.class.getSimpleName(), pathToFunction); } @AfterAll - public static void tearDown() { + void cleanup() { if (infrastructure != null) { infrastructure.destroy(); } } @AfterEach - public void reset() { + void tearDown() { + reset(); + } + + private void reset() { if (messageId != null) { Map<String, AttributeValue> itemToDelete = new HashMap<>(); itemToDelete.put("functionName", AttributeValue.builder().s(functionName).build()); @@ -98,27 +119,29 @@ public void reset() { } } - @Test - public void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException, InterruptedException { - // given - final ExtendedClientConfiguration extendedClientConfig = - new ExtendedClientConfiguration() - .withPayloadSupportEnabled(s3Client, bucketName); - AmazonSQSExtendedClient client = - new AmazonSQSExtendedClient(SqsClient.builder().httpClient(httpClient).build(), extendedClientConfig); - InputStream inputStream = this.getClass().getResourceAsStream("/large_sqs_message.txt"); - String bigMessage = IOUtils.toString(inputStream, StandardCharsets.UTF_8); - - // when - client.sendMessage(SendMessageRequest - .builder() - .queueUrl(queueUrl) - .messageBody(bigMessage) - .build()); - - Thread.sleep(30000); // wait for function to be executed + @ParameterizedTest + @ValueSource(strings = { "largemessage", "largemessage-functional" }) + @Timeout(value = 5, unit = TimeUnit.MINUTES) + void bigSQSMessageOffloadedToS3_shouldLoadFromS3(String pathToFunction) throws IOException { + setupInfrastructure(pathToFunction); + + // GIVEN + final ExtendedClientConfiguration extendedClientConfig = new ExtendedClientConfiguration() + .withPayloadSupportEnabled(s3Client, bucketName); + try (AmazonSQSExtendedClient client = new AmazonSQSExtendedClient( + SqsClient.builder().region(region).httpClient(httpClient).build(), extendedClientConfig); + InputStream inputStream = this.getClass().getResourceAsStream("/large_sqs_message.txt");) { + String bigMessage = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + + // WHEN + client.sendMessage(SendMessageRequest + .builder() + .queueUrl(queueUrl) + .messageBody(bigMessage) + .build()); + } - // then + // THEN QueryRequest request = QueryRequest .builder() .tableName(tableName) @@ -126,47 +149,68 @@ public void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException, In .expressionAttributeValues( Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) .build(); - QueryResponse response = dynamoDbClient.query(request); - List<Map<String, AttributeValue>> items = response.items(); + + RetryUtils.withRetry(() -> { + QueryResponse response = dynamoDbClient.query(request); + if (response.items().size() != 1) { + throw new DataNotReadyException("Large message processing not complete yet"); + } + return null; + }, "large-message-processing", DataNotReadyException.class).get(); + + QueryResponse finalResponse = dynamoDbClient.query(request); + List<Map<String, AttributeValue>> items = finalResponse.items(); assertThat(items).hasSize(1); messageId = items.get(0).get("id").s(); assertThat(Integer.valueOf(items.get(0).get("bodySize").n())).isEqualTo(300977); assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("22bde5e7b05fa80bc7be45bdd4bc6c75"); } - @Test - public void smallSQSMessage_shouldNotReadFromS3() throws IOException, InterruptedException { - // given - final ExtendedClientConfiguration extendedClientConfig = - new ExtendedClientConfiguration() - .withPayloadSupportEnabled(s3Client, bucketName); - AmazonSQSExtendedClient client = - new AmazonSQSExtendedClient(SqsClient.builder().httpClient(httpClient).build(), extendedClientConfig); - String message = "Hello World"; - - // when - client.sendMessage(SendMessageRequest - .builder() - .queueUrl(queueUrl) - .messageBody(message) - .build()); - - Thread.sleep(30000); // wait for function to be executed - - // then - QueryRequest request = QueryRequest - .builder() - .tableName(tableName) - .keyConditionExpression("functionName = :func") - .expressionAttributeValues( - Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) - .build(); - QueryResponse response = dynamoDbClient.query(request); - List<Map<String, AttributeValue>> items = response.items(); - assertThat(items).hasSize(1); - messageId = items.get(0).get("id").s(); - assertThat(Integer.valueOf(items.get(0).get("bodySize").n())).isEqualTo( - message.getBytes(StandardCharsets.UTF_8).length); - assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("b10a8db164e0754105b7a99be72e3fe5"); + @ParameterizedTest + @ValueSource(strings = { "largemessage", "largemessage-functional" }) + @Timeout(value = 5, unit = TimeUnit.MINUTES) + void smallSQSMessage_shouldNotReadFromS3(String pathToFunction) { + setupInfrastructure(pathToFunction); + + // GIVEN + final ExtendedClientConfiguration extendedClientConfig = new ExtendedClientConfiguration() + .withPayloadSupportEnabled(s3Client, bucketName); + try (AmazonSQSExtendedClient client = new AmazonSQSExtendedClient( + SqsClient.builder().region(region).httpClient(httpClient).build(), + extendedClientConfig)) { + String message = "Hello World"; + + // WHEN + client.sendMessage(SendMessageRequest + .builder() + .queueUrl(queueUrl) + .messageBody(message) + .build()); + + // THEN + QueryRequest request = QueryRequest + .builder() + .tableName(tableName) + .keyConditionExpression("functionName = :func") + .expressionAttributeValues( + Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) + .build(); + + RetryUtils.withRetry(() -> { + QueryResponse response = dynamoDbClient.query(request); + if (response.items().size() != 1) { + throw new DataNotReadyException("Small message processing not complete yet"); + } + return null; + }, "small-message-processing", DataNotReadyException.class).get(); + + QueryResponse finalResponse = dynamoDbClient.query(request); + List<Map<String, AttributeValue>> items = finalResponse.items(); + assertThat(items).hasSize(1); + messageId = items.get(0).get("id").s(); + assertThat(Integer.valueOf(items.get(0).get("bodySize").n())).isEqualTo( + message.getBytes(StandardCharsets.UTF_8).length); + assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("b10a8db164e0754105b7a99be72e3fe5"); + } } } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java index e8ee3ca5c..cd787b60a 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; + import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -31,6 +32,7 @@ import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -44,9 +46,11 @@ import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.awssdk.services.sqs.model.MessageAttributeValue; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; +import software.amazon.lambda.powertools.testutils.DataNotReadyException; import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.RetryUtils; -public class LargeMessageIdempotentE2ET { +class LargeMessageIdempotentE2ET { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageIdempotentE2ET.class); private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); @@ -72,10 +76,9 @@ public class LargeMessageIdempotentE2ET { private static String queueUrl; private static String tableName; - @BeforeAll @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { + static void setup() { String random = UUID.randomUUID().toString().substring(0, 6); bucketName = "largemessagebucket" + random; String queueName = "largemessagequeue" + random; @@ -98,34 +101,33 @@ public static void setup() { } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @Test - public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throws InterruptedException, + void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throws InterruptedException, IOException { - int waitMs = 10000; - // GIVEN - InputStream inputStream = this.getClass().getResourceAsStream("/large_sqs_message.txt"); - String bigMessage = IOUtils.toString(inputStream, StandardCharsets.UTF_8); - - // upload manually to S3 - String key = UUID.randomUUID().toString(); - s3Client.putObject(PutObjectRequest.builder() - .bucket(bucketName) - .key(key) - .build(), RequestBody.fromString(bigMessage)); + String s3Key = UUID.randomUUID().toString(); + try (InputStream inputStream = this.getClass().getResourceAsStream("/large_sqs_message.txt")) { + String bigMessage = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + + // upload manually to S3 + s3Client.putObject(PutObjectRequest.builder() + .bucket(bucketName) + .key(s3Key) + .build(), RequestBody.fromString(bigMessage)); + } // WHEN SendMessageRequest messageRequest = SendMessageRequest.builder() .queueUrl(queueUrl) .messageBody(String.format( "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"%s\",\"s3Key\":\"%s\"}]", - bucketName, key)) + bucketName, s3Key)) .messageAttributes(Collections.singletonMap("SQSLargePayloadSize", MessageAttributeValue.builder() .stringValue("300977") .dataType("Number") @@ -135,7 +137,6 @@ public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throw // First invocation // send message to SQS with the good pointer and metadata sqsClient.sendMessage(messageRequest); - Thread.sleep(waitMs); // wait for the function to be invoked & executed // THEN QueryRequest request = QueryRequest @@ -145,6 +146,15 @@ public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throw .expressionAttributeValues( Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) .build(); + + RetryUtils.withRetry(() -> { + QueryResponse response = dynamoDbClient.query(request); + if (response.items().size() != 1) { + throw new DataNotReadyException("First invocation processing not complete yet"); + } + return null; + }, "first-invocation-processing", DataNotReadyException.class).get(); + QueryResponse response = dynamoDbClient.query(request); List<Map<String, AttributeValue>> items = response.items(); assertThat(items).hasSize(1); @@ -156,7 +166,14 @@ public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throw // Second invocation // send the same message before ttl expires sqsClient.sendMessage(messageRequest); - Thread.sleep(waitMs); // wait for the function to be invoked & executed + + RetryUtils.withRetry(() -> { + QueryResponse resp = dynamoDbClient.query(request); + if (resp.items().size() != 1) { + throw new DataNotReadyException("Second invocation processing not complete yet"); + } + return null; + }, "second-invocation-processing", DataNotReadyException.class).get(); // THEN response = dynamoDbClient.query(request); @@ -174,7 +191,14 @@ public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throw // Third invocation // send the same message again sqsClient.sendMessage(messageRequest); - Thread.sleep(waitMs); // wait for the function to be invoked + + RetryUtils.withRetry(() -> { + QueryResponse resp = dynamoDbClient.query(request); + if (resp.items().size() != 2) { + throw new DataNotReadyException("Third invocation processing not complete yet"); + } + return null; + }, "third-invocation-processing", DataNotReadyException.class).get(); // THEN response = dynamoDbClient.query(request); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java index f958970d8..20bc5394d 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java @@ -19,54 +19,62 @@ import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import static software.amazon.lambda.powertools.testutils.logging.InvocationLogs.Level.INFO; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -public class LoggingE2ET { +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class LoggingE2ET { private static final ObjectMapper objectMapper = new ObjectMapper(); - private static Infrastructure infrastructure; - private static String functionName; + private Infrastructure infrastructure; + private String functionName; - @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { + private void setupInfrastructure(String pathToFunction) { infrastructure = Infrastructure.builder() - .testName(LoggingE2ET.class.getSimpleName()) - .pathToFunction("logging") + .testName(LoggingE2ET.class.getSimpleName() + "-" + pathToFunction) + .tracing(true) + .pathToFunction(pathToFunction) .environmentVariables( Stream.of(new String[][] { - {"POWERTOOLS_LOG_LEVEL", "INFO"}, - {"POWERTOOLS_SERVICE_NAME", LoggingE2ET.class.getSimpleName()} - }) + { "POWERTOOLS_LOG_LEVEL", "INFO" }, + { "POWERTOOLS_SERVICE_NAME", LoggingE2ET.class.getSimpleName() } + }) .collect(Collectors.toMap(data -> data[0], data -> data[1]))) .build(); Map<String, String> outputs = infrastructure.deploy(); functionName = outputs.get(FUNCTION_NAME_OUTPUT); } - @AfterAll - public static void tearDown() { + @AfterEach + void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } - @Test - public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { + @ParameterizedTest + @ValueSource(strings = { "logging-log4j", "logging-logback", "logging-functional" }) + @Timeout(value = 15, unit = TimeUnit.MINUTES) + void test_logInfoWithAdditionalKeys(String pathToFunction) throws JsonProcessingException { + setupInfrastructure(pathToFunction); + // GIVEN String orderId = UUID.randomUUID().toString(); String event = "{\"message\":\"New Order\", \"keys\":{\"orderId\":\"" + orderId + "\"}}"; @@ -82,13 +90,14 @@ public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { JsonNode jsonNode = objectMapper.readTree(functionLogs[0]); assertThat(jsonNode.get("message").asText()).isEqualTo("New Order"); assertThat(jsonNode.get("orderId").asText()).isEqualTo(orderId); - assertThat(jsonNode.get("coldStart").asBoolean()).isTrue(); + assertThat(jsonNode.get("cold_start").asBoolean()).isTrue(); + assertThat(jsonNode.get("xray_trace_id").asText()).isNotBlank(); assertThat(jsonNode.get("function_request_id").asText()).isEqualTo(invocationResult1.getRequestId()); // second call should not be cold start functionLogs = invocationResult2.getLogs().getFunctionLogs(INFO); assertThat(functionLogs).hasSize(1); jsonNode = objectMapper.readTree(functionLogs[0]); - assertThat(jsonNode.get("coldStart").asBoolean()).isFalse(); + assertThat(jsonNode.get("cold_start").asBoolean()).isFalse(); } } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java index 80673b995..35f8b5ba3 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java @@ -18,6 +18,9 @@ import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +import java.time.Clock; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.List; import java.util.Map; @@ -25,31 +28,35 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; + +import software.amazon.lambda.powertools.testutils.DataNotReadyException; import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.RetryUtils; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; import software.amazon.lambda.powertools.testutils.metrics.MetricsFetcher; -public class MetricsE2ET { - private static final String namespace = "MetricsE2ENamespace_" + UUID.randomUUID(); - private static final String service = "MetricsE2EService_" + UUID.randomUUID(); +class MetricsE2ET { + private static final String NAMESPACE = "MetricsE2ENamespace_" + UUID.randomUUID(); + private static final String SERVICE = "MetricsE2EService_" + UUID.randomUUID(); private static Infrastructure infrastructure; private static String functionName; @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { + @Timeout(value = 10, unit = TimeUnit.MINUTES) + static void setup() { infrastructure = Infrastructure.builder() .testName(MetricsE2ET.class.getSimpleName()) .pathToFunction("metrics") .environmentVariables( Stream.of(new String[][] { - {"POWERTOOLS_METRICS_NAMESPACE", namespace}, - {"POWERTOOLS_SERVICE_NAME", service} - }) + { "POWERTOOLS_METRICS_NAMESPACE", NAMESPACE }, + { "POWERTOOLS_SERVICE_NAME", SERVICE } + }) .collect(Collectors.toMap(data -> data[0], data -> data[1]))) .build(); Map<String, String> outputs = infrastructure.deploy(); @@ -57,45 +64,66 @@ public static void setup() { } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @Test - public void test_recordMetrics() { + void test_recordMetrics() { // GIVEN - String event1 = - "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"} }"; + Instant currentTimeTruncatedToMinutes = Instant.now(Clock.systemUTC()).truncatedTo(ChronoUnit.MINUTES); + String event1 = "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"}, \"highResolution\": \"false\"}"; + + String event2 = "{ \"metrics\": {\"orders\": 1, \"products\": 8}, \"dimensions\": { \"Environment\": \"test\"}, \"highResolution\": \"true\"}"; // WHEN InvocationResult invocationResult = invokeFunction(functionName, event1); + invokeFunction(functionName, event2); + // THEN MetricsFetcher metricsFetcher = new MetricsFetcher(); - List<Double> coldStart = - metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, - "ColdStart", Stream.of(new String[][] { - {"FunctionName", functionName}, - {"Service", service}} - ).collect(Collectors.toMap(data -> data[0], data -> data[1]))); + List<Double> coldStart = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, + NAMESPACE, + "ColdStart", Stream.of(new String[][] { + { "FunctionName", functionName }, + { "Service", SERVICE } }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); assertThat(coldStart.get(0)).isEqualTo(1); - List<Double> orderMetrics = - metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, - "orders", Collections.singletonMap("Environment", "test")); - assertThat(orderMetrics.get(0)).isEqualTo(1); - List<Double> productMetrics = - metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, - "products", Collections.singletonMap("Environment", "test")); - assertThat(productMetrics.get(0)).isEqualTo(4); - orderMetrics = - metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, - "orders", Collections.singletonMap("Service", service)); - assertThat(orderMetrics.get(0)).isEqualTo(1); - productMetrics = - metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, - "products", Collections.singletonMap("Service", service)); - assertThat(productMetrics.get(0)).isEqualTo(4); + List<Double> orderMetrics = RetryUtils.withRetry(() -> { + List<Double> metrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), + 60, NAMESPACE, "orders", Collections.singletonMap("Environment", "test")); + if (metrics.get(0) != 2.0) { + throw new DataNotReadyException("Expected 2.0 orders but got " + metrics.get(0)); + } + return metrics; + }, "orderMetricsRetry", DataNotReadyException.class).get(); + assertThat(orderMetrics.get(0)).isEqualTo(2); + List<Double> productMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), + invocationResult.getEnd(), 60, NAMESPACE, + "products", Collections.singletonMap("Environment", "test")); + + // When searching across a 1 minute time period with a period of 60 we find both metrics and the sum is 12 + assertThat(productMetrics.get(0)).isEqualTo(12); + + orderMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, + NAMESPACE, + "orders", Collections.singletonMap("Service", SERVICE)); + assertThat(orderMetrics.get(0)).isEqualTo(2); + productMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, + NAMESPACE, + "products", Collections.singletonMap("Service", SERVICE)); + assertThat(productMetrics.get(0)).isEqualTo(12); + + Instant searchStartTime = currentTimeTruncatedToMinutes.plusSeconds(15); + Instant searchEndTime = currentTimeTruncatedToMinutes.plusSeconds(45); + + List<Double> productMetricDataResult = metricsFetcher.fetchMetrics(searchStartTime, searchEndTime, 1, NAMESPACE, + "products", Collections.singletonMap("Environment", "test")); + + // We are searching across the time period the metric was created but with a period of 1 second. Only the high + // resolution metric will be available at this point + assertThat(productMetricDataResult.get(0)).isEqualTo(8); } } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java index 9582f9f23..39254a9e6 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java @@ -24,22 +24,24 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; + import software.amazon.lambda.powertools.testutils.AppConfig; import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class ParametersE2ET { +class ParametersE2ET { private final AppConfig appConfig; private Infrastructure infrastructure; private String functionName; - public ParametersE2ET() { + ParametersE2ET() { String appName = UUID.randomUUID().toString(); Map<String, String> params = new HashMap<>(); params.put("key1", "value1"); @@ -48,17 +50,17 @@ public ParametersE2ET() { } @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public void setup() { + @Timeout(value = 15, unit = TimeUnit.MINUTES) + void setup() { infrastructure = Infrastructure.builder() .testName(ParametersE2ET.class.getSimpleName()) .pathToFunction("parameters") .appConfig(appConfig) .environmentVariables( Stream.of(new String[][] { - {"POWERTOOLS_LOG_LEVEL", "INFO"}, - {"POWERTOOLS_SERVICE_NAME", ParametersE2ET.class.getSimpleName()} - }) + { "POWERTOOLS_LOG_LEVEL", "INFO" }, + { "POWERTOOLS_SERVICE_NAME", ParametersE2ET.class.getSimpleName() } + }) .collect(Collectors.toMap(data -> data[0], data -> data[1]))) .build(); Map<String, String> outputs = infrastructure.deploy(); @@ -66,14 +68,14 @@ public void setup() { } @AfterAll - public void tearDown() { + void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @Test - public void test_getAppConfigValue() { + void test_getAppConfigValue() { for (Map.Entry<String, String> configKey : appConfig.getConfigurationValues().entrySet()) { // Arrange diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java index 0827d91ae..13d8adb9b 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java @@ -15,61 +15,65 @@ package software.amazon.lambda.powertools; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; -import java.util.Collections; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; + import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; import software.amazon.lambda.powertools.testutils.tracing.Trace; import software.amazon.lambda.powertools.testutils.tracing.TraceFetcher; -public class TracingE2ET { - private static final String service = "TracingE2EService_" + UUID.randomUUID(); +class TracingE2ET { + private static final String SERVICE = "TracingE2EService_" + UUID.randomUUID(); private static Infrastructure infrastructure; private static String functionName; @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { + @Timeout(value = 15, unit = TimeUnit.MINUTES) + static void setup() { infrastructure = Infrastructure.builder() .testName(TracingE2ET.class.getSimpleName()) .pathToFunction("tracing") .tracing(true) - .environmentVariables(Collections.singletonMap("POWERTOOLS_SERVICE_NAME", service)) + .environmentVariables( + Map.of("POWERTOOLS_SERVICE_NAME", SERVICE, + "POWERTOOLS_TRACER_CAPTURE_RESPONSE", "true")) .build(); Map<String, String> outputs = infrastructure.deploy(); functionName = outputs.get(FUNCTION_NAME_OUTPUT); } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @Test - public void test_tracing() { + void test_tracing() { // GIVEN - String message = "Hello World"; - String event = String.format("{\"message\":\"%s\"}", message); - String result = String.format("%s (%s)", message, functionName); + final String message = "Hello World"; + final String event = String.format("{\"message\":\"%s\"}", message); + final String result = String.format("%s (%s)", message, functionName); // WHEN - InvocationResult invocationResult = invokeFunction(functionName, event); + final InvocationResult invocationResult = invokeFunction(functionName, event); // THEN - Trace trace = TraceFetcher.builder() + final Trace trace = TraceFetcher.builder() .start(invocationResult.getStart()) .end(invocationResult.getEnd()) .functionName(functionName) @@ -77,29 +81,35 @@ public void test_tracing() { .fetchTrace(); assertThat(trace.getSubsegments()).hasSize(1); - SubSegment handleRequest = trace.getSubsegments().get(0); - assertThat(handleRequest.getName()).isEqualTo("## handleRequest"); - assertThat(handleRequest.getAnnotations()).hasSize(2); - assertThat(handleRequest.getAnnotations().get("ColdStart")).isEqualTo(true); - assertThat(handleRequest.getAnnotations().get("Service")).isEqualTo(service); - assertThat(handleRequest.getMetadata()).hasSize(1); - Map<String, Object> metadata = (Map<String, Object>) handleRequest.getMetadata().get(service); - assertThat(metadata.get("handleRequest response")).isEqualTo(result); - assertThat(handleRequest.getSubsegments()).hasSize(2); - - SubSegment sub = handleRequest.getSubsegments().get(0); + + final SubSegment handleRequestSegment = trace.getSubsegments().stream() + .filter(subSegment -> "## handleRequest".equals(subSegment.getName())) + .findFirst().orElse(null); + assertNotNull(handleRequestSegment); + assertThat(handleRequestSegment.getName()).isEqualTo("## handleRequest"); + assertThat(handleRequestSegment.getAnnotations()).hasSize(2); + assertThat(handleRequestSegment.getAnnotations()).containsEntry("ColdStart", true); + assertThat(handleRequestSegment.getAnnotations()).containsEntry("Service", SERVICE); + assertThat(handleRequestSegment.getMetadata()).hasSize(1); + final Map<String, Object> metadata = (Map<String, Object>) handleRequestSegment.getMetadata().get(SERVICE); + assertThat(metadata).containsEntry("handleRequest response", result); + assertThat(handleRequestSegment.getSubsegments()).hasSize(2); + + SubSegment sub = handleRequestSegment.getSubsegments().get(0); assertThat(sub.getName()).isIn("## internal_stuff", "## buildMessage"); - sub = handleRequest.getSubsegments().get(1); + sub = handleRequestSegment.getSubsegments().get(1); assertThat(sub.getName()).isIn("## internal_stuff", "## buildMessage"); - SubSegment buildMessage = handleRequest.getSubsegments().stream() - .filter(subSegment -> subSegment.getName().equals("## buildMessage")).findFirst().orElse(null); + SubSegment buildMessage = handleRequestSegment.getSubsegments().stream() + .filter(subSegment -> "## buildMessage".equals(subSegment.getName())) + .findFirst().orElse(null); assertThat(buildMessage).isNotNull(); assertThat(buildMessage.getAnnotations()).hasSize(1); - assertThat(buildMessage.getAnnotations().get("message")).isEqualTo(message); + assertThat(buildMessage.getAnnotations()).containsEntry("message", message); assertThat(buildMessage.getMetadata()).hasSize(1); - metadata = (Map<String, Object>) buildMessage.getMetadata().get(service); - assertThat(metadata.get("buildMessage response")).isEqualTo(result); + final Map<String, Object> buildMessageSegmentMetadata = (Map<String, Object>) buildMessage.getMetadata() + .get(SERVICE); + assertThat(buildMessageSegmentMetadata).containsEntry("buildMessage response", result); } } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationALBE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationALBE2ET.java new file mode 100644 index 000000000..bf90851cb --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationALBE2ET.java @@ -0,0 +1,107 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; + +class ValidationALBE2ET { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static Infrastructure infrastructure; + private static String functionName; + + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + static void setup() { + infrastructure = Infrastructure.builder().testName(ValidationALBE2ET.class.getSimpleName()) + .pathToFunction("validation-alb-event").build(); + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); + } + + @AfterAll + static void tearDown() { + if (infrastructure != null) { + infrastructure.destroy(); + } + } + + @Test + void test_validInboundSQSEvent() throws IOException { + try (InputStream is = this.getClass().getResourceAsStream("/validation/valid_alb_in_out_event.json")) { + String validEvent = IOUtils.toString(is, StandardCharsets.UTF_8); + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, validEvent); + + // THEN + // invocation should pass validation and return 200 + JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult()); + assertThat(validJsonNode.get("statusCode").asInt()).isEqualTo(200); + assertThat(validJsonNode.get("body").asText()).isEqualTo("{\"price\": 150}"); + } + } + + @Test + void test_invalidInboundSQSEvent() throws IOException { + try (InputStream is = this.getClass().getResourceAsStream("/validation/invalid_alb_in_event.json")) { + String invalidEvent = IOUtils.toString(is, StandardCharsets.UTF_8); + + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, invalidEvent); + + // THEN + // invocation should fail inbound validation and return an error message + JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult()); + assertThat(validJsonNode.get("errorMessage").asText()).contains(": required property 'price' not found"); + } + } + + @Test + void test_invalidOutboundSQSEvent() throws IOException { + try (InputStream is = this.getClass().getResourceAsStream("/validation/invalid_alb_out_event.json")) { + String invalidEvent = IOUtils.toString(is, StandardCharsets.UTF_8); + + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, invalidEvent); + + // THEN + // invocation should fail outbound validation and return 400 + JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult()); + assertThat(validJsonNode.get("errorMessage").asText()) + .contains("/price: must have an exclusive maximum value of 1000"); + } + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationApiGWE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationApiGWE2ET.java new file mode 100644 index 000000000..2d1bf4657 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationApiGWE2ET.java @@ -0,0 +1,110 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; + +class ValidationApiGWE2ET { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static Infrastructure infrastructure; + private static String functionName; + + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + static void setup() { + infrastructure = Infrastructure.builder().testName(ValidationApiGWE2ET.class.getSimpleName()) + .pathToFunction("validation-apigw-event").build(); + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); + } + + @AfterAll + static void tearDown() { + if (infrastructure != null) { + infrastructure.destroy(); + } + } + + @Test + void test_validInboundApiGWEvent() throws IOException { + try (InputStream is = this.getClass().getResourceAsStream("/validation/valid_api_gw_in_out_event.json")) { + String validEvent = IOUtils.toString(is, StandardCharsets.UTF_8); + + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, validEvent); + + // THEN + // invocation should pass validation and return 200 + JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult()); + assertThat(validJsonNode.get("statusCode").asInt()).isEqualTo(200); + assertThat(validJsonNode.get("body").asText()).isEqualTo("{\"price\": 150}"); + } + } + + @Test + void test_invalidInboundApiGWEvent() throws IOException { + try (InputStream is = this.getClass().getResourceAsStream("/validation/invalid_api_gw_in_event.json")) { + String invalidEvent = IOUtils.toString(is, StandardCharsets.UTF_8); + + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, invalidEvent); + + // THEN + // invocation should fail inbound validation and return 400 + JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult()); + assertThat(validJsonNode.get("statusCode").asInt()).isEqualTo(400); + assertThat(validJsonNode.get("body").asText()).contains(": required property 'price' not found"); + } + } + + @Test + void test_invalidOutboundApiGWEvent() throws IOException { + try (InputStream is = this.getClass().getResourceAsStream("/validation/invalid_api_gw_out_event.json")) { + String invalidEvent = IOUtils.toString(is, StandardCharsets.UTF_8); + + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, invalidEvent); + + // THEN + // invocation should fail outbound validation and return 400 + JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult()); + assertThat(validJsonNode.get("statusCode").asInt()).isEqualTo(400); + assertThat(validJsonNode.get("body").asText()) + .contains("/price: must have an exclusive maximum value of 1000"); + } + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DataNotReadyException.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DataNotReadyException.java new file mode 100644 index 000000000..0c16bb68b --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DataNotReadyException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.testutils; + +/** + * Exception thrown when test data is not ready yet. + * This exception is used to trigger retries in tests waiting for async operations. + */ +public class DataNotReadyException extends RuntimeException { + public DataNotReadyException(String message) { + super(message); + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DockerConfiguration.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DockerConfiguration.java new file mode 100644 index 000000000..0508534c2 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DockerConfiguration.java @@ -0,0 +1,182 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.testutils; + +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import software.amazon.awscdk.BundlingOptions; +import software.amazon.awscdk.BundlingOutput; +import software.amazon.awscdk.DockerImage; +import software.amazon.awscdk.DockerVolume; + +/** + * Configuration class for managing build environments and Docker settings + * used during Lambda function compilation. + */ +public class DockerConfiguration { + private final String baseImage; + private final List<String> buildArgs; + private final Map<String, String> environmentVariables; + private final List<DockerVolume> volumes; + + private DockerConfiguration(Builder builder) { + this.baseImage = builder.baseImage; + this.buildArgs = builder.buildArgs; + this.environmentVariables = builder.environmentVariables; + this.volumes = builder.volumes; + } + + public static Builder builder() { + return new Builder(); + } + + public String getBaseImage() { + return baseImage; + } + + public List<String> getBuildArgs() { + return buildArgs; + } + + public Map<String, String> getEnvironmentVariables() { + return environmentVariables; + } + + public List<DockerVolume> getVolumes() { + return volumes; + } + + /** + * Creates bundling options for GraalVM native image compilation. + */ + public BundlingOptions createGraalVMBundlingOptions(String pathToFunction, JavaRuntime runtime) { + List<String> packagingInstruction = Arrays.asList( + "/bin/sh", + "-c", + "cd " + pathToFunction + + " && timeout -s SIGKILL 10m mvn clean package -Pnative-image -ff" + + " -Dmaven.test.skip=true" + + " -Dmaven.compiler.source=" + runtime.getMvnProperty() + + " -Dmaven.compiler.target=" + runtime.getMvnProperty() + + " && mkdir -p /tmp/lambda-package" + + " && cp /asset-input/" + pathToFunction + "/target/handler /tmp/lambda-package/" + + " && chmod +x /tmp/lambda-package/handler" + + " && echo '#!/bin/bash\nset -e\n./handler $_HANDLER' > /tmp/lambda-package/bootstrap" + + " && chmod +x /tmp/lambda-package/bootstrap" + + " && cd /tmp/lambda-package" + + " && zip -r /asset-output/function.zip ."); + + return BundlingOptions.builder() + .command(packagingInstruction) + .image(DockerImage.fromRegistry(baseImage)) + .volumes(volumes) + .environment(environmentVariables) + .user("root") + .outputType(BundlingOutput.ARCHIVED) + .platform("linux/amd64") + .build(); + } + + /** + * Creates bundling options for standard JVM compilation. + */ + public BundlingOptions createJVMBundlingOptions(String pathToFunction, JavaRuntime runtime) { + List<String> packagingInstruction = Arrays.asList( + "/bin/sh", + "-c", + "cd " + pathToFunction + + " && timeout -s SIGKILL 5m mvn clean install -ff" + + " -Dmaven.test.skip=true" + + " -Dmaven.compiler.source=" + runtime.getMvnProperty() + + " -Dmaven.compiler.target=" + runtime.getMvnProperty() + + " && cp /asset-input/" + pathToFunction + "/target/function.jar /asset-output/"); + + return BundlingOptions.builder() + .command(packagingInstruction) + .image(DockerImage.fromRegistry(baseImage)) + .volumes(volumes) + .user("root") + .outputType(BundlingOutput.ARCHIVED) + .platform("linux/amd64") + .build(); + } + + /** + * Creates a default Docker configuration for GraalVM native image compilation. + */ + public static DockerConfiguration createGraalVMDefault(JavaRuntime runtime) { + // Use custom Dockerfile for GraalVM + String dockerDir = Paths.get(System.getProperty("user.dir"), "src", "test", "resources", "docker").toString(); + DockerImage customImage = DockerImage.fromBuild(dockerDir); + + return builder() + .baseImage(customImage.getImage()) + .environmentVariables(Map.of("JAVA_VERSION", runtime.getMvnProperty())) + .volumes(List.of( + DockerVolume.builder() + .hostPath(System.getProperty("user.home") + "/.m2/") + .containerPath("/root/.m2/") + .build())) + .build(); + } + + /** + * Creates a default Docker configuration for standard JVM compilation. + */ + public static DockerConfiguration createJVMDefault(JavaRuntime runtime) { + return builder() + .baseImage(runtime.getCdkRuntime().getBundlingImage().getImage()) + .volumes(List.of( + DockerVolume.builder() + .hostPath(System.getProperty("user.home") + "/.m2/") + .containerPath("/root/.m2/") + .build())) + .build(); + } + + public static class Builder { + private String baseImage; + private List<String> buildArgs; + private Map<String, String> environmentVariables; + private List<DockerVolume> volumes; + + public Builder baseImage(String baseImage) { + this.baseImage = baseImage; + return this; + } + + public Builder buildArgs(List<String> buildArgs) { + this.buildArgs = buildArgs; + return this; + } + + public Builder environmentVariables(Map<String, String> environmentVariables) { + this.environmentVariables = environmentVariables; + return this; + } + + public Builder volumes(List<DockerVolume> volumes) { + this.volumes = volumes; + return this; + } + + public DockerConfiguration build() { + return new DockerConfiguration(this); + } + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java index b1fab2883..ae96943c2 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java @@ -16,25 +16,25 @@ import static java.util.Collections.singletonList; -import com.fasterxml.jackson.databind.JsonNode; import java.io.File; import java.io.IOException; import java.nio.file.Paths; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; + +import com.fasterxml.jackson.databind.JsonNode; + import software.amazon.awscdk.App; import software.amazon.awscdk.BundlingOptions; -import software.amazon.awscdk.BundlingOutput; import software.amazon.awscdk.CfnOutput; -import software.amazon.awscdk.DockerVolume; +import software.amazon.awscdk.DefaultStackSynthesizer; import software.amazon.awscdk.Duration; import software.amazon.awscdk.RemovalPolicy; import software.amazon.awscdk.Stack; @@ -45,12 +45,17 @@ import software.amazon.awscdk.services.appconfig.CfnDeploymentStrategy; import software.amazon.awscdk.services.appconfig.CfnEnvironment; import software.amazon.awscdk.services.appconfig.CfnHostedConfigurationVersion; -import software.amazon.awscdk.services.dynamodb.*; +import software.amazon.awscdk.services.dynamodb.Attribute; +import software.amazon.awscdk.services.dynamodb.AttributeType; +import software.amazon.awscdk.services.dynamodb.BillingMode; +import software.amazon.awscdk.services.dynamodb.StreamViewType; +import software.amazon.awscdk.services.dynamodb.Table; import software.amazon.awscdk.services.iam.PolicyStatement; import software.amazon.awscdk.services.kinesis.Stream; import software.amazon.awscdk.services.kinesis.StreamMode; import software.amazon.awscdk.services.lambda.Code; import software.amazon.awscdk.services.lambda.Function; +import software.amazon.awscdk.services.lambda.Runtime; import software.amazon.awscdk.services.lambda.StartingPosition; import software.amazon.awscdk.services.lambda.Tracing; import software.amazon.awscdk.services.lambda.eventsources.DynamoEventSource; @@ -89,10 +94,11 @@ * CloudWatch log groups, ... * <br/> * It uses the Cloud Development Kit (CDK) to define required resources. The CDK stack is then synthesized to retrieve - * the CloudFormation templates and the assets (function jars). Assets are uploaded to S3 (with the SDK `PutObjectRequest`) + * the CloudFormation templates and the assets (function jars). Assets are uploaded to S3 (with the SDK + * `PutObjectRequest`) * and the CloudFormation stack is created (with the SDK `createStack`) */ -public class Infrastructure { +public final class Infrastructure { public static final String FUNCTION_NAME_OUTPUT = "functionName"; private static final Logger LOG = LoggerFactory.getLogger(Infrastructure.class); @@ -115,7 +121,6 @@ public class Infrastructure { private final String kinesisStream; private final String largeMessagesBucket; private String ddbStreamsTableName; - private String functionName; private Object cfnTemplate; private String cfnAssetDirectory; @@ -174,11 +179,11 @@ public Map<String, String> deploy() { .onFailure(OnFailure.ROLLBACK) .capabilities(Capability.CAPABILITY_IAM) .build()); - WaiterResponse<DescribeStacksResponse> waiterResponse = - cfn.waiter().waitUntilStackCreateComplete(DescribeStacksRequest.builder().stackName(stackName).build()); + WaiterResponse<DescribeStacksResponse> waiterResponse = cfn.waiter() + .waitUntilStackCreateComplete(DescribeStacksRequest.builder().stackName(stackName).build()); if (waiterResponse.matched().response().isPresent()) { - software.amazon.awssdk.services.cloudformation.model.Stack deployedStack = - waiterResponse.matched().response().get().stacks().get(0); + software.amazon.awssdk.services.cloudformation.model.Stack deployedStack = waiterResponse.matched() + .response().get().stacks().get(0); LOG.info("Stack " + deployedStack.stackName() + " successfully deployed"); Map<String, String> outputs = new HashMap<>(); deployedStack.outputs().forEach(output -> outputs.put(output.outputKey(), output.outputValue())); @@ -203,65 +208,51 @@ public void destroy() { */ private Stack createStackWithLambda() { boolean createTableForAsyncTests = false; - Stack stack = new Stack(app, stackName); - List<String> packagingInstruction = Arrays.asList( - "/bin/sh", - "-c", - "cd " + pathToFunction + - " && timeout -s SIGKILL 5m mvn clean install -ff " + - " -Dmaven.test.skip=true " + - " -Dmaven.resources.skip=true " + - " -Dmaven.compiler.source=" + runtime.getMvnProperty() + - " -Dmaven.compiler.target=" + runtime.getMvnProperty() + - " && cp /asset-input/" + pathToFunction + "/target/function.jar /asset-output/" - ); - - BundlingOptions.Builder builderOptions = BundlingOptions.builder() - .command(packagingInstruction) - .image(runtime.getCdkRuntime().getBundlingImage()) - .volumes(singletonList( - // Mount local .m2 repo to avoid download all the dependencies again inside the container - DockerVolume.builder() - .hostPath(System.getProperty("user.home") + "/.m2/") - .containerPath("/root/.m2/") - .build() - )) - .user("root") - .outputType(BundlingOutput.ARCHIVED); - - functionName = stackName + "-function"; - CfnOutput.Builder.create(stack, FUNCTION_NAME_OUTPUT) + final Stack e2eStack = Stack.Builder.create(app, stackName) + .synthesizer(DefaultStackSynthesizer.Builder.create() + .generateBootstrapVersionRule(false) // Disable bootstrap version check + .build()) + .build(); + + boolean isGraalVMEnabled = Boolean.parseBoolean(System.getProperty("graalvm.enabled", "false")); + DockerConfiguration dockerConfig = isGraalVMEnabled + ? DockerConfiguration.createGraalVMDefault(runtime) + : DockerConfiguration.createJVMDefault(runtime); + + BundlingOptions bundlingOptions = isGraalVMEnabled + ? dockerConfig.createGraalVMBundlingOptions(pathToFunction, runtime) + : dockerConfig.createJVMBundlingOptions(pathToFunction, runtime); + + String functionName = stackName + "-function"; + CfnOutput.Builder.create(e2eStack, FUNCTION_NAME_OUTPUT) .value(functionName) .build(); - LOG.debug("Building Lambda function with command " + - packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); + LOG.debug("Building Lambda function with {} configuration", isGraalVMEnabled ? "GraalVM" : "JVM"); Function function = Function.Builder - .create(stack, functionName) + .create(e2eStack, functionName) .code(Code.fromAsset("handlers/", AssetOptions.builder() - .bundling(builderOptions - .command(packagingInstruction) - .build()) + .bundling(bundlingOptions) .build())) .functionName(functionName) .handler("software.amazon.lambda.powertools.e2e.Function::handleRequest") .memorySize(1024) .timeout(Duration.seconds(timeout)) - .runtime(runtime.getCdkRuntime()) + .runtime(isGraalVMEnabled ? Runtime.PROVIDED_AL2023 : runtime.getCdkRuntime()) .environment(envVar) .tracing(tracing ? Tracing.ACTIVE : Tracing.DISABLED) .build(); LogGroup.Builder - .create(stack, functionName + "-logs") + .create(e2eStack, functionName + "-logs") .logGroupName("/aws/lambda/" + functionName) .retention(RetentionDays.ONE_DAY) - .removalPolicy(RemovalPolicy.DESTROY) + .removalPolicy(RemovalPolicy.RETAIN) .build(); if (!StringUtils.isEmpty(idempotencyTable)) { Table table = Table.Builder - .create(stack, "IdempotencyTable") + .create(e2eStack, "IdempotencyTable") .billingMode(BillingMode.PAY_PER_REQUEST) .removalPolicy(RemovalPolicy.DESTROY) .partitionKey(Attribute.builder().name("id").type(AttributeType.STRING).build()) @@ -275,10 +266,11 @@ private Stack createStackWithLambda() { if (!StringUtils.isEmpty(queue)) { Queue sqsQueue = Queue.Builder - .create(stack, "SQSQueue") + .create(e2eStack, "SQSQueue") .queueName(queue) .visibilityTimeout(Duration.seconds(timeout * 6)) .retentionPeriod(Duration.seconds(timeout * 6)) + .removalPolicy(RemovalPolicy.DESTROY) .build(); DeadLetterQueue.builder() .queue(sqsQueue) @@ -293,16 +285,17 @@ private Stack createStackWithLambda() { .build(); function.addEventSource(sqsEventSource); CfnOutput.Builder - .create(stack, "QueueURL") + .create(e2eStack, "QueueURL") .value(sqsQueue.getQueueUrl()) .build(); createTableForAsyncTests = true; } if (!StringUtils.isEmpty(kinesisStream)) { Stream stream = Stream.Builder - .create(stack, "KinesisStream") + .create(e2eStack, "KinesisStream") .streamMode(StreamMode.ON_DEMAND) .streamName(kinesisStream) + .removalPolicy(RemovalPolicy.DESTROY) .build(); stream.grantRead(function); @@ -316,13 +309,13 @@ private Stack createStackWithLambda() { .build(); function.addEventSource(kinesisEventSource); CfnOutput.Builder - .create(stack, "KinesisStreamName") + .create(e2eStack, "KinesisStreamName") .value(stream.getStreamName()) .build(); } if (!StringUtils.isEmpty(ddbStreamsTableName)) { - Table ddbStreamsTable = Table.Builder.create(stack, "DDBStreamsTable") + Table ddbStreamsTable = Table.Builder.create(e2eStack, "DDBStreamsTable") .tableName(ddbStreamsTableName) .stream(StreamViewType.KEYS_ONLY) .removalPolicy(RemovalPolicy.DESTROY) @@ -336,12 +329,12 @@ private Stack createStackWithLambda() { .reportBatchItemFailures(true) .build(); function.addEventSource(ddbEventSource); - CfnOutput.Builder.create(stack, "DdbStreamsTestTable").value(ddbStreamsTable.getTableName()).build(); + CfnOutput.Builder.create(e2eStack, "DdbStreamsTestTable").value(ddbStreamsTable.getTableName()).build(); } if (!StringUtils.isEmpty(largeMessagesBucket)) { Bucket offloadBucket = Bucket.Builder - .create(stack, "LargeMessagesOffloadBucket") + .create(e2eStack, "LargeMessagesOffloadBucket") .removalPolicy(RemovalPolicy.RETAIN) // autodelete does not work without cdk deploy .bucketName(largeMessagesBucket) .build(); @@ -352,19 +345,19 @@ private Stack createStackWithLambda() { if (appConfig != null) { CfnApplication app = CfnApplication.Builder - .create(stack, "AppConfigApp") + .create(e2eStack, "AppConfigApp") .name(appConfig.getApplication()) .build(); CfnEnvironment environment = CfnEnvironment.Builder - .create(stack, "AppConfigEnvironment") + .create(e2eStack, "AppConfigEnvironment") .applicationId(app.getRef()) .name(appConfig.getEnvironment()) .build(); // Create a fast deployment strategy, so we don't have to wait ages CfnDeploymentStrategy fastDeployment = CfnDeploymentStrategy.Builder - .create(stack, "AppConfigDeployment") + .create(e2eStack, "AppConfigDeployment") .name("FastDeploymentStrategy") .deploymentDurationInMinutes(0) .finalBakeTimeInMinutes(0) @@ -377,20 +370,19 @@ private Stack createStackWithLambda() { .create() .actions(singletonList("appconfig:*")) .resources(singletonList("*")) - .build() - ); + .build()); CfnDeployment previousDeployment = null; for (Map.Entry<String, String> entry : appConfig.getConfigurationValues().entrySet()) { CfnConfigurationProfile configProfile = CfnConfigurationProfile.Builder - .create(stack, "AppConfigProfileFor" + entry.getKey()) + .create(e2eStack, "AppConfigProfileFor" + entry.getKey()) .applicationId(app.getRef()) .locationUri("hosted") .name(entry.getKey()) .build(); CfnHostedConfigurationVersion configVersion = CfnHostedConfigurationVersion.Builder - .create(stack, "AppConfigHostedVersionFor" + entry.getKey()) + .create(e2eStack, "AppConfigHostedVersionFor" + entry.getKey()) .applicationId(app.getRef()) .contentType("text/plain") .configurationProfileId(configProfile.getRef()) @@ -398,7 +390,7 @@ private Stack createStackWithLambda() { .build(); CfnDeployment deployment = CfnDeployment.Builder - .create(stack, "AppConfigDepoymentFor" + entry.getKey()) + .create(e2eStack, "AppConfigDepoymentFor" + entry.getKey()) .applicationId(app.getRef()) .environmentId(environment.getRef()) .deploymentStrategyId(fastDeployment.getRef()) @@ -415,7 +407,7 @@ private Stack createStackWithLambda() { } if (createTableForAsyncTests) { Table table = Table.Builder - .create(stack, "TableForAsyncTests") + .create(e2eStack, "TableForAsyncTests") .billingMode(BillingMode.PAY_PER_REQUEST) .removalPolicy(RemovalPolicy.DESTROY) .partitionKey(Attribute.builder().name("functionName").type(AttributeType.STRING).build()) @@ -424,10 +416,10 @@ private Stack createStackWithLambda() { table.grantReadWriteData(function); function.addEnvironment("TABLE_FOR_ASYNC_TESTS", table.getTableName()); - CfnOutput.Builder.create(stack, "TableNameForAsyncTests").value(table.getTableName()).build(); + CfnOutput.Builder.create(e2eStack, "TableNameForAsyncTests").value(table.getTableName()).build(); } - return stack; + return e2eStack; } /** @@ -444,18 +436,20 @@ private void synthesize() { */ private void uploadAssets() { Map<String, Asset> assets = findAssets(); - assets.forEach((objectKey, asset) -> - { - if (!asset.assetPath.endsWith(".jar")) { + assets.forEach((objectKey, asset) -> { + // .zip will be used for GraalVM bundles. + if (!asset.assetPath.endsWith(".jar") && !asset.assetPath.endsWith(".zip")) { + LOG.info("Skipping upload of {}", asset); return; } - ListObjectsV2Response objects = - s3.listObjectsV2(ListObjectsV2Request.builder().bucket(asset.bucketName).build()); + LOG.info("Uploading {}", asset); + + ListObjectsV2Response objects = s3 + .listObjectsV2(ListObjectsV2Request.builder().bucket(asset.bucketName).build()); if (objects.contents().stream().anyMatch(o -> o.key().equals(objectKey))) { - LOG.debug("Asset already exists, skipping"); + LOG.debug("{} already exists, skipping", asset); return; } - LOG.info("Uploading asset " + objectKey + " to " + asset.bucketName); s3.putObject(PutObjectRequest.builder().bucket(asset.bucketName).key(objectKey).build(), Paths.get(cfnAssetDirectory, asset.assetPath)); }); @@ -472,14 +466,21 @@ private Map<String, Asset> findAssets() { JsonNode jsonNode = JsonConfig.get().getObjectMapper() .readTree(new File(cfnAssetDirectory, stackName + ".assets.json")); JsonNode files = jsonNode.get("files"); - files.iterator().forEachRemaining(file -> - { + files.iterator().forEachRemaining(file -> { String assetPath = file.get("source").get("path").asText(); String assetPackaging = file.get("source").get("packaging").asText(); - String bucketName = - file.get("destinations").get("current_account-current_region").get("bucketName").asText(); - String objectKey = - file.get("destinations").get("current_account-current_region").get("objectKey").asText(); + JsonNode destinations = file.get("destinations"); + String bucketName = null; + String objectKey = null; + Iterator<String> fieldNames = destinations.fieldNames(); + while (fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + if (fieldName.startsWith("current_account-current_region")) { + bucketName = destinations.get(fieldName).get("bucketName").asText(); + objectKey = destinations.get(fieldName).get("objectKey").asText(); + break; + } + } Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account) .replace("${AWS::Region}", region.toString())); assets.put(objectKey, asset); @@ -490,7 +491,7 @@ private Map<String, Asset> findAssets() { return assets; } - public static class Builder { + public static final class Builder { public long timeoutInSeconds = 30; public String pathToFunction; public String testName; @@ -506,27 +507,28 @@ public static class Builder { private String ddbStreamsTableName; private Builder() { - getJavaRuntime(); + runtime = mapRuntimeVersion("JAVA_VERSION"); } - /** - * Retrieve the java runtime to use for the lambda function. - */ - private void getJavaRuntime() { - String javaVersion = System.getenv("JAVA_VERSION"); // must be set in GitHub actions + private JavaRuntime mapRuntimeVersion(String environmentVariableName) { + String javaVersion = System.getenv(environmentVariableName); // must be set in GitHub actions + JavaRuntime ret = null; if (javaVersion == null) { - throw new IllegalArgumentException("JAVA_VERSION is not set"); + throw new IllegalArgumentException(environmentVariableName + " is not set"); } - if (javaVersion.startsWith("8")) { - runtime = JavaRuntime.JAVA8AL2; - } else if (javaVersion.startsWith("11")) { - runtime = JavaRuntime.JAVA11; + if (javaVersion.startsWith("11")) { + ret = JavaRuntime.JAVA11; } else if (javaVersion.startsWith("17")) { - runtime = JavaRuntime.JAVA17; + ret = JavaRuntime.JAVA17; + } else if (javaVersion.startsWith("21")) { + ret = JavaRuntime.JAVA21; + } else if (javaVersion.startsWith("25")) { + ret = JavaRuntime.JAVA25; } else { throw new IllegalArgumentException("Unsupported Java version " + javaVersion); } - LOG.debug("Java Version set to {}, using runtime {}", javaVersion, runtime.getRuntime()); + LOG.debug("Java Version set to {}, using runtime variable {}", ret, javaVersion); + return ret; } public Infrastructure build() { diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java index c50fcab84..625a222aa 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java @@ -17,10 +17,10 @@ import software.amazon.awscdk.services.lambda.Runtime; public enum JavaRuntime { - JAVA8("java8", Runtime.JAVA_8, "1.8"), - JAVA8AL2("java8.al2", Runtime.JAVA_8_CORRETTO, "1.8"), JAVA11("java11", Runtime.JAVA_11, "11"), - JAVA17("java17", Runtime.JAVA_17, "17"); + JAVA17("java17", Runtime.JAVA_17, "17"), + JAVA21("java21", Runtime.JAVA_21, "21"), + JAVA25("java25", Runtime.JAVA_25, "25"); private final String runtime; private final Runtime cdkRuntime; diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/RetryUtils.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/RetryUtils.java new file mode 100644 index 000000000..ce64f04ea --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/RetryUtils.java @@ -0,0 +1,105 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.testutils; + +import java.time.Duration; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.retry.RetryConfig; + +/** + * Utility class for consistent retry configuration across all test utilities. + */ +public final class RetryUtils { + private static final Logger LOG = LoggerFactory.getLogger(RetryUtils.class); + + private static final RetryConfig DEFAULT_RETRY_CONFIG = RetryConfig.custom() + .maxAttempts(60) // 60 attempts over 5 minutes + .waitDuration(Duration.ofSeconds(5)) // 5 seconds between attempts + .build(); + + private RetryUtils() { + // Utility class + } + + /** + * Creates a retry instance with default configuration for the specified throwable types. + * + * @param name the name for the retry instance + * @param retryOnThrowables the throwable classes to retry on + * @return configured Retry instance + */ + @SafeVarargs + public static Retry createRetry(String name, Class<? extends Throwable>... retryOnThrowables) { + return createRetry(name, DEFAULT_RETRY_CONFIG, retryOnThrowables); + } + + /** + * Creates a retry instance with custom configuration for the specified throwable types. + * + * @param name the name for the retry instance + * @param customConfig the custom retry configuration + * @param retryOnThrowables the throwable classes to retry on + * @return configured Retry instance + */ + @SafeVarargs + public static Retry createRetry(String name, RetryConfig customConfig, + Class<? extends Throwable>... retryOnThrowables) { + RetryConfig config = RetryConfig.from(customConfig) + .retryExceptions(retryOnThrowables) + .build(); + + Retry retry = Retry.of(name, config); + retry.getEventPublisher().onRetry(event -> LOG.warn("Retry attempt {} for {}: {}", + event.getNumberOfRetryAttempts(), name, event.getLastThrowable().getMessage())); + + return retry; + } + + /** + * Decorates a supplier with retry logic for the specified throwable types. + * + * @param supplier the supplier to decorate + * @param name the name for the retry instance + * @param retryOnThrowables the throwable classes to retry on + * @return decorated supplier with retry logic + */ + @SafeVarargs + public static <T> Supplier<T> withRetry(Supplier<T> supplier, String name, + Class<? extends Throwable>... retryOnThrowables) { + Retry retry = createRetry(name, retryOnThrowables); + return Retry.decorateSupplier(retry, supplier); + } + + /** + * Decorates a supplier with custom retry logic for the specified throwable types. + * + * @param supplier the supplier to decorate + * @param name the name for the retry instance + * @param customConfig the custom retry configuration + * @param retryOnThrowables the throwable classes to retry on + * @return decorated supplier with retry logic + */ + @SafeVarargs + public static <T> Supplier<T> withRetry(Supplier<T> supplier, String name, RetryConfig customConfig, + Class<? extends Throwable>... retryOnThrowables) { + Retry retry = createRetry(name, customConfig, retryOnThrowables); + return Retry.decorateSupplier(retry, supplier); + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricDataNotFoundException.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricDataNotFoundException.java new file mode 100644 index 000000000..79478c14e --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricDataNotFoundException.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.testutils.metrics; + +import software.amazon.lambda.powertools.testutils.DataNotReadyException; + +/** + * Exception thrown when metric data is not found in CloudWatch. + * This exception is used to trigger retries as metrics may not be available immediately. + */ +public class MetricDataNotFoundException extends DataNotReadyException { + public MetricDataNotFoundException(String message) { + super(message); + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java index 00728f451..f856d8f2f 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java @@ -14,20 +14,16 @@ package software.amazon.lambda.powertools.testutils.metrics; -import static java.time.Duration.ofSeconds; - -import com.evanlennick.retry4j.CallExecutor; -import com.evanlennick.retry4j.CallExecutorBuilder; -import com.evanlennick.retry4j.Status; -import com.evanlennick.retry4j.config.RetryConfig; -import com.evanlennick.retry4j.config.RetryConfigBuilder; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.concurrent.Callable; +import java.util.function.Supplier; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -39,6 +35,7 @@ import software.amazon.awssdk.services.cloudwatch.model.MetricDataQuery; import software.amazon.awssdk.services.cloudwatch.model.MetricStat; import software.amazon.awssdk.services.cloudwatch.model.StandardUnit; +import software.amazon.lambda.powertools.testutils.RetryUtils; /** * Class in charge of retrieving the actual metrics of a Lambda execution on CloudWatch @@ -54,7 +51,8 @@ public class MetricsFetcher { .build(); /** - * Retrieve the metric values from start to end. Different parameters are required (see {@link CloudWatchClient#getMetricData} for more info). + * Retrieve the metric values from start to end. Different parameters are required (see + * {@link CloudWatchClient#getMetricData} for more info). * Use a retry mechanism as metrics may not be available instantaneously after a function runs. * * @param start @@ -66,21 +64,20 @@ public class MetricsFetcher { * @return */ public List<Double> fetchMetrics(Instant start, Instant end, int period, String namespace, String metricName, - Map<String, String> dimensions) { + Map<String, String> dimensions) { List<Dimension> dimensionsList = new ArrayList<>(); if (dimensions != null) { dimensions.forEach((key, value) -> dimensionsList.add(Dimension.builder().name(key).value(value).build())); } - Callable<List<Double>> callable = () -> - { + Supplier<List<Double>> supplier = () -> { LOG.debug("Get Metrics for namespace {}, start {}, end {}, metric {}, dimensions {}", namespace, start, end, metricName, dimensionsList); GetMetricDataResponse metricData = cloudwatch.getMetricData(GetMetricDataRequest.builder() .startTime(start) .endTime(end) .metricDataQueries(MetricDataQuery.builder() - .id(metricName.toLowerCase()) + .id(metricName.toLowerCase(Locale.ROOT)) .metricStat(MetricStat.builder() .unit(StandardUnit.COUNT) .metric(Metric.builder() @@ -96,25 +93,11 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String .build()); List<Double> values = metricData.metricDataResults().get(0).values(); if (values == null || values.isEmpty()) { - throw new Exception("No data found for metric " + metricName); + throw new MetricDataNotFoundException("No data found for metric " + metricName); } return values; }; - RetryConfig retryConfig = new RetryConfigBuilder() - .withMaxNumberOfTries(10) - .retryOnAnyException() - .withDelayBetweenTries(ofSeconds(2)) - .withRandomExponentialBackoff() - .build(); - CallExecutor<List<Double>> callExecutor = new CallExecutorBuilder<List<Double>>() - .config(retryConfig) - .afterFailedTryListener(s -> - { - LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); - }) - .build(); - Status<List<Double>> status = callExecutor.execute(callable); - return status.getResult(); + return RetryUtils.withRetry(supplier, "metrics-fetcher-" + metricName, MetricDataNotFoundException.class).get(); } } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java index 2b5b6e15b..0aed9e811 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java @@ -14,25 +14,25 @@ package software.amazon.lambda.powertools.testutils.tracing; -import static java.time.Duration.ofSeconds; - -import com.evanlennick.retry4j.CallExecutor; -import com.evanlennick.retry4j.CallExecutorBuilder; -import com.evanlennick.retry4j.Status; -import com.evanlennick.retry4j.config.RetryConfig; -import com.evanlennick.retry4j.config.RetryConfigBuilder; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.concurrent.Callable; +import java.util.function.Supplier; import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; + +import io.github.resilience4j.retry.RetryConfig; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -43,15 +43,17 @@ import software.amazon.awssdk.services.xray.model.GetTraceSummariesResponse; import software.amazon.awssdk.services.xray.model.TimeRangeType; import software.amazon.awssdk.services.xray.model.TraceSummary; +import software.amazon.lambda.powertools.testutils.RetryUtils; import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; /** * Class in charge of retrieving the actual traces of a Lambda execution on X-Ray */ public class TraceFetcher { - - private static final ObjectMapper MAPPER = - new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + private static final ObjectMapper MAPPER = JsonMapper.builder() + .disable(MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .build(); private static final Logger LOG = LoggerFactory.getLogger(TraceFetcher.class); private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); private static final Region region = Region.of(System.getProperty("AWS_DEFAULT_REGION", "eu-west-1")); @@ -88,27 +90,17 @@ public static Builder builder() { * @return traces */ public Trace fetchTrace() { - Callable<Trace> callable = () -> - { + Supplier<Trace> supplier = () -> { List<String> traceIds = getTraceIds(); return getTrace(traceIds); }; - RetryConfig retryConfig = new RetryConfigBuilder() - .withMaxNumberOfTries(10) - .retryOnAnyException() - .withDelayBetweenTries(ofSeconds(5)) - .withRandomExponentialBackoff() + RetryConfig customConfig = RetryConfig.custom() + .maxAttempts(120) // 120 attempts over 10 minutes + .waitDuration(Duration.ofSeconds(5)) // 5 seconds between attempts .build(); - CallExecutor<Trace> callExecutor = new CallExecutorBuilder<Trace>() - .config(retryConfig) - .afterFailedTryListener(s -> - { - LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); - }) - .build(); - Status<Trace> status = callExecutor.execute(callable); - return status.getResult(); + + return RetryUtils.withRetry(supplier, "trace-fetcher", customConfig, TraceNotFoundException.class).get(); } /** @@ -122,35 +114,46 @@ private Trace getTrace(List<String> traceIds) { .traceIds(traceIds) .build()); if (!tracesResponse.hasTraces()) { - throw new RuntimeException("No trace found"); + throw new TraceNotFoundException(String.format("No trace found for traceIds %s", traceIds)); } + Trace traceRes = new Trace(); - tracesResponse.traces().forEach(trace -> - { + tracesResponse.traces().forEach(trace -> { if (trace.hasSegments()) { - trace.segments().forEach(segment -> - { + trace.segments().forEach(segment -> { try { SegmentDocument document = MAPPER.readValue(segment.document(), SegmentDocument.class); - if (document.getOrigin().equals("AWS::Lambda::Function")) { - if (document.hasSubsegments()) { - getNestedSubSegments(document.getSubsegments(), traceRes, - Collections.emptyList()); + if ("AWS::Lambda::Function".equals(document.getOrigin()) && document.hasSubsegments()) { + LOG.debug("Populating subsegments for document {}", MAPPER.writeValueAsString(document)); + getNestedSubSegments(document.getSubsegments(), traceRes, Collections.emptyList()); + // If only the default (excluded) subsegments were populated we need to keep retrying for + // our custom subsegments. They might appear later. + if (traceRes.getSubsegments().isEmpty()) { + throw new TraceNotFoundException( + "Found AWS::Lambda::Function SegmentDocument with no non-excluded subsegments."); } + } else if ("AWS::Lambda::Function".equals(document.getOrigin())) { + LOG.debug( + "Found AWS::Lambda::Function SegmentDocument with no subsegments. Retrying {}", + MAPPER.writeValueAsString(document)); + throw new TraceNotFoundException( + "Found AWS::Lambda::Function SegmentDocument with no subsegments."); } } catch (JsonProcessingException e) { LOG.error("Failed to parse segment document: " + e.getMessage()); throw new RuntimeException(e); } }); + } else { + throw new TraceNotFoundException(String.format("No segments found in trace %s", trace.id())); } }); + return traceRes; } private void getNestedSubSegments(List<SubSegment> subsegments, Trace traceRes, List<String> idsToIgnore) { - subsegments.forEach(subsegment -> - { + subsegments.forEach(subsegment -> { List<String> subSegmentIdsToIgnore = Collections.emptyList(); if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { traceRes.addSubSegment(subsegment); @@ -171,21 +174,30 @@ private void getNestedSubSegments(List<SubSegment> subsegments, Trace traceRes, * @return a list of trace ids */ private List<String> getTraceIds() { + LOG.debug("Searching for traces from {} to {} with filter: {}", start, end, filterExpression); GetTraceSummariesResponse traceSummaries = xray.getTraceSummaries(GetTraceSummariesRequest.builder() .startTime(start) .endTime(end) - .timeRangeType(TimeRangeType.EVENT) + .timeRangeType(TimeRangeType.TRACE_ID) .sampling(false) .filterExpression(filterExpression) .build()); + + LOG.debug("Found {} trace summaries", + traceSummaries.hasTraceSummaries() ? traceSummaries.traceSummaries().size() : 0); + if (!traceSummaries.hasTraceSummaries()) { - throw new RuntimeException("No trace id found"); + throw new TraceNotFoundException(String.format("No trace id found for filter '%s' between %s and %s", + filterExpression, start, end)); } - List<String> traceIds = - traceSummaries.traceSummaries().stream().map(TraceSummary::id).collect(Collectors.toList()); + List<String> traceIds = traceSummaries.traceSummaries().stream().map(TraceSummary::id) + .collect(Collectors.toList()); if (traceIds.isEmpty()) { - throw new RuntimeException("No trace id found"); + throw new TraceNotFoundException( + String.format("Empty trace summary found for filter '%s' between %s and %s", + filterExpression, start, end)); } + LOG.debug("Found trace IDs: {}", traceIds); return traceIds; } @@ -193,7 +205,7 @@ public static class Builder { private Instant start; private Instant end; private String filterExpression; - private List<String> excludedSegments = Arrays.asList("Initialization", "Invocation", "Overhead"); + private List<String> excludedSegments = Arrays.asList("Initialization", "Init", "Invocation", "Overhead"); public TraceFetcher build() { if (filterExpression == null) { @@ -205,8 +217,13 @@ public TraceFetcher build() { if (end == null) { end = start.plus(1, ChronoUnit.MINUTES); } - LOG.debug("Looking for traces from {} to {} with filter {}", start, end, filterExpression); - return new TraceFetcher(start, end, filterExpression, excludedSegments); + // Expand search window by 1 minute on each side to account for timing imprecisions + Instant expandedStart = start.minus(1, ChronoUnit.MINUTES); + Instant expandedEnd = end.plus(1, ChronoUnit.MINUTES); + LOG.debug( + "Looking for traces from {} to {} (expanded from {} to {}) with filter {} and excluded segments {}", + expandedStart, expandedEnd, start, end, filterExpression, excludedSegments); + return new TraceFetcher(expandedStart, expandedEnd, filterExpression, excludedSegments); } public Builder start(Instant start) { @@ -236,8 +253,8 @@ public Builder excludeSegments(List<String> excludedSegments) { } public Builder functionName(String functionName) { - this.filterExpression = - String.format("service(id(name: \"%s\", type: \"AWS::Lambda::Function\"))", functionName); + this.filterExpression = String.format("service(id(name: \"%s\", type: \"AWS::Lambda::Function\"))", + functionName); return this; } } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceNotFoundException.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceNotFoundException.java new file mode 100644 index 000000000..453aae669 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceNotFoundException.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.testutils.tracing; + +import software.amazon.lambda.powertools.testutils.DataNotReadyException; + +/** + * Exception thrown when trace data is not found in X-Ray. + * This exception is used to trigger retries as traces may not be available immediately. + */ +public class TraceNotFoundException extends DataNotReadyException { + public TraceNotFoundException(String message) { + super(message); + } +} diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile new file mode 100644 index 000000000..1ceb29aa0 --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -0,0 +1,14 @@ +# Use the official AWS SAM base image for Java 25 +FROM public.ecr.aws/sam/build-java25@sha256:bffac7de6e418a93d2aefc1e8e7c79eda0971e7a026725fe618b58ddfba7a128 + +# Install GraalVM dependencies +RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz +RUN mv graalvm-jdk-25.* /usr/lib/graalvm + +# Make native image and mvn available on CLI +RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image +RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn + +# Set GraalVM as default +ENV JAVA_HOME=/usr/lib/graalvm +ENV PATH=/usr/lib/graalvm/bin:$PATH diff --git a/powertools-e2e-tests/src/test/resources/validation/invalid_alb_in_event.json b/powertools-e2e-tests/src/test/resources/validation/invalid_alb_in_event.json new file mode 100644 index 000000000..ebad834d8 --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/validation/invalid_alb_in_event.json @@ -0,0 +1,28 @@ +{ + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a" + } + }, + "httpMethod": "POST", + "path": "/path/to/resource", + "queryStringParameters": { + "query": "1234ABCD" + }, + "headers": { + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", + "accept-encoding": "gzip", + "accept-language": "en-US,en;q=0.9", + "connection": "keep-alive", + "host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com", + "upgrade-insecure-requests": "1", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36", + "x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476", + "x-forwarded-for": "72.12.164.125", + "x-forwarded-port": "80", + "x-forwarded-proto": "http", + "x-imforwards": "20" + }, + "body": "{\"message\": \"Lambda rocks\"}", + "isBase64Encoded": true +} \ No newline at end of file diff --git a/powertools-e2e-tests/src/test/resources/validation/invalid_alb_out_event.json b/powertools-e2e-tests/src/test/resources/validation/invalid_alb_out_event.json new file mode 100644 index 000000000..1b1961063 --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/validation/invalid_alb_out_event.json @@ -0,0 +1,28 @@ +{ + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a" + } + }, + "httpMethod": "POST", + "path": "/path/to/resource", + "queryStringParameters": { + "query": "1234ABCD" + }, + "headers": { + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", + "accept-encoding": "gzip", + "accept-language": "en-US,en;q=0.9", + "connection": "keep-alive", + "host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com", + "upgrade-insecure-requests": "1", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36", + "x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476", + "x-forwarded-for": "72.12.164.125", + "x-forwarded-port": "80", + "x-forwarded-proto": "http", + "x-imforwards": "20" + }, + "body": "{\"price\": 50000}", + "isBase64Encoded": true +} \ No newline at end of file diff --git a/powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_in_event.json b/powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_in_event.json new file mode 100644 index 000000000..014ef9f05 --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_in_event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"Lambda rocks\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_out_event.json b/powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_out_event.json new file mode 100644 index 000000000..b7ef1780c --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_out_event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"price\": 50000}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/src/test/resources/validation/valid_alb_in_out_event.json b/powertools-e2e-tests/src/test/resources/validation/valid_alb_in_out_event.json new file mode 100644 index 000000000..35560f109 --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/validation/valid_alb_in_out_event.json @@ -0,0 +1,28 @@ +{ + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a" + } + }, + "httpMethod": "POST", + "path": "/path/to/resource", + "queryStringParameters": { + "query": "1234ABCD" + }, + "headers": { + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", + "accept-encoding": "gzip", + "accept-language": "en-US,en;q=0.9", + "connection": "keep-alive", + "host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com", + "upgrade-insecure-requests": "1", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36", + "x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476", + "x-forwarded-for": "72.12.164.125", + "x-forwarded-port": "80", + "x-forwarded-proto": "http", + "x-imforwards": "20" + }, + "body": "{\"price\": 150}", + "isBase64Encoded": true +} \ No newline at end of file diff --git a/powertools-e2e-tests/src/test/resources/validation/valid_api_gw_in_out_event.json b/powertools-e2e-tests/src/test/resources/validation/valid_api_gw_in_out_event.json new file mode 100644 index 000000000..8cb8ea27a --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/validation/valid_api_gw_in_out_event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"price\": 150}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} \ No newline at end of file diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 2e68ea563..cbe2384ba 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,76 +21,31 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> <artifactId>powertools-idempotency</artifactId> - <packaging>jar</packaging> + <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Idempotency</name> - <description> + <description>Utility to implement idempotency of Lambda functions</description> - </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> + <modules> + <module>powertools-idempotency-core</module> + <module>powertools-idempotency-dynamodb</module> + </modules> <dependencies> <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-serialization</artifactId> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>dynamodb</artifactId> - <exclusions> - <exclusion> - <groupId>software.amazon.awssdk</groupId> - <artifactId>netty-nio-client</artifactId> - </exclusion> - <exclusion> - <groupId>software.amazon.awssdk</groupId> - <artifactId>apache-client</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>url-connection-client</artifactId> - <version>${aws.sdk.version}</version> - </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> + <artifactId>powertools-common</artifactId> </dependency> - <!-- Test dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> @@ -107,16 +62,6 @@ <artifactId>junit-pioneer</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> @@ -137,20 +82,6 @@ <artifactId>aws-lambda-java-tests</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>DynamoDBLocal</artifactId> - <version>[1.12,2.0)</version> - <scope>test</scope> - </dependency> - <!--Needed when building locally on M1 Mac--> - <dependency> - <groupId>io.github.ganadist.sqlite4java</groupId> - <artifactId>libsqlite4java-osx-aarch64</artifactId> - <version>1.0.392</version> - <scope>test</scope> - <type>dylib</type> - </dependency> </dependencies> <build> <plugins> @@ -181,29 +112,6 @@ </execution> </executions> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-jar-plugin</artifactId> - <version>3.3.0</version> - <configuration> - <archive> - <manifestEntries> - <Automatic-Module-Name>software.amazon.awssdk.enhanced.dynamodb</Automatic-Module-Name> - </manifestEntries> - </archive> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> </plugins> </build> - <repositories> - <repository> - <id>dynamodb-local-oregon</id> - <name>DynamoDB Local Release Repository</name> - <url>https://s3.eu-central-1.amazonaws.com/dynamodb-local-frankfurt/release</url> - </repository> - </repositories> -</project> \ No newline at end of file +</project> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml new file mode 100644 index 000000000..4cba1956f --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2023 Amazon.com, Inc. or its affiliates. + ~ Licensed under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>powertools-idempotency-core</artifactId> + <packaging>jar</packaging> + + <name>Powertools for AWS Lambda (Java) library Idempotency - Core</name> + <description> + Idempotency module common implementation + </description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-serialization</artifactId> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-core,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-idempotency-core</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java new file mode 100644 index 000000000..4f73edea0 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java @@ -0,0 +1,328 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency; + +import java.util.function.Function; +import java.util.function.Supplier; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; + +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; +import software.amazon.lambda.powertools.idempotency.internal.IdempotencyHandler; +import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +/** + * Idempotency provides both a configuration and a functional API for implementing idempotent workloads. + * + * <p>This class is thread-safe. All operations delegate to the underlying persistence store + * which handles concurrent access safely.</p> + * + * <h2>Configuration</h2> + * <p>Configure the persistence layer and idempotency settings before your handler executes (e.g. in constructor):</p> + * <pre>{@code + * Idempotency.config() + * .withPersistenceStore(persistenceStore) + * .withConfig(idempotencyConfig) + * .configure(); + * }</pre> + * + * <h2>Functional API</h2> + * <p>Make methods idempotent without AspectJ annotations. Generic return types (e.g., {@code Map<String, Object>}, + * {@code List<Product>}) are supported via Jackson {@link TypeReference}.</p> + * + * <p><strong>Important:</strong> Always call {@link #registerLambdaContext(Context)} + * at the start of your handler to enable proper timeout handling.</p> + * + * <p>Example usage with Function (single parameter):</p> + * <pre>{@code + * public Basket handleRequest(Product input, Context context) { + * Idempotency.registerLambdaContext(context); + * return Idempotency.makeIdempotent(this::processProduct, input, Basket.class); + * } + * + * private Basket processProduct(Product product) { + * // business logic + * } + * }</pre> + * + * <p>Example usage with Supplier (multi-parameter methods):</p> + * <pre>{@code + * public String handleRequest(SQSEvent event, Context context) { + * Idempotency.registerLambdaContext(context); + * return Idempotency.makeIdempotent( + * event.getRecords().get(0).getBody(), + * () -> processPayment(orderId, amount, currency), + * String.class + * ); + * } + * }</pre> + * + * <p>When different methods use the same payload as idempotency key, use explicit function names + * to differentiate between them:</p> + * <pre>{@code + * // Different methods, same payload + * Idempotency.makeIdempotent("processPayment", orderId, + * () -> processPayment(orderId), String.class); + * + * Idempotency.makeIdempotent("refundPayment", orderId, + * () -> refundPayment(orderId), String.class); + * }</pre> + * + * @see #config() + * @see #registerLambdaContext(Context) + * @see #makeIdempotent(Object, Supplier, Class) + * @see #makeIdempotent(String, Object, Supplier, Class) + * @see #makeIdempotent(Function, Object, Class) + * @see #makeIdempotent(Object, Supplier, TypeReference) + * @see #makeIdempotent(String, Object, Supplier, TypeReference) + * @see #makeIdempotent(Function, Object, TypeReference) + */ +public final class Idempotency { + private static final String DEFAULT_FUNCTION_NAME = "function"; + + private IdempotencyConfig config; + private BasePersistenceStore persistenceStore; + + private Idempotency() { + } + + public static Idempotency getInstance() { + return Holder.instance; + } + + /** + * Can be used in a method which is not the handler to capture the Lambda context, + * to calculate the remaining time before the invocation times out. + * + * @param lambdaContext + */ + public static void registerLambdaContext(Context lambdaContext) { + getInstance().getConfig().setLambdaContext(lambdaContext); + } + + /** + * Acts like a builder that can be used to configure {@link Idempotency} + * + * @return a new instance of {@link Config} + */ + public static Config config() { + return new Config(); + } + + public IdempotencyConfig getConfig() { + return config; + } + + private void setConfig(IdempotencyConfig config) { + this.config = config; + } + + public BasePersistenceStore getPersistenceStore() { + if (persistenceStore == null) { + throw new IllegalStateException("Persistence Store is null, did you call 'configure()'?"); + } + return persistenceStore; + } + + private void setPersistenceStore(BasePersistenceStore persistenceStore) { + this.persistenceStore = persistenceStore; + } + + private static final class Holder { + private static final Idempotency instance = new Idempotency(); + } + + public static class Config { + + private IdempotencyConfig config; + private BasePersistenceStore store; + + /** + * Use this method after configuring persistence layer (mandatory) and idem potency configuration (optional) + */ + public void configure() { + if (store == null) { + throw new IllegalStateException( + "Persistence Layer is null, configure one with 'withPersistenceStore()'"); + } + if (config == null) { + config = IdempotencyConfig.builder().build(); + } + Idempotency.getInstance().setConfig(config); + Idempotency.getInstance().setPersistenceStore(store); + } + + public Config withPersistenceStore(BasePersistenceStore persistenceStore) { + this.store = persistenceStore; + return this; + } + + public Config withConfig(IdempotencyConfig config) { + this.config = config; + return this; + } + } + + // Functional API methods + + /** + * Makes a function idempotent using the provided idempotency key. + * Uses a default function name for namespacing the idempotency key. + * + * <p>This method is thread-safe and can be used in parallel processing scenarios + * such as batch processors.</p> + * + * <p>This method is suitable for making methods idempotent that have more than one parameter. + * For simple single-parameter methods, {@link #makeIdempotent(Function, Object, Class)} is more intuitive.</p> + * + * <p><strong>Note:</strong> If you need to call different functions with the same payload, + * use {@link #makeIdempotent(String, Object, Supplier, Class)} to specify distinct function names. + * This ensures each function has its own idempotency scope.</p> + * + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param returnType the class of the return type for deserialization + * @param <T> the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + public static <T> T makeIdempotent(Object idempotencyKey, Supplier<T> function, Class<T> returnType) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, idempotencyKey, function, returnType); + } + + /** + * Makes a function idempotent using the provided function name and idempotency key. + * + * <p>This method is thread-safe and can be used in parallel processing scenarios + * such as batch processors.</p> + * + * @param functionName the name of the function (used for persistence store configuration) + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param returnType the class of the return type for deserialization + * @param <T> the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + public static <T> T makeIdempotent(String functionName, Object idempotencyKey, Supplier<T> function, + Class<T> returnType) { + return makeIdempotent(functionName, idempotencyKey, function, JsonConfig.toTypeReference(returnType)); + } + + /** + * Makes a function with one parameter idempotent. + * The parameter is used as the idempotency key. + * + * <p>For functions with more than one parameter, use {@link #makeIdempotent(Object, Supplier, Class)} instead.</p> + * + * <p><strong>Note:</strong> If you need to call different functions with the same payload, + * use {@link #makeIdempotent(String, Object, Supplier, Class)} to specify distinct function names. + * This ensures each function has its own idempotency scope.</p> + * + * @param function the function to make idempotent (method reference) + * @param arg the argument to pass to the function (also used as idempotency key) + * @param returnType the class of the return type for deserialization + * @param <T> the argument type + * @param <R> the return type + * @return the result of the function execution (either fresh or cached) + */ + public static <T, R> R makeIdempotent(Function<T, R> function, T arg, Class<R> returnType) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, arg, () -> function.apply(arg), returnType); + } + + /** + * Makes a function idempotent using the provided idempotency key with support for generic return types. + * Uses a default function name for namespacing the idempotency key. + * + * <p>Use this method when the return type contains generics (e.g., {@code Map<String, Basket>}).</p> + * + * <p>Example usage:</p> + * <pre>{@code + * Map<String, Basket> result = Idempotency.makeIdempotent( + * payload, + * () -> processBaskets(), + * new TypeReference<Map<String, Basket>>() {} + * ); + * }</pre> + * + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param typeRef the TypeReference for deserialization of generic types + * @param <T> the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + public static <T> T makeIdempotent(Object idempotencyKey, Supplier<T> function, TypeReference<T> typeRef) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, idempotencyKey, function, typeRef); + } + + /** + * Makes a function idempotent using the provided function name and idempotency key with support for generic return types. + * + * @param functionName the name of the function (used for persistence store configuration) + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param typeRef the TypeReference for deserialization of generic types + * @param <T> the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + @SuppressWarnings("unchecked") + public static <T> T makeIdempotent(String functionName, Object idempotencyKey, Supplier<T> function, + TypeReference<T> typeRef) { + try { + JsonNode payload = JsonConfig.get().getObjectMapper().valueToTree(idempotencyKey); + Context lambdaContext = Idempotency.getInstance().getConfig().getLambdaContext(); + + IdempotencyHandler handler = new IdempotencyHandler( + function::get, + typeRef, + functionName, + payload, + lambdaContext); + + Object result = handler.handle(); + return (T) result; + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new IdempotencyConfigurationException("Idempotency operation failed: " + e.getMessage()); + } + } + + /** + * Makes a function with one parameter idempotent with support for generic return types. + * The parameter is used as the idempotency key. + * + * <p>Example usage:</p> + * <pre>{@code + * Map<String, Basket> result = Idempotency.makeIdempotent( + * this::processProduct, + * product, + * new TypeReference<Map<String, Basket>>() {} + * ); + * }</pre> + * + * @param function the function to make idempotent (method reference) + * @param arg the argument to pass to the function (also used as idempotency key) + * @param typeRef the TypeReference for deserialization of generic types + * @param <T> the argument type + * @param <R> the return type + * @return the result of the function execution (either fresh or cached) + */ + public static <T, R> R makeIdempotent(Function<T, R> function, T arg, TypeReference<R> typeRef) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, arg, () -> function.apply(arg), typeRef); + } + +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java similarity index 70% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java index 58d0a7f5b..0d9504483 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java @@ -14,14 +14,18 @@ package software.amazon.lambda.powertools.idempotency; -import com.amazonaws.services.lambda.runtime.Context; import java.time.Duration; +import java.util.function.BiFunction; + +import com.amazonaws.services.lambda.runtime.Context; + import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; /** * Configuration of the idempotency feature. Use the {@link Builder} to create an instance. */ -public class IdempotencyConfig { +public final class IdempotencyConfig { private final int localCacheMaxItems; private final boolean useLocalCache; private final long expirationInSeconds; @@ -29,11 +33,12 @@ public class IdempotencyConfig { private final String payloadValidationJMESPath; private final boolean throwOnNoIdempotencyKey; private final String hashFunction; - private Context lambdaContext; + private final BiFunction<Object, DataRecord, Object> responseHook; + private final InheritableThreadLocal<Context> lambdaContext = new InheritableThreadLocal<>(); private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, - boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, - long expirationInSeconds, String hashFunction) { + boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, + long expirationInSeconds, String hashFunction, BiFunction<Object, DataRecord, Object> responseHook) { this.localCacheMaxItems = localCacheMaxItems; this.useLocalCache = useLocalCache; this.expirationInSeconds = expirationInSeconds; @@ -41,6 +46,7 @@ private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESP this.payloadValidationJMESPath = payloadValidationJMESPath; this.throwOnNoIdempotencyKey = throwOnNoIdempotencyKey; this.hashFunction = hashFunction; + this.responseHook = responseHook; } /** @@ -81,30 +87,38 @@ public String getHashFunction() { } public Context getLambdaContext() { - return lambdaContext; + return lambdaContext.get(); } public void setLambdaContext(Context lambdaContext) { - this.lambdaContext = lambdaContext; + this.lambdaContext.set(lambdaContext); + } + + public BiFunction<Object, DataRecord, Object> getResponseHook() { + return responseHook; } public static class Builder { private int localCacheMaxItems = 256; private boolean useLocalCache = false; - private long expirationInSeconds = 60 * 60; // 1 hour + private long expirationInSeconds = 60 * 60L; // 1 hour private String eventKeyJMESPath; private String payloadValidationJMESPath; private boolean throwOnNoIdempotencyKey = false; private String hashFunction = "MD5"; + private BiFunction<Object, DataRecord, Object> responseHook; /** * Initialize and return an instance of {@link IdempotencyConfig}.<br> * Example:<br> + * * <pre> * IdempotencyConfig.builder().withUseLocalCache().build(); * </pre> + * * This instance must then be passed to the {@link Idempotency.Config}: + * * <pre> * Idempotency.config().withConfig(config).configure(); * </pre> @@ -119,13 +133,15 @@ public IdempotencyConfig build() { useLocalCache, localCacheMaxItems, expirationInSeconds, - hashFunction); + hashFunction, + responseHook); } /** * A JMESPath expression to extract the idempotency key from the event record. <br> * See <a href="https://jmespath.org/">https://jmespath.org/</a> for more details.<br> - * Common paths are: <ul> + * Common paths are: + * <ul> * <li><code>powertools_json(body)</code> for APIGatewayProxyRequestEvent and APIGatewayV2HTTPEvent</li> * <li><code>Records[*].powertools_json(body)</code> for SQSEvent</li> * <li><code>Records[0].Sns.Message | powertools_json(@)</code> for SNSEvent</li> @@ -135,7 +151,8 @@ public IdempotencyConfig build() { * <li>...</li> * </ul> * - * @param eventKeyJMESPath path of the key in the Lambda event + * @param eventKeyJMESPath + * path of the key in the Lambda event * @return the instance of the builder (to chain operations) */ public Builder withEventKeyJMESPath(String eventKeyJMESPath) { @@ -146,7 +163,8 @@ public Builder withEventKeyJMESPath(String eventKeyJMESPath) { /** * Set the maximum number of items to store in local cache, by default 256 * - * @param localCacheMaxItems maximum number of items to store in local cache + * @param localCacheMaxItems + * maximum number of items to store in local cache * @return the instance of the builder (to chain operations) */ public Builder withLocalCacheMaxItems(int localCacheMaxItems) { @@ -157,8 +175,9 @@ public Builder withLocalCacheMaxItems(int localCacheMaxItems) { /** * Whether to locally cache idempotency results, by default false * - * @param useLocalCache boolean that indicate if a local cache must be used in addition to the persistence store. - * If set to true, will use the {@link LRUCache} + * @param useLocalCache + * boolean that indicate if a local cache must be used in addition to the persistence store. + * If set to true, will use the {@link LRUCache} * @return the instance of the builder (to chain operations) */ public Builder withUseLocalCache(boolean useLocalCache) { @@ -169,7 +188,8 @@ public Builder withUseLocalCache(boolean useLocalCache) { /** * The number of seconds to wait before a record is expired * - * @param expiration expiration of the record in the store + * @param expiration + * expiration of the record in the store * @return the instance of the builder (to chain operations) */ public Builder withExpiration(Duration expiration) { @@ -181,7 +201,8 @@ public Builder withExpiration(Duration expiration) { * A JMESPath expression to extract the payload to be validated from the event record. <br/> * See <a href="https://jmespath.org/">https://jmespath.org/</a> for more details. * - * @param payloadValidationJMESPath JMES Path of a part of the payload to be used for validation + * @param payloadValidationJMESPath + * JMES Path of a part of the payload to be used for validation * @return the instance of the builder (to chain operations) */ public Builder withPayloadValidationJMESPath(String payloadValidationJMESPath) { @@ -192,8 +213,9 @@ public Builder withPayloadValidationJMESPath(String payloadValidationJMESPath) { /** * Whether to throw an exception if no idempotency key was found in the request, by default false * - * @param throwOnNoIdempotencyKey boolean to indicate if we must throw an Exception when - * idempotency key could not be found in the payload. + * @param throwOnNoIdempotencyKey + * boolean to indicate if we must throw an Exception when idempotency key could not be found in the + * payload. * @return the instance of the builder (to chain operations) */ public Builder withThrowOnNoIdempotencyKey(boolean throwOnNoIdempotencyKey) { @@ -214,15 +236,43 @@ public Builder withThrowOnNoIdempotencyKey() { /** * Function to use for calculating hashes, by default MD5. * - * @param hashFunction Can be any algorithm supported by {@link java.security.MessageDigest}, most commons are<ul> - * <li>MD5</li> - * <li>SHA-1</li> - * <li>SHA-256</li></ul> + * @param hashFunction + * Can be any algorithm supported by {@link java.security.MessageDigest}, most commons are + * <ul> + * <li>MD5</li> + * <li>SHA-1</li> + * <li>SHA-256</li> + * </ul> * @return the instance of the builder (to chain operations) */ public Builder withHashFunction(String hashFunction) { this.hashFunction = hashFunction; return this; } + + /** + * Response hook that will be called for each idempotent response. This hook will receive the de-serialized + * response data from the persistence store as first argument and the original DataRecord from the persistence + * store as second argument. + * + * Usage: + * + * <pre> + * IdempotencyConfig.builder().withResponseHook((responseData, dataRecord) -> { + * // do something with the response data, for example: + * if(responseData instanceof APIGatewayProxyRequestEvent) { + * ((APIGatewayProxyRequestEvent) responseData).setHeaders(Map.of("x-idempotency-response", "true") + * } + * return responseData; + * }) + * </pre> + * + * @param responseHook + * @return + */ + public Builder withResponseHook(BiFunction<Object, DataRecord, Object> responseHook) { + this.responseHook = responseHook; + return this; + } } } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java similarity index 99% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java index 6ca40a0e1..d08874492 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java @@ -15,6 +15,7 @@ package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.lambda.runtime.Context; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java similarity index 68% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java index ba7da69bf..aed2e5ae0 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java @@ -14,11 +14,17 @@ package software.amazon.lambda.powertools.idempotency.exceptions; +import java.util.Optional; + +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; + /** * Exception thrown when trying to store an item which already exists. */ public class IdempotencyItemAlreadyExistsException extends RuntimeException { private static final long serialVersionUID = 9027152772149436500L; + // transient because we don't want to accidentally dump any payloads in logs / stack traces + private transient Optional<DataRecord> dr = Optional.empty(); public IdempotencyItemAlreadyExistsException() { super(); @@ -27,4 +33,13 @@ public IdempotencyItemAlreadyExistsException() { public IdempotencyItemAlreadyExistsException(String msg, Throwable e) { super(msg, e); } + + public IdempotencyItemAlreadyExistsException(String msg, Throwable e, DataRecord dr) { + super(msg, e); + this.dr = Optional.ofNullable(dr); + } + + public Optional<DataRecord> getDataRecord() { + return dr; + } } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java similarity index 79% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 2875ab3d1..d4e0d2222 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -17,14 +17,17 @@ import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.databind.JsonNode; import java.time.Instant; import java.util.OptionalInt; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.reflect.MethodSignature; +import java.util.function.BiFunction; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; + import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyAlreadyInProgressException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyInconsistentStateException; @@ -38,20 +41,29 @@ import software.amazon.lambda.powertools.utilities.JsonConfig; /** - * Internal class that will handle the Idempotency, and use the {@link software.amazon.lambda.powertools.idempotency.persistence.PersistenceStore} + * Internal class that will handle the Idempotency, and use the + * {@link software.amazon.lambda.powertools.idempotency.persistence.PersistenceStore} * to store the result of previous calls. */ public class IdempotencyHandler { private static final Logger LOG = LoggerFactory.getLogger(IdempotencyHandler.class); private static final int MAX_RETRIES = 2; - private final ProceedingJoinPoint pjp; + private final IdempotentFunction<?> function; + private final TypeReference<?> returnTypeRef; private final JsonNode data; private final BasePersistenceStore persistenceStore; private final Context lambdaContext; - public IdempotencyHandler(ProceedingJoinPoint pjp, String functionName, JsonNode payload, Context lambdaContext) { - this.pjp = pjp; + public IdempotencyHandler(IdempotentFunction<?> function, Class<?> returnType, String functionName, + JsonNode payload, Context lambdaContext) { + this(function, JsonConfig.toTypeReference(returnType), functionName, payload, lambdaContext); + } + + public IdempotencyHandler(IdempotentFunction<?> function, TypeReference<?> returnTypeRef, String functionName, + JsonNode payload, Context lambdaContext) { + this.function = function; + this.returnTypeRef = returnTypeRef; this.data = payload; this.lambdaContext = lambdaContext; persistenceStore = Idempotency.getInstance().getPersistenceStore(); @@ -89,12 +101,16 @@ private Object processIdempotency() throws Throwable { // already exists. If it succeeds, there's no need to call getRecord. persistenceStore.saveInProgress(data, Instant.now(), getRemainingTimeInMillis()); } catch (IdempotencyItemAlreadyExistsException iaee) { - DataRecord record = getIdempotencyRecord(); - if (record != null) { - return handleForStatus(record); + // If a DataRecord is already present on the Exception we can immediately take that one instead of trying + // to fetch it first. + DataRecord dr = iaee.getDataRecord().orElseGet(this::getIdempotencyRecord); + if (dr != null) { + return handleForStatus(dr); } } catch (IdempotencyKeyException ike) { throw ike; + } catch (IdempotencyValidationException ive) { + throw ive; } catch (Exception e) { throw new IdempotencyPersistenceLayerException( "Failed to save in progress record to idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", @@ -105,7 +121,8 @@ private Object processIdempotency() throws Throwable { /** * Tries to determine the remaining time available for the current lambda invocation. - * Currently, it only works if the idempotent handler decorator is used or using {@link Idempotency#registerLambdaContext(Context)} + * Currently, it only works if the idempotent handler decorator is used or using + * {@link Idempotency#registerLambdaContext(Context)} * * @return the remaining time in milliseconds or empty if the context was not provided/found */ @@ -143,7 +160,8 @@ private DataRecord getIdempotencyRecord() { /** * Take appropriate action based on data_record's status * - * @param record DataRecord + * @param record + * DataRecord * @return Function's response previously used for this idempotency key, if it has successfully executed already. */ private Object handleForStatus(DataRecord record) { @@ -162,24 +180,38 @@ private Object handleForStatus(DataRecord record) { "Execution already in progress with idempotency key: " + record.getIdempotencyKey()); } - Class<?> returnType = ((MethodSignature) pjp.getSignature()).getReturnType(); try { LOG.debug("Response for key '{}' retrieved from idempotency store, skipping the function", record.getIdempotencyKey()); - if (returnType.equals(String.class)) { - return record.getResponseData(); + + final BiFunction<Object, DataRecord, Object> responseHook = Idempotency.getInstance().getConfig() + .getResponseHook(); + final Object responseData; + + if (String.class.equals(returnTypeRef.getType())) { + // Primitive String data will be returned raw and not de-serialized from JSON. + responseData = record.getResponseData(); + } else { + responseData = JsonConfig.get().getObjectMapper().readValue(record.getResponseData(), + returnTypeRef); } - return JsonConfig.get().getObjectMapper().reader().readValue(record.getResponseData(), returnType); + + if (responseHook != null) { + LOG.debug("Applying user-defined response hook to idempotency data before returning."); + return responseHook.apply(responseData, record); + } + + return responseData; } catch (Exception e) { throw new IdempotencyPersistenceLayerException( - "Unable to get function response as " + returnType.getSimpleName(), e); + "Unable to get function response as " + returnTypeRef.getType().getTypeName(), e); } } private Object getFunctionResponse() throws Throwable { Object response; try { - response = pjp.proceed(pjp.getArgs()); + response = function.execute(); } catch (Throwable handlerException) { // We need these nested blocks to preserve function's exception in case the persistence store operation // also raises an exception diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java similarity index 91% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index 989e88eb7..35c6dee40 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -14,18 +14,21 @@ package software.amazon.lambda.powertools.idempotency.internal; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.databind.JsonNode; import java.lang.annotation.Annotation; import java.lang.reflect.Method; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclarePrecedence; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.databind.JsonNode; + import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyKey; @@ -41,14 +44,15 @@ // Idempotency annotation should come first before large message @DeclarePrecedence("software.amazon.lambda.powertools.idempotency.internal.IdempotentAspect, *") public class IdempotentAspect { - @SuppressWarnings({"EmptyMethod"}) + @SuppressWarnings({ "EmptyMethod" }) @Pointcut("@annotation(idempotent)") public void callAt(Idempotent idempotent) { + // Pointcut method - body intentionally empty } @Around(value = "callAt(idempotent) && execution(@Idempotent * *.*(..))", argNames = "pjp,idempotent") public Object around(ProceedingJoinPoint pjp, - Idempotent idempotent) throws Throwable { + Idempotent idempotent) throws Throwable { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); if (idempotencyDisabledEnv != null && !"false".equalsIgnoreCase(idempotencyDisabledEnv)) { @@ -75,7 +79,12 @@ public Object around(ProceedingJoinPoint pjp, lambdaContext = Idempotency.getInstance().getConfig().getLambdaContext(); } - IdempotencyHandler idempotencyHandler = new IdempotencyHandler(pjp, method.getName(), payload, lambdaContext); + IdempotencyHandler idempotencyHandler = new IdempotencyHandler( + () -> pjp.proceed(pjp.getArgs()), + method.getReturnType(), + method.getName(), + payload, + lambdaContext); return idempotencyHandler.handle(); } diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java new file mode 100644 index 000000000..2ff2071b0 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.internal; + +/** + * Functional interface for idempotent function execution. + * <p> + * This interface is similar to {@link java.util.concurrent.Callable} but throws {@link Throwable} + * instead of {@link Exception}. This is necessary to support AspectJ's {@code ProceedingJoinPoint.proceed()} + * which throws {@code Throwable}, allowing exceptions to bubble up naturally without wrapping. + * + * @param <T> the return type of the function + */ +@FunctionalInterface +public interface IdempotentFunction<T> { + @SuppressWarnings("java:S112") // Throwable is required for AspectJ compatibility + T execute() throws Throwable; +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java similarity index 92% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java index b57ad2977..a3bf1c5ff 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java @@ -35,7 +35,7 @@ public LRUCache(int capacity) { } @Override - protected boolean removeEldestEntry(Map.Entry entry) { - return (size() > this.capacity); + protected boolean removeEldestEntry(Map.Entry<K, V> entry) { + return size() > this.capacity; } } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java similarity index 84% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index f58b276fd..00ddc4336 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -14,19 +14,14 @@ package software.amazon.lambda.powertools.idempotency.persistence; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; +import static software.amazon.lambda.powertools.common.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectWriter; -import io.burt.jmespath.Expression; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; @@ -34,9 +29,15 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import software.amazon.awssdk.utils.StringUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectWriter; + +import io.burt.jmespath.Expression; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; @@ -56,7 +57,7 @@ public abstract class BasePersistenceStore implements PersistenceStore { protected boolean payloadValidationEnabled = false; private String functionName = ""; private boolean configured = false; - private long expirationInSeconds = 60 * 60; // 1 hour default + private long expirationInSeconds = 60 * 60L; // 1 hour default private boolean useLocalCache = false; private LRUCache<String, DataRecord> cache; private String eventKeyJMESPath; @@ -74,7 +75,7 @@ public abstract class BasePersistenceStore implements PersistenceStore { public void configure(IdempotencyConfig config, String functionName) { String funcEnv = System.getenv(LAMBDA_FUNCTION_NAME_ENV); this.functionName = funcEnv != null ? funcEnv : "testFunction"; - if (!StringUtils.isEmpty(functionName)) { + if (functionName != null && !functionName.isEmpty()) { this.functionName += "." + functionName; } @@ -122,19 +123,17 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { // missing idempotency key => non-idempotent transaction, we do not store the data, simply return return; } - DataRecord record = new DataRecord( + DataRecord dataRecord = new DataRecord( hashedIdempotencyKey.get(), DataRecord.Status.COMPLETED, getExpiryEpochSecond(now), responseJson, - getHashedPayload(data) - ); + getHashedPayload(data)); LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", - record.getIdempotencyKey()); - updateRecord(record); - saveToCache(record); + dataRecord.getIdempotencyKey()); + updateRecord(dataRecord); + saveToCache(dataRecord); } catch (JsonProcessingException e) { - // TODO : throw ? throw new RuntimeException("Error while serializing the response", e); } } @@ -160,20 +159,33 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime OptionalLong inProgressExpirationMsTimestamp = OptionalLong.empty(); if (remainingTimeInMs.isPresent()) { - inProgressExpirationMsTimestamp = - OptionalLong.of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); + inProgressExpirationMsTimestamp = OptionalLong + .of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); } - DataRecord record = new DataRecord( + DataRecord dataRecord = new DataRecord( idempotencyKey, DataRecord.Status.INPROGRESS, getExpiryEpochSecond(now), null, getHashedPayload(data), - inProgressExpirationMsTimestamp - ); - LOG.debug("saving in progress record for idempotency key: {}", record.getIdempotencyKey()); - putRecord(record, now); + inProgressExpirationMsTimestamp); + LOG.debug("saving in progress record for idempotency key: {}", dataRecord.getIdempotencyKey()); + + try { + putRecord(dataRecord, now); + } catch (IdempotencyItemAlreadyExistsException iaee) { + // Similar to getRecord, we need to call validatePayload before returning a data record. + // PR https://github.com/aws-powertools/powertools-lambda-java/pull/1821 introduced returning a data record + // through IdempotencyItemAlreadyExistsException to save DynamoDB calls when using DDB as store. + Optional<DataRecord> dr = iaee.getDataRecord(); + if (dr.isPresent()) { + // throws IdempotencyValidationException if payload validation is enabled and failing + validatePayload(data, dr.get()); + } + + throw iaee; + } } /** @@ -191,7 +203,7 @@ public void deleteRecord(JsonNode data, Throwable throwable) { String idemPotencyKey = hashedIdempotencyKey.get(); LOG.debug("Function raised an exception {}. " + - "Clearing in progress record in persistence store for idempotency key: {}", + "Clearing in progress record in persistence store for idempotency key: {}", throwable.getClass(), idemPotencyKey); @@ -223,10 +235,10 @@ public DataRecord getRecord(JsonNode data, Instant now) return cachedRecord; } - DataRecord record = getRecord(idemPotencyKey); - saveToCache(record); - validatePayload(data, record); - return record; + DataRecord dataRecord = getRecord(idemPotencyKey); + saveToCache(dataRecord); + validatePayload(data, dataRecord); + return dataRecord; } /** @@ -258,10 +270,10 @@ private Optional<String> getHashedIdempotencyKey(JsonNode data) { private boolean isMissingIdemPotencyKey(JsonNode data) { if (data.isContainerNode()) { - Stream<Map.Entry<String, JsonNode>> stream = - StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.fields(), Spliterator.ORDERED), - false); - return stream.allMatch(e -> e.getValue().isNull()); + Stream<JsonNode> stream = StreamSupport.stream( + Spliterators.spliteratorUnknownSize(data.elements(), Spliterator.ORDERED), + false); + return stream.allMatch(JsonNode::isNull); } return data.isNull(); } @@ -316,6 +328,7 @@ String generateHash(JsonNode data) { return String.format("%032x", new BigInteger(1, digest)); } + @SuppressWarnings("java:S4790") // Usage of MessageDigest is OK private MessageDigest getHashAlgorithm() { MessageDigest hashAlgorithm; try { @@ -340,7 +353,7 @@ private MessageDigest getHashAlgorithm() { private void validatePayload(JsonNode data, DataRecord dataRecord) throws IdempotencyValidationException { if (payloadValidationEnabled) { String dataHash = getHashedPayload(data); - if (!StringUtils.equals(dataHash, dataRecord.getPayloadHash())) { + if (!isEqual(dataRecord.getPayloadHash(), dataHash)) { throw new IdempotencyValidationException("Payload does not match stored record for this event key"); } } @@ -378,10 +391,10 @@ private DataRecord retrieveFromCache(String idempotencyKey, Instant now) { return null; } - DataRecord record = cache.get(idempotencyKey); - if (record != null) { - if (!record.isExpired(now)) { - return record; + DataRecord dataRecord = cache.get(idempotencyKey); + if (dataRecord != null) { + if (!dataRecord.isExpired(now)) { + return dataRecord; } LOG.debug("Removing expired local cache record for idempotency key: {}", idempotencyKey); deleteFromCache(idempotencyKey); @@ -403,4 +416,12 @@ void configure(IdempotencyConfig config, String functionName, LRUCache<String, D this.configure(config, functionName); this.cache = cache; } + + private static boolean isEqual(String dataRecordPayload, String dataHash) { + if (dataHash != null && dataRecordPayload != null) { + return dataHash.length() == dataRecordPayload.length() && dataHash.equals(dataRecordPayload); + } else { + return false; + } + } } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java similarity index 88% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java index 9af7c6a53..0621c372b 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java @@ -14,10 +14,11 @@ package software.amazon.lambda.powertools.idempotency.persistence; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; + import java.time.Instant; import java.util.Objects; import java.util.OptionalLong; -import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; /** * Data Class for idempotency records. This is actually the item that will be stored in the persistence layer. @@ -52,7 +53,7 @@ public class DataRecord { private final OptionalLong inProgressExpiryTimestamp; public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, - String payloadHash) { + String payloadHash) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -62,7 +63,7 @@ public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, St } public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, - String payloadHash, OptionalLong inProgressExpiryTimestamp) { + String payloadHash, OptionalLong inProgressExpiryTimestamp) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -130,13 +131,22 @@ public int hashCode() { return Objects.hash(idempotencyKey, status, expiryTimestamp, responseData, payloadHash); } + @Override + public String toString() { + return "DataRecord{" + + "idempotencyKey='" + idempotencyKey + '\'' + + ", status='" + status + '\'' + + ", expiryTimestamp=" + expiryTimestamp + + ", payloadHash='" + payloadHash + '\'' + + '}'; + } /** * Status of the record: * <ul> - * <li>INPROGRESS: record initialized when function starts</li> - * <li>COMPLETED: record updated with the result of the function when it ends</li> - * <li>EXPIRED: record expired, idempotency will not happen</li> + * <li>INPROGRESS: record initialized when function starts</li> + * <li>COMPLETED: record updated with the result of the function when it ends</li> + * <li>EXPIRED: record expired, idempotency will not happen</li> * </ul> */ public enum Status { diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java similarity index 99% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java index c058b592e..b4f105720 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java @@ -14,10 +14,11 @@ package software.amazon.lambda.powertools.idempotency.persistence; -import java.time.Instant; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import java.time.Instant; + /** * Persistence layer that will store the idempotency result. * In order to provide another implementation, extends {@link BasePersistenceStore}. diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java new file mode 100644 index 000000000..10664a25b --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java @@ -0,0 +1,311 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalInt; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; + +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyFunctionalFunction; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyMultiArgFunctionalFunction; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; +import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.idempotency.testutils.InMemoryPersistenceStore; + +@ExtendWith(MockitoExtension.class) +class IdempotencyTest { + + private Context context = new TestLambdaContext(); + + @Mock + private BasePersistenceStore store; + + @Test + void firstCall_shouldPutInStore() { + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build()) + .configure(); + + IdempotencyFunctionalFunction function = new IdempotencyFunctionalFunction(); + + Product p = new Product(42, "fake product", 12); + Basket basket = function.handleRequest(p, context); + + assertThat(function.processCalled()).isTrue(); + assertThat(basket.getProducts()).hasSize(1); + + ArgumentCaptor<JsonNode> nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + ArgumentCaptor<OptionalInt> expiryCaptor = ArgumentCaptor.forClass(OptionalInt.class); + verify(store).saveInProgress(nodeCaptor.capture(), any(), expiryCaptor.capture()); + assertThat(nodeCaptor.getValue().get("id").asLong()).isEqualTo(p.getId()); + assertThat(nodeCaptor.getValue().get("name").asText()).isEqualTo(p.getName()); + assertThat(nodeCaptor.getValue().get("price").asDouble()).isEqualTo(p.getPrice()); + assertThat(expiryCaptor.getValue().orElse(-1)).isEqualTo(30000); + + ArgumentCaptor<Basket> resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue()).isEqualTo(basket); + } + + @Test + void testMakeIdempotentWithFunctionName() throws Throwable { + BasePersistenceStore spyStore = spy(BasePersistenceStore.class); + Idempotency.config() + .withPersistenceStore(spyStore) + .configure(); + Idempotency.registerLambdaContext(context); + + String result = Idempotency.makeIdempotent("myFunction", "test-key", () -> "test-result", + String.class); + + assertThat(result).isEqualTo("test-result"); + + ArgumentCaptor<String> functionNameCaptor = ArgumentCaptor.forClass(String.class); + verify(spyStore).configure(any(), functionNameCaptor.capture()); + assertThat(functionNameCaptor.getValue()).isEqualTo("myFunction"); + } + + @Test + void testMakeIdempotentWithMethodReferenceUsesDefaultName() throws Throwable { + BasePersistenceStore spyStore = spy(BasePersistenceStore.class); + Idempotency.config() + .withPersistenceStore(spyStore) + .configure(); + Idempotency.registerLambdaContext(context); + + String result = Idempotency.makeIdempotent("test-key", this::helperMethod, String.class); + + assertThat(result).isEqualTo("helper-result"); + + ArgumentCaptor<String> functionNameCaptor = ArgumentCaptor.forClass(String.class); + verify(spyStore).configure(any(), functionNameCaptor.capture()); + assertThat(functionNameCaptor.getValue()).isEqualTo("function"); + } + + private String helperMethod() { + return "helper-result"; + } + + @Test + void testMakeIdempotentWithFunctionOverload() throws Throwable { + BasePersistenceStore spyStore = spy(BasePersistenceStore.class); + Idempotency.config() + .withPersistenceStore(spyStore) + .configure(); + Idempotency.registerLambdaContext(context); + + Product p = new Product(42, "test product", 10); + Basket result = Idempotency.makeIdempotent(this::processProduct, p, Basket.class); + + assertThat(result.getProducts()).hasSize(1); + assertThat(result.getProducts().get(0)).isEqualTo(p); + + ArgumentCaptor<String> functionNameCaptor = ArgumentCaptor.forClass(String.class); + verify(spyStore).configure(any(), functionNameCaptor.capture()); + assertThat(functionNameCaptor.getValue()).isEqualTo("function"); + + ArgumentCaptor<JsonNode> nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + verify(spyStore).saveInProgress(nodeCaptor.capture(), any(), any()); + assertThat(nodeCaptor.getValue().get("id").asLong()).isEqualTo(p.getId()); + } + + private Basket processProduct(Product product) { + Basket basket = new Basket(); + basket.add(product); + return basket; + } + + @Test + void firstCall_withExplicitIdempotencyKey_shouldPutInStore() { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + IdempotencyMultiArgFunctionalFunction function = new IdempotencyMultiArgFunctionalFunction(); + + Product p = new Product(42, "fake product", 12); + Basket basket = function.handleRequest(p, context); + + assertThat(function.processCalled()).isTrue(); + assertThat(function.getExtraData()).isEqualTo("extra-data"); + assertThat(basket.getProducts()).hasSize(1); + + ArgumentCaptor<JsonNode> nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + verify(store).saveInProgress(nodeCaptor.capture(), any(), any()); + assertThat(nodeCaptor.getValue().asLong()).isEqualTo(p.getId()); + + ArgumentCaptor<Basket> resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue()).isEqualTo(basket); + } + + @Test + void secondCall_shouldRetrieveFromCacheAndDeserialize() throws Throwable { + InMemoryPersistenceStore inMemoryStore = new InMemoryPersistenceStore(); + + Idempotency.config() + .withPersistenceStore(inMemoryStore) + .configure(); + Idempotency.registerLambdaContext(context); + + Product p = new Product(42, "test product", 10); + int[] callCount = { 0 }; + + // First call - executes function and stores result + Basket result1 = Idempotency.makeIdempotent(p, () -> { + callCount[0]++; + return processProduct(p); + }, Basket.class); + assertThat(result1.getProducts()).hasSize(1); + assertThat(callCount[0]).isEqualTo(1); + + // Second call - should retrieve from cache, deserialize, and NOT execute function + Basket result2 = Idempotency.makeIdempotent(p, () -> { + callCount[0]++; + return processProduct(p); + }, Basket.class); + assertThat(result2.getProducts()).hasSize(1); + assertThat(result2.getProducts().get(0).getId()).isEqualTo(42); + assertThat(result2.getProducts().get(0).getName()).isEqualTo("test product"); + assertThat(callCount[0]).isEqualTo(1); // Function should NOT be called again + } + + @Test + void concurrentInvocations_shouldNotLeakContext() throws Exception { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + IdempotencyMultiArgFunctionalFunction function = new IdempotencyMultiArgFunctionalFunction(); + + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + Context[] capturedContexts = new Context[threadCount]; + int[] capturedRemainingTimes = new int[threadCount]; + boolean[] success = new boolean[threadCount]; + + // WHEN - Multiple threads call handleRequest with different contexts + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final int expectedTime = (i + 1) * 2000; // 2000, 4000, 6000, ..., 20000 + + final Context threadContext = new TestLambdaContext() { + @Override + public int getRemainingTimeInMillis() { + return expectedTime; + } + }; + + threads[i] = new Thread(() -> { + try { + Product p = new Product(threadIndex, "product" + threadIndex, 10); + function.handleRequest(p, threadContext); + + // Capture the context that was actually stored in ThreadLocal by this thread + Context captured = Idempotency.getInstance().getConfig().getLambdaContext(); + capturedContexts[threadIndex] = captured; + capturedRemainingTimes[threadIndex] = captured != null ? captured.getRemainingTimeInMillis() : -1; + success[threadIndex] = true; + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each thread should have captured its own context (no leakage) + for (int i = 0; i < threadCount; i++) { + int expectedTime = (i + 1) * 2000; + assertThat(capturedRemainingTimes[i]) + .as("Thread %d should have remaining time %d", i, expectedTime) + .isEqualTo(expectedTime); + assertThat(capturedContexts[i]).as("Thread %d should have non-null context", i).isNotNull(); + } + } + + @Test + void testMakeIdempotentWithGenericType() throws Throwable { + InMemoryPersistenceStore inMemoryStore = new InMemoryPersistenceStore(); + + Idempotency.config() + .withPersistenceStore(inMemoryStore) + .configure(); + Idempotency.registerLambdaContext(context); + + int[] callCount = { 0 }; + + // First call - executes function and stores result + Map<String, Basket> result1 = Idempotency.makeIdempotent("test-key", () -> { + callCount[0]++; + Map<String, Basket> map = new HashMap<>(); + Basket basket = new Basket(); + basket.add(new Product(1, "product1", 10)); + map.put("basket1", basket); + return map; + }, new TypeReference<Map<String, Basket>>() { + }); + + assertThat(result1).hasSize(1); + assertThat(result1.get("basket1").getProducts()).hasSize(1); + assertThat(callCount[0]).isEqualTo(1); + + // Second call - should retrieve from cache and deserialize correctly + Map<String, Basket> result2 = Idempotency.makeIdempotent("test-key", () -> { + callCount[0]++; + return new HashMap<>(); + }, new TypeReference<Map<String, Basket>>() { + }); + + assertThat(result2).hasSize(1); + assertThat(result2.get("basket1").getProducts()).hasSize(1); + assertThat(result2.get("basket1").getProducts().get(0).getName()).isEqualTo("product1"); + assertThat(callCount[0]).isEqualTo(1); // Function should NOT be called again + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java new file mode 100644 index 000000000..c2a85d178 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Lambda function using Idempotency functional API without AspectJ annotations + */ +public class IdempotencyFunctionalFunction implements RequestHandler<Product, Basket> { + + private boolean processCalled = false; + + public boolean processCalled() { + return processCalled; + } + + @Override + public Basket handleRequest(Product input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent(this::process, input, Basket.class); + } + + private Basket process(Product input) { + processCalled = true; + Basket b = new Basket(); + b.add(input); + return b; + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java new file mode 100644 index 000000000..42e75fd55 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Lambda function using Idempotency functional API with explicit idempotency key + */ +public class IdempotencyMultiArgFunctionalFunction implements RequestHandler<Product, Basket> { + + private boolean processCalled = false; + private String extraData; + + public boolean processCalled() { + return processCalled; + } + + public String getExtraData() { + return extraData; + } + + @Override + public Basket handleRequest(Product input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent(input.getId(), () -> process(input, "extra-data"), Basket.class); + } + + private Basket process(Product input, String extraData) { + processCalled = true; + this.extraData = extraData; + Basket b = new Basket(); + b.add(input); + return b; + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java similarity index 54% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java index 9a30472a8..384a20b2a 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java @@ -20,23 +20,27 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import java.time.Instant; import java.util.OptionalInt; import java.util.OptionalLong; -import org.junit.jupiter.api.BeforeEach; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; + +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; @@ -57,35 +61,66 @@ import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class IdempotencyAspectTest { +@ExtendWith(MockitoExtension.class) +class IdempotencyAspectTest { - @Mock - private Context context; + private Context context = new TestLambdaContext(); @Mock private BasePersistenceStore store; - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); + @Test + void firstCall_shouldPutInStore() { + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build()) + .configure(); + + IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); + + Product p = new Product(42, "fake product", 12); + Basket basket = function.handleRequest(p, context); + assertThat(basket.getProducts()).hasSize(1); + assertThat(function.handlerCalled()).isTrue(); + + ArgumentCaptor<JsonNode> nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + ArgumentCaptor<OptionalInt> expiryCaptor = ArgumentCaptor.forClass(OptionalInt.class); + verify(store).saveInProgress(nodeCaptor.capture(), any(), expiryCaptor.capture()); + assertThat(nodeCaptor.getValue().get("id").asLong()).isEqualTo(p.getId()); + assertThat(nodeCaptor.getValue().get("name").asText()).isEqualTo(p.getName()); + assertThat(nodeCaptor.getValue().get("price").asDouble()).isEqualTo(p.getPrice()); + + assertThat(expiryCaptor.getValue().orElse(-1)).isEqualTo(30000); + + ArgumentCaptor<Basket> resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue()).isEqualTo(basket); } @Test - public void firstCall_shouldPutInStore() { + void firstCall_shouldPutInStoreAndNotApplyResponseHook() { Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + // This hook will add a second product to the basket. It should not run here. + // Only for + // idempotent responses. + .withResponseHook((responseData, dataRecord) -> { + final Basket basket = (Basket) responseData; + basket.add(new Product(42, "fake product 2", 12)); + return basket; + }) + .build()) + .configure(); IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); - when(context.getRemainingTimeInMillis()).thenReturn(30000); - Product p = new Product(42, "fake product", 12); Basket basket = function.handleRequest(p, context); - assertThat(basket.getProducts()).hasSize(1); + assertThat(basket.getProducts()).hasSize(1); // Size should be 1 because response hook should not run assertThat(function.handlerCalled()).isTrue(); ArgumentCaptor<JsonNode> nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); @@ -103,26 +138,63 @@ public void firstCall_shouldPutInStore() { } @Test - public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { + void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); + + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + Product p = new Product(42, "fake product", 12); + Basket b = new Basket(p); + DataRecord dr = new DataRecord( + "42", + DataRecord.Status.COMPLETED, + Instant.now().plus(356, SECONDS).getEpochSecond(), + JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), + null); + doReturn(dr).when(store).getRecord(any(), any()); + + // WHEN + IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); + Basket basket = function.handleRequest(p, context); + + // THEN + assertThat(basket).isEqualTo(b); + assertThat(function.handlerCalled()).isFalse(); + } + + @Test + void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyException() + throws JsonProcessingException { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build()) + .configure(); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); - DataRecord record = new DataRecord( + DataRecord dr = new DataRecord( "42", DataRecord.Status.COMPLETED, Instant.now().plus(356, SECONDS).getEpochSecond(), JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), null); - doReturn(record).when(store).getRecord(any(), any()); + + // A data record on this exception should take precedence over fetching a record + // from the store / cache + doThrow(new IdempotencyItemAlreadyExistsException( + "Test message", + new RuntimeException("Test Cause"), + dr)) + .when(store).saveInProgress(any(), any(), any()); // WHEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -131,28 +203,31 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce // THEN assertThat(basket).isEqualTo(b); assertThat(function.handlerCalled()).isFalse(); + // Should never call the store because item is already present on + // IdempotencyItemAlreadyExistsException + verify(store, never()).getRecord(any(), any()); } @Test - public void secondCall_notExpired_shouldGetStringFromStore() { + void secondCall_notExpired_shouldGetStringFromStore() { // GIVEN Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); - DataRecord record = new DataRecord( + DataRecord dr = new DataRecord( "42", DataRecord.Status.COMPLETED, Instant.now().plus(356, SECONDS).getEpochSecond(), p.getName(), null); - doReturn(record).when(store).getRecord(any(), any()); + doReturn(dr).when(store).getRecord(any(), any()); // WHEN IdempotencyStringFunction function = new IdempotencyStringFunction(); @@ -164,30 +239,65 @@ public void secondCall_notExpired_shouldGetStringFromStore() { } @Test - public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() + void secondCall_notExpired_shouldGetStringFromStoreWithResponseHook() { + // GIVEN + final String RESPONSE_HOOK_SUFFIX = " ResponseHook"; + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .withResponseHook((responseData, dataRecord) -> { + responseData += RESPONSE_HOOK_SUFFIX; + return responseData; + }) + .build()) + .configure(); + + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); + + Product p = new Product(42, "fake product", 12); + DataRecord dr = new DataRecord( + "42", + DataRecord.Status.COMPLETED, + Instant.now().plus(356, SECONDS).getEpochSecond(), + p.getName(), + null); + doReturn(dr).when(store).getRecord(any(), any()); + + // WHEN + IdempotencyStringFunction function = new IdempotencyStringFunction(); + String name = function.handleRequest(p, context); + + // THEN + assertThat(name).isEqualTo(p.getName() + RESPONSE_HOOK_SUFFIX); + assertThat(function.handlerCalled()).isFalse(); + } + + @Test + void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); - OptionalLong timestampInFuture = - OptionalLong.of(Instant.now().toEpochMilli() + 1000); // timeout not expired (in 1sec) - DataRecord record = new DataRecord( + OptionalLong timestampInFuture = OptionalLong.of(Instant.now().toEpochMilli() + 1000); // timeout not expired + // (in 1sec) + DataRecord dr = new DataRecord( "42", DataRecord.Status.INPROGRESS, Instant.now().plus(356, SECONDS).getEpochSecond(), JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), null, timestampInFuture); - doReturn(record).when(store).getRecord(any(), any()); + doReturn(dr).when(store).getRecord(any(), any()); // THEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -196,30 +306,30 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti } @Test - public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() + void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); - OptionalLong timestampInThePast = - OptionalLong.of(Instant.now().toEpochMilli() - 100); // timeout expired 100ms ago - DataRecord record = new DataRecord( + OptionalLong timestampInThePast = OptionalLong.of(Instant.now().toEpochMilli() - 100); // timeout expired 100ms + // ago + DataRecord dr = new DataRecord( "42", DataRecord.Status.INPROGRESS, Instant.now().plus(356, SECONDS).getEpochSecond(), JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), null, timestampInThePast); - doReturn(record).when(store).getRecord(any(), any()); + doReturn(dr).when(store).getRecord(any(), any()); // THEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -228,14 +338,14 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons } @Test - public void functionThrowException_shouldDeleteRecord_andThrowFunctionException() { + void functionThrowException_shouldDeleteRecord_andThrowFunctionException() { // GIVEN Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); // WHEN / THEN IdempotencyWithErrorFunction function = new IdempotencyWithErrorFunction(); @@ -249,14 +359,14 @@ public void functionThrowException_shouldDeleteRecord_andThrowFunctionException( @Test @SetEnvironmentVariable(key = Constants.IDEMPOTENCY_DISABLED_ENV, value = "true") - public void testIdempotencyDisabled_shouldJustRunTheFunction() { + void testIdempotencyDisabled_shouldJustRunTheFunction() { // GIVEN Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); // WHEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -270,15 +380,13 @@ public void testIdempotencyDisabled_shouldJustRunTheFunction() { } @Test - public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { + void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { Idempotency.config() .withPersistenceStore(store) .configure(); // WHEN boolean registerContext = true; - when(context.getRemainingTimeInMillis()).thenReturn(30000); - IdempotencyInternalFunction function = new IdempotencyInternalFunction(registerContext); Product p = new Product(42, "fake product", 12); Basket basket = function.handleRequest(p, context); @@ -300,7 +408,7 @@ public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { } @Test - public void idempotencyOnSubMethodAnnotated_firstCall_contextNotRegistered_shouldPutInStore() { + void idempotencyOnSubMethodAnnotated_firstCall_contextNotRegistered_shouldPutInStore() { Idempotency.config() .withPersistenceStore(store) .configure(); @@ -328,24 +436,24 @@ public void idempotencyOnSubMethodAnnotated_firstCall_contextNotRegistered_shoul } @Test - public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() + void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); - DataRecord record = new DataRecord( + DataRecord dr = new DataRecord( "fake", DataRecord.Status.COMPLETED, Instant.now().plus(356, SECONDS).getEpochSecond(), JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), null); - doReturn(record).when(store).getRecord(any(), any()); + doReturn(dr).when(store).getRecord(any(), any()); // WHEN IdempotencyInternalFunction function = new IdempotencyInternalFunction(false); @@ -357,7 +465,7 @@ public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromS } @Test - public void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey() { + void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey() { BasePersistenceStore persistenceStore = spy(BasePersistenceStore.class); Idempotency.config() @@ -379,11 +487,32 @@ public void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey( } @Test - public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { + void idempotencyOnSubMethodAnnotated_keyJMESPathArray_shouldPutInStoreWithKey() { + BasePersistenceStore persistenceStore = spy(BasePersistenceStore.class); + + Idempotency.config() + .withPersistenceStore(persistenceStore) + .withConfig(IdempotencyConfig.builder().withEventKeyJMESPath("[id,name]").build()) + .configure(); + + // WHEN + IdempotencyInternalFunctionInternalKey function = new IdempotencyInternalFunctionInternalKey(); + Product p = new Product(42, "fake product", 12); + function.handleRequest(p, context); + + // THEN + ArgumentCaptor<DataRecord> recordCaptor = ArgumentCaptor.forClass(DataRecord.class); + verify(persistenceStore).putRecord(recordCaptor.capture(), any()); + // eec7cd392d9e3bb20deb2c9676697c3c = MD5([42,"fake product"]) + assertThat(recordCaptor.getValue().getIdempotencyKey()).isEqualTo( + "testFunction.createBasket#eec7cd392d9e3bb20deb2c9676697c3c"); + } + + @Test + void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { Idempotency.config() .withPersistenceStore(store) - .withConfig(IdempotencyConfig.builder().build() - ).configure(); + .withConfig(IdempotencyConfig.builder().build()).configure(); // WHEN IdempotencyInternalFunctionInvalid function = new IdempotencyInternalFunctionInvalid(); @@ -395,11 +524,10 @@ public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { } @Test - public void idempotencyOnSubMethodVoid_shouldThrowException() { + void idempotencyOnSubMethodVoid_shouldThrowException() { Idempotency.config() .withPersistenceStore(store) - .withConfig(IdempotencyConfig.builder().build() - ).configure(); + .withConfig(IdempotencyConfig.builder().build()).configure(); // WHEN IdempotencyInternalFunctionVoid function = new IdempotencyInternalFunctionVoid(); @@ -410,4 +538,73 @@ public void idempotencyOnSubMethodVoid_shouldThrowException() { IdempotencyConfigurationException.class); } + @Test + void concurrentInvocations_shouldNotLeakContext() throws Exception { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + // Use IdempotencyInternalFunction which calls registerLambdaContext + IdempotencyInternalFunction function = new IdempotencyInternalFunction(true); + + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + Context[] capturedContexts = new Context[threadCount]; + int[] capturedRemainingTimes = new int[threadCount]; + boolean[] success = new boolean[threadCount]; + + // WHEN - Multiple threads call handleRequest with different contexts + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final int expectedTime = (i + 1) * 1000; // 1000, 2000, 3000, ..., 10000 + + final Context threadContext = new TestLambdaContext() { + @Override + public int getRemainingTimeInMillis() { + return expectedTime; + } + }; + + threads[i] = new Thread(() -> { + try { + Product p = new Product(threadIndex, "product" + threadIndex, 10); + function.handleRequest(p, threadContext); + + // Capture the context that was actually stored in ThreadLocal by this thread + Context captured = Idempotency.getInstance().getConfig().getLambdaContext(); + capturedContexts[threadIndex] = captured; + capturedRemainingTimes[threadIndex] = captured != null ? captured.getRemainingTimeInMillis() : -1; + success[threadIndex] = true; + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each thread should have captured its own context (no leakage) + for (int i = 0; i < threadCount; i++) { + int expectedTime = (i + 1) * 1000; + assertThat(capturedRemainingTimes[i]) + .as("Thread %d should have remaining time %d", i, expectedTime) + .isEqualTo(expectedTime); + assertThat(capturedContexts[i]).as("Thread %d should have non-null context", i).isNotNull(); + } + } + } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java similarity index 92% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java index 8854be1f2..d14f07315 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java @@ -18,10 +18,10 @@ import org.junit.jupiter.api.Test; -public class LRUCacheTest { +class LRUCacheTest { @Test - public void testLRUCache_shouldRemoveEldestEntry() { + void testLRUCache_shouldRemoveEldestEntry() { LRUCache<String, String> cache = new LRUCache<>(3); cache.put("key1", "value1"); cache.put("key2", "value2"); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java similarity index 100% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java similarity index 67% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java rename to powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java index 67bc5aa22..8e46de1cc 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -17,17 +17,21 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.tests.EventLoader; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.DoubleNode; -import com.fasterxml.jackson.databind.node.TextNode; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.OptionalInt; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.tests.EventLoader; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.DoubleNode; +import com.fasterxml.jackson.databind.node.TextNode; + import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; @@ -37,7 +41,7 @@ import software.amazon.lambda.powertools.idempotency.model.Product; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class BasePersistenceStoreTest { +class BasePersistenceStoreTest { private DataRecord dr; private BasePersistenceStore persistenceStore; @@ -45,7 +49,7 @@ public class BasePersistenceStoreTest { private String validationHash; @BeforeEach - public void setup() { + void setup() { validationHash = null; dr = null; status = -1; @@ -58,14 +62,14 @@ public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoun } @Override - public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlreadyExistsException { - dr = record; + public void putRecord(DataRecord dataRecord, Instant now) throws IdempotencyItemAlreadyExistsException { + dr = dataRecord; status = 1; } @Override - public void updateRecord(DataRecord record) { - dr = record; + public void updateRecord(DataRecord dataRecord) { + dr = dataRecord; status = 2; } @@ -77,10 +81,8 @@ public void deleteRecord(String idempotencyKey) { }; } - // ================================================================= - //<editor-fold desc="saveInProgress"> @Test - public void saveInProgress_defaultConfig() { + void saveInProgress_defaultConfig() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder().build(), null); @@ -90,14 +92,14 @@ public void saveInProgress_defaultConfig() { assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); - assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); - assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); + assertThat(dr.getPayloadHash()).isEmpty(); assertThat(dr.getInProgressExpiryTimestamp()).isEmpty(); assertThat(status).isEqualTo(1); } @Test - public void saveInProgress_withRemainingTime() { + void saveInProgress_withRemainingTime() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder().build(), null); @@ -108,15 +110,15 @@ public void saveInProgress_withRemainingTime() { assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); - assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); - assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); + assertThat(dr.getPayloadHash()).isEmpty(); assertThat(dr.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo( now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); assertThat(status).isEqualTo(1); } @Test - public void saveInProgress_jmespath() { + void saveInProgress_jmespath() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder() .withEventKeyJMESPath("powertools_json(body).id") @@ -129,12 +131,12 @@ public void saveInProgress_jmespath() { assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction.myfunc#2fef178cc82be5ce3da6c5e0466a6182"); - assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(dr.getPayloadHash()).isEmpty(); assertThat(status).isEqualTo(1); } @Test - public void saveInProgress_jmespath_NotFound_shouldThrowException() { + void saveInProgress_jmespath_NotFound_shouldThrowException() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder() .withEventKeyJMESPath("unavailable") @@ -144,13 +146,13 @@ public void saveInProgress_jmespath_NotFound_shouldThrowException() { assertThatThrownBy( () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) - .isInstanceOf(IdempotencyKeyException.class) - .hasMessageContaining("No data found to create a hashed idempotency key"); + .isInstanceOf(IdempotencyKeyException.class) + .hasMessageContaining("No data found to create a hashed idempotency key"); assertThat(status).isEqualTo(-1); } @Test - public void saveInProgress_jmespath_NotFound_shouldNotPersist() { + void saveInProgress_jmespath_NotFound_shouldNotPersist() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder() .withEventKeyJMESPath("unavailable") @@ -163,7 +165,7 @@ public void saveInProgress_jmespath_NotFound_shouldNotPersist() { } @Test - public void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { + void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() @@ -176,17 +178,16 @@ public void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { "testFunction#2fef178cc82be5ce3da6c5e0466a6182", DataRecord.Status.INPROGRESS, now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), - null, null) - ); + null, null)); assertThatThrownBy( () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) - .isInstanceOf(IdempotencyItemAlreadyExistsException.class); + .isInstanceOf(IdempotencyItemAlreadyExistsException.class); assertThat(status).isEqualTo(-1); } @Test - public void saveInProgress_withLocalCache_Expired_ShouldRemoveFromCache() { + void saveInProgress_withLocalCache_Expired_ShouldRemoveFromCache() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() @@ -200,22 +201,16 @@ public void saveInProgress_withLocalCache_Expired_ShouldRemoveFromCache() { "testFunction#2fef178cc82be5ce3da6c5e0466a6182", DataRecord.Status.INPROGRESS, now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), - null, null) - ); + null, null)); persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(cache).isEmpty(); assertThat(status).isEqualTo(1); } - //</editor-fold> - // ================================================================= - - // ================================================================= - //<editor-fold desc="saveSuccess"> @Test - public void saveSuccess_shouldUpdateRecord() throws JsonProcessingException { + void saveSuccess_shouldUpdateRecord() throws JsonProcessingException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder().build(), null, cache); @@ -227,14 +222,14 @@ public void saveSuccess_shouldUpdateRecord() throws JsonProcessingException { assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isEqualTo(JsonConfig.get().getObjectMapper().writeValueAsString(product)); - assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); - assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); + assertThat(dr.getPayloadHash()).isEmpty(); assertThat(status).isEqualTo(2); assertThat(cache).isEmpty(); } @Test - public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessingException { + void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessingException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() @@ -246,37 +241,32 @@ public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessi assertThat(status).isEqualTo(2); assertThat(cache).hasSize(1); - DataRecord record = cache.get("testFunction#7b40f56c086de5aa91dc467456329ed2"); - assertThat(record.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); - assertThat(record.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); - assertThat(record.getResponseData()).isEqualTo(JsonConfig.get().getObjectMapper().writeValueAsString(product)); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); - assertThat(record.getPayloadHash()).isEqualTo(""); + DataRecord cachedDr = cache.get("testFunction#8d6a8f173b46479eff55e0997864a514"); + assertThat(cachedDr.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); + assertThat(cachedDr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); + assertThat(cachedDr.getResponseData()) + .isEqualTo(JsonConfig.get().getObjectMapper().writeValueAsString(product)); + assertThat(cachedDr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); + assertThat(cachedDr.getPayloadHash()).isEmpty(); } - //</editor-fold> - // ================================================================= - - // ================================================================= - //<editor-fold desc="getRecord"> - @Test - public void getRecord_shouldReturnRecordFromPersistence() + void getRecord_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder().build(), "myfunc", cache); Instant now = Instant.now(); - DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); - assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); - assertThat(record.getResponseData()).isEqualTo("Response"); - assertThat(status).isEqualTo(0); + DataRecord freshDr = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); + assertThat(freshDr.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); + assertThat(freshDr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); + assertThat(freshDr.getResponseData()).isEqualTo("Response"); + assertThat(status).isZero(); } @Test - public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() + void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); @@ -284,23 +274,23 @@ public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() .withUseLocalCache(true).build(), "myfunc", cache); Instant now = Instant.now(); - DataRecord dr = new DataRecord( - "testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", + DataRecord dr1 = new DataRecord( + "testFunction.myfunc#8d6a8f173b46479eff55e0997864a514", DataRecord.Status.COMPLETED, now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "result of the function", null); - cache.put("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", dr); + cache.put("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514", dr1); - DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); - assertThat(record.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); - assertThat(record.getResponseData()).isEqualTo("result of the function"); + DataRecord dr2 = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); + assertThat(dr2.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); + assertThat(dr2.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); + assertThat(dr2.getResponseData()).isEqualTo("result of the function"); assertThat(status).isEqualTo(-1); // getRecord must not be called (retrieve from cache) } @Test - public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() + void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); @@ -308,46 +298,86 @@ public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() .withUseLocalCache(true).build(), "myfunc", cache); Instant now = Instant.now(); - DataRecord dr = new DataRecord( - "testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", + DataRecord dr1 = new DataRecord( + "testFunction.myfunc#8d6a8f173b46479eff55e0997864a514", DataRecord.Status.COMPLETED, now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), "result of the function", null); - cache.put("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", dr); + cache.put("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514", dr1); - DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); - assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); - assertThat(record.getResponseData()).isEqualTo("Response"); - assertThat(status).isEqualTo(0); + DataRecord dr2 = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); + assertThat(dr2.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); + assertThat(dr2.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); + assertThat(dr2.getResponseData()).isEqualTo("Response"); + assertThat(status).isZero(); assertThat(cache).isEmpty(); } @Test - public void getRecord_invalidPayload_shouldThrowValidationException() { + void getRecord_invalidPayload_shouldThrowValidationException() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder() - .withEventKeyJMESPath("powertools_json(body).id") - .withPayloadValidationJMESPath("powertools_json(body).message") - .build(), + .withEventKeyJMESPath("powertools_json(body).id") + .withPayloadValidationJMESPath("powertools_json(body).message") + .build(), "myfunc"); this.validationHash = "different hash"; // "Lambda rocks" ==> 70c24d88041893f7fbab4105b76fd9e1 assertThatThrownBy( () -> persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), Instant.now())) - .isInstanceOf(IdempotencyValidationException.class); + .isInstanceOf(IdempotencyValidationException.class); } - //</editor-fold> - // ================================================================= + @Test + void saveInProgress_invalidPayload_shouldThrowValidationException() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore = new BasePersistenceStore() { + @Override + public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { + return new DataRecord(idempotencyKey, DataRecord.Status.INPROGRESS, + Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "Response", "different hash"); + } + + @Override + public void putRecord(DataRecord dataRecord, Instant now) throws IdempotencyItemAlreadyExistsException { + DataRecord existingRecord = new DataRecord( + dataRecord.getIdempotencyKey(), + DataRecord.Status.INPROGRESS, + Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), + null, + "different hash"); + throw new IdempotencyItemAlreadyExistsException("Item already exists", new Exception(), existingRecord); + } + + @Override + public void updateRecord(DataRecord dataRecord) { + // Not needed for this test. + } - // ================================================================= - //<editor-fold desc="deleteRecord"> + @Override + public void deleteRecord(String idempotencyKey) { + // Not needed for this test. + + } + }; + + persistenceStore.configure(IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).id") + .withPayloadValidationJMESPath("powertools_json(body).message") + .build(), + "myfunc"); + + Instant now = Instant.now(); + OptionalInt remainingTime = OptionalInt.empty(); + JsonNode eventJson = JsonConfig.get().getObjectMapper().valueToTree(event); + assertThatThrownBy(() -> persistenceStore.saveInProgress(eventJson, now, remainingTime)) + .isInstanceOf(IdempotencyValidationException.class); + } @Test - public void deleteRecord_shouldDeleteRecordFromPersistence() { + void deleteRecord_shouldDeleteRecordFromPersistence() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder().build(), null); @@ -356,25 +386,22 @@ public void deleteRecord_shouldDeleteRecordFromPersistence() { } @Test - public void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { + void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() .withUseLocalCache(true).build(), null, cache); - cache.put("testFunction#7b40f56c086de5aa91dc467456329ed2", - new DataRecord("testFunction#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, 123, null, + cache.put("testFunction#8d6a8f173b46479eff55e0997864a514", + new DataRecord("testFunction#8d6a8f173b46479eff55e0997864a514", DataRecord.Status.COMPLETED, 123, null, null)); persistenceStore.deleteRecord(JsonConfig.get().getObjectMapper().valueToTree(event), new ArithmeticException()); assertThat(status).isEqualTo(3); assertThat(cache).isEmpty(); } - //</editor-fold> - // ================================================================= - @Test - public void generateHashString_shouldGenerateMd5ofString() { + void generateHashString_shouldGenerateMd5ofString() { persistenceStore.configure(IdempotencyConfig.builder().build(), null); String expectedHash = "70c24d88041893f7fbab4105b76fd9e1"; // MD5(Lambda rocks) String generatedHash = persistenceStore.generateHash(new TextNode("Lambda rocks")); @@ -382,7 +409,7 @@ public void generateHashString_shouldGenerateMd5ofString() { } @Test - public void generateHashObject_shouldGenerateMd5ofJsonObject() { + void generateHashObject_shouldGenerateMd5ofJsonObject() { persistenceStore.configure(IdempotencyConfig.builder().build(), null); Product product = new Product(42, "Product", 12); String expectedHash = "e71c41727848ed68050d82740894c29b"; // MD5({"id":42,"name":"Product","price":12.0}) @@ -391,7 +418,7 @@ public void generateHashObject_shouldGenerateMd5ofJsonObject() { } @Test - public void generateHashDouble_shouldGenerateMd5ofDouble() { + void generateHashDouble_shouldGenerateMd5ofDouble() { persistenceStore.configure(IdempotencyConfig.builder().build(), null); String expectedHash = "bb84c94278119c8838649706df4db42b"; // MD5(256.42) String generatedHash = persistenceStore.generateHash(new DoubleNode(256.42)); @@ -399,16 +426,16 @@ public void generateHashDouble_shouldGenerateMd5ofDouble() { } @Test - public void generateHashString_withSha256Algorithm_shouldGenerateSha256ofString() { + void generateHashString_withSha256Algorithm_shouldGenerateSha256ofString() { persistenceStore.configure(IdempotencyConfig.builder().withHashFunction("SHA-256").build(), null); - String expectedHash = - "e6139efa88ef3337e901e826e6f327337f414860fb499d9f26eefcff21d719af"; // SHA-256(Lambda rocks) + String expectedHash = "e6139efa88ef3337e901e826e6f327337f414860fb499d9f26eefcff21d719af"; // SHA-256(Lambda + // rocks) String generatedHash = persistenceStore.generateHash(new TextNode("Lambda rocks")); assertThat(generatedHash).isEqualTo(expectedHash); } @Test - public void generateHashString_unknownAlgorithm_shouldGenerateMd5ofString() { + void generateHashString_unknownAlgorithm_shouldGenerateMd5ofString() { persistenceStore.configure(IdempotencyConfig.builder().withHashFunction("HASH").build(), null); String expectedHash = "70c24d88041893f7fbab4105b76fd9e1"; // MD5(Lambda rocks) String generatedHash = persistenceStore.generateHash(new TextNode("Lambda rocks")); diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java new file mode 100644 index 000000000..5fec80a3c --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.testutils; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; + +/** + * In-memory implementation of BasePersistenceStore for testing purposes. + */ +public class InMemoryPersistenceStore extends BasePersistenceStore { + private final Map<String, DataRecord> data = new HashMap<>(); + + @Override + public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { + DataRecord dr = data.get(idempotencyKey); + if (dr == null) { + throw new IdempotencyItemNotFoundException(idempotencyKey); + } + return dr; + } + + @Override + public void putRecord(DataRecord dr, Instant now) throws IdempotencyItemAlreadyExistsException { + if (data.containsKey(dr.getIdempotencyKey())) { + throw new IdempotencyItemAlreadyExistsException(); + } + data.put(dr.getIdempotencyKey(), dr); + } + + @Override + public void updateRecord(DataRecord dr) { + data.put(dr.getIdempotencyKey(), dr); + } + + @Override + public void deleteRecord(String idempotencyKey) { + data.remove(idempotencyKey); + } +} diff --git a/powertools-idempotency/src/test/resources/apigw_event.json b/powertools-idempotency/powertools-idempotency-core/src/test/resources/apigw_event.json similarity index 100% rename from powertools-idempotency/src/test/resources/apigw_event.json rename to powertools-idempotency/powertools-idempotency-core/src/test/resources/apigw_event.json diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/resources/simplelogger.properties b/powertools-idempotency/powertools-idempotency-core/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..dcded6172 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/resources/simplelogger.properties @@ -0,0 +1,7 @@ +org.slf4j.simpleLogger.logFile=target/idempotency-core-test.log +org.slf4j.simpleLogger.defaultLogLevel=warn +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS +org.slf4j.simpleLogger.showThreadName=false +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml new file mode 100644 index 000000000..d223e0d2f --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -0,0 +1,292 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2023 Amazon.com, Inc. or its affiliates. + ~ Licensed under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency</artifactId> + <version>2.9.0</version> + </parent> + + <artifactId>powertools-idempotency-dynamodb</artifactId> + <packaging>jar</packaging> + + <name>Powertools for AWS Lambda (Java) library Idempotency - DynamoDB</name> + <description> + DynamoDB implementation for the idempotency module + </description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>url-connection-client</artifactId> + <version>${aws.sdk.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>dynamodb</artifactId> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>netty-nio-client</artifactId> + </exclusion> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>apache-client</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <!-- DynamoDB Local for testing --> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>DynamoDBLocal</artifactId> + <!-- >2.2.0 is not compatible with Java 11 anymore --> + <!-- https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocalHistory.html --> + <version>2.2.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <!-- The AWS DynamoDBClient uses com.amazonaws.xray.interceptors.TracingInterceptor if available on the + classpath. If the DynamoDB persistence store is used together with tracing, this would fail in GraalVM + otherwise since it tries to load the tracing interceptor at runtime. This makes sure it is included in + reflect-config.json --> + <dependencies> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-xray-recorder-sdk-aws-sdk-v2-instrumentor</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-idempotency-dynamodb</imageName> + <exclusions> + <exclusion> + <groupId>com.amazonaws</groupId> + <artifactId>DynamoDBLocal</artifactId> + </exclusion> + </exclusions> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--enable-url-protocols=http</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <plugins> + <!-- Copy DynamoDB Local JAR --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dynamodb-local</id> + <phase>generate-test-resources</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <includeScope>test</includeScope> + <excludeTransitive>false</excludeTransitive> + <!-- MDEP-187: Avoids Maven phase circular dependency by not trying to copy unpacked reactor + artifacts (powertools-idempotency-core) --> + <excludeGroupIds>software.amazon.lambda</excludeGroupIds> + <outputDirectory>${project.build.directory}/dynamodb-local</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <!-- Start DynamoDB Local before tests --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <executions> + <execution> + <id>start-dynamodb-local</id> + <phase>process-test-classes</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <executable>java</executable> + <workingDirectory>${project.build.directory}/dynamodb-local</workingDirectory> + <arguments> + <argument>-Djava.library.path=${project.build.directory}/dynamodb-local</argument> + <argument>-cp</argument> + <argument>${project.build.directory}/dynamodb-local/*</argument> + <argument>com.amazonaws.services.dynamodbv2.local.main.ServerRunner</argument> + <argument>-inMemory</argument> + <argument>-port</argument> + <argument>8000</argument> + </arguments> + <async>true</async> + <asyncDestroyOnShutdown>true</asyncDestroyOnShutdown> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>3.4.2</version> + <configuration> + <archive> + <manifestEntries> + <Automatic-Module-Name>software.amazon.awssdk.enhanced.dynamodb</Automatic-Module-Name> + </manifestEntries> + </archive> + </configuration> + </plugin> + <!-- Configure Surefire to expose external DynamoDB Local address --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <systemPropertyVariables> + <dynamodb.endpoint>http://localhost:8000</dynamodb.endpoint> + </systemPropertyVariables> + </configuration> + </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>${aspectj-maven-plugin.version}</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <Xlint>ignore</Xlint> + <encoding>${project.build.sourceEncoding}</encoding> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-core</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + <goal>test-compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptor.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptor.java new file mode 100644 index 000000000..98d7a7440 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.idempotency.dynamodb.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-idempotency-dynamodb module is on the classpath. + */ +public final class IdempotencyDynamodbUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("idempotency-dynamodb"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java similarity index 88% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java index 82e7b9ead..c128fa5bd 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java @@ -12,19 +12,10 @@ * */ -package software.amazon.lambda.powertools.idempotency.persistence; +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_REGION_ENV; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; - -import java.time.Instant; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Map; -import java.util.OptionalLong; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import org.crac.Core; +import org.crac.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; @@ -39,17 +30,32 @@ import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; -import software.amazon.awssdk.utils.StringUtils; -import software.amazon.lambda.powertools.core.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; +import software.amazon.lambda.powertools.idempotency.persistence.PersistenceStore; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalLong; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static software.amazon.lambda.powertools.common.internal.LambdaConstants.AWS_REGION_ENV; +import static software.amazon.lambda.powertools.common.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; /** * DynamoDB version of the {@link PersistenceStore}. Will store idempotency data in DynamoDB.<br> * Use the {@link Builder} to create a new instance. */ -public class DynamoDBPersistenceStore extends BasePersistenceStore implements PersistenceStore { +public final class DynamoDBPersistenceStore extends BasePersistenceStore implements PersistenceStore, Resource { public static final String IDEMPOTENCY = "idempotency"; private static final Logger LOG = LoggerFactory.getLogger(DynamoDBPersistenceStore.class); @@ -106,6 +112,39 @@ private DynamoDBPersistenceStore(String tableName, this.dynamoDbClient = null; } } + Core.getGlobalContext().register(this); + } + + /** + * Primes the persistent store by invoking the get record method with a key that doesn't exist. + * + * @param context + * @throws Exception + */ + @Override + public void beforeCheckpoint(org.crac.Context<? extends Resource> context) throws Exception { + try { + String primingRecordKey = "__invoke_prime__"; + Instant now = Instant.now(); + long expiry = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); + DataRecord primingDataRecord = new DataRecord( + primingRecordKey, + DataRecord.Status.COMPLETED, + expiry, + null, // no data + null // no validation + ); + putRecord(primingDataRecord, Instant.now()); + getRecord(primingRecordKey); + deleteRecord(primingRecordKey); + } catch (Exception unknown) { + // This is unexpected but we must continue without any interruption + } + } + + @Override + public void afterRestore(org.crac.Context<? extends Resource> context) throws Exception { + // This is a no-op, as we don't need to do anything after restore } public static Builder builder() { @@ -186,10 +225,17 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre "attribute_not_exists(#id) OR #expiry < :now OR (attribute_exists(#in_progress_expiry) AND #in_progress_expiry < :now_milliseconds AND #status = :inprogress)") .expressionAttributeNames(expressionAttributeNames) .expressionAttributeValues(expressionAttributeValues) - .build() - ); + .returnValuesOnConditionCheckFailure("ALL_OLD") + .build()); } catch (ConditionalCheckFailedException e) { LOG.debug("Failed to put record for already existing idempotency key: {}", record.getIdempotencyKey()); + if (e.hasItem()) { + DataRecord existingRecord = itemToRecord(e.item()); + throw new IdempotencyItemAlreadyExistsException( + "Failed to put record for already existing idempotency key: " + record.getIdempotencyKey() + + ". Existing record: " + existingRecord, + e, existingRecord); + } throw new IdempotencyItemAlreadyExistsException( "Failed to put record for already existing idempotency key: " + record.getIdempotencyKey(), e); } @@ -310,7 +356,7 @@ public static class Builder { * @return an instance of the {@link DynamoDBPersistenceStore} */ public DynamoDBPersistenceStore build() { - if (StringUtils.isEmpty(tableName)) { + if (tableName == null || "".equals(tableName)) { throw new IllegalArgumentException("Table name is not specified"); } return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/reflect-config.json b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/reflect-config.json new file mode 100644 index 000000000..6d6fbceb3 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/reflect-config.json @@ -0,0 +1,329 @@ +[ +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.KeyDeserializers;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiators;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.deser.Deserializers;" +}, +{ + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.Context" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getHeaders","parameterTypes":[] }, {"name":"getHttpMethod","parameterTypes":[] }, {"name":"getIsBase64Encoded","parameterTypes":[] }, {"name":"getMultiValueHeaders","parameterTypes":[] }, {"name":"getMultiValueQueryStringParameters","parameterTypes":[] }, {"name":"getPath","parameterTypes":[] }, {"name":"getPathParameters","parameterTypes":[] }, {"name":"getQueryStringParameters","parameterTypes":[] }, {"name":"getRequestContext","parameterTypes":[] }, {"name":"getResource","parameterTypes":[] }, {"name":"getStageVariables","parameterTypes":[] }, {"name":"getVersion","parameterTypes":[] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setIsBase64Encoded","parameterTypes":["java.lang.Boolean"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setPathParameters","parameterTypes":["java.util.Map"] }, {"name":"setQueryStringParameters","parameterTypes":["java.util.Map"] }, {"name":"setRequestContext","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext"] }, {"name":"setResource","parameterTypes":["java.lang.String"] }, {"name":"setStageVariables","parameterTypes":["java.util.Map"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getAccountId","parameterTypes":[] }, {"name":"getApiId","parameterTypes":[] }, {"name":"getAuthorizer","parameterTypes":[] }, {"name":"getDomainName","parameterTypes":[] }, {"name":"getDomainPrefix","parameterTypes":[] }, {"name":"getExtendedRequestId","parameterTypes":[] }, {"name":"getHttpMethod","parameterTypes":[] }, {"name":"getIdentity","parameterTypes":[] }, {"name":"getOperationName","parameterTypes":[] }, {"name":"getPath","parameterTypes":[] }, {"name":"getProtocol","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }, {"name":"getRequestTime","parameterTypes":[] }, {"name":"getRequestTimeEpoch","parameterTypes":[] }, {"name":"getResourceId","parameterTypes":[] }, {"name":"getResourcePath","parameterTypes":[] }, {"name":"getStage","parameterTypes":[] }, {"name":"setAccountId","parameterTypes":["java.lang.String"] }, {"name":"setApiId","parameterTypes":["java.lang.String"] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setIdentity","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setProtocol","parameterTypes":["java.lang.String"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setRequestTime","parameterTypes":["java.lang.String"] }, {"name":"setRequestTimeEpoch","parameterTypes":["java.lang.Long"] }, {"name":"setResourceId","parameterTypes":["java.lang.String"] }, {"name":"setResourcePath","parameterTypes":["java.lang.String"] }, {"name":"setStage","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getAccessKey","parameterTypes":[] }, {"name":"getAccountId","parameterTypes":[] }, {"name":"getApiKey","parameterTypes":[] }, {"name":"getCaller","parameterTypes":[] }, {"name":"getCognitoAuthenticationProvider","parameterTypes":[] }, {"name":"getCognitoAuthenticationType","parameterTypes":[] }, {"name":"getCognitoIdentityId","parameterTypes":[] }, {"name":"getCognitoIdentityPoolId","parameterTypes":[] }, {"name":"getPrincipalOrgId","parameterTypes":[] }, {"name":"getSourceIp","parameterTypes":[] }, {"name":"getUser","parameterTypes":[] }, {"name":"getUserAgent","parameterTypes":[] }, {"name":"getUserArn","parameterTypes":[] }, {"name":"setAccessKey","parameterTypes":["java.lang.String"] }, {"name":"setAccountId","parameterTypes":["java.lang.String"] }, {"name":"setCaller","parameterTypes":["java.lang.String"] }, {"name":"setCognitoAuthenticationProvider","parameterTypes":["java.lang.String"] }, {"name":"setCognitoAuthenticationType","parameterTypes":["java.lang.String"] }, {"name":"setCognitoIdentityId","parameterTypes":["java.lang.String"] }, {"name":"setCognitoIdentityPoolId","parameterTypes":["java.lang.String"] }, {"name":"setSourceIp","parameterTypes":["java.lang.String"] }, {"name":"setUser","parameterTypes":["java.lang.String"] }, {"name":"setUserAgent","parameterTypes":["java.lang.String"] }, {"name":"setUserArn","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getHeaders","parameterTypes":[] }, {"name":"getIsBase64Encoded","parameterTypes":[] }, {"name":"getMultiValueHeaders","parameterTypes":[] }, {"name":"getStatusCode","parameterTypes":[] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setStatusCode","parameterTypes":["java.lang.Integer"] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSOperationHandler", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSOperationHandlerManifest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSOperationHandlerRequestDescriptor", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSOperationHandlerResponseDescriptor", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSServiceHandlerManifest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.interceptors.TracingInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.strategy.sampling.manifest.SamplingRuleManifest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setDefaultRule","parameterTypes":["com.amazonaws.xray.strategy.sampling.rule.SamplingRule"] }, {"name":"setRules","parameterTypes":["java.util.List"] }, {"name":"setVersion","parameterTypes":["int"] }] +}, +{ + "name":"com.amazonaws.xray.strategy.sampling.reservoir.Reservoir", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.xray.strategy.sampling.rule.SamplingRule", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setFixedTarget","parameterTypes":["int"] }, {"name":"setRate","parameterTypes":["float"] }] +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ARCFOURCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESedeCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.HmacCore$HmacSHA256", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"java.io.IOException", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Cloneable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.ProcessEnvironment", + "fields":[{"name":"theCaseInsensitiveEnvironment"}, {"name":"theEnvironment"}] +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.Thread", + "fields":[{"name":"threadLocalRandomProbe"}], + "methods":[{"name":"getContextClassLoader","parameterTypes":[] }] +}, +{ + "name":"java.security.AlgorithmParametersSpi" +}, +{ + "name":"java.security.KeyStoreSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.Striped64", + "fields":[{"name":"base"}, {"name":"cellsBusy"}] +}, +{ + "name":"javax.crac.Resource" +}, +{ + "name":"javax.security.auth.x500.X500Principal", + "fields":[{"name":"thisX500Name"}], + "methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }] +}, +{ + "name":"jdk.crac.Resource" +}, +{ + "name":"kotlin.Metadata" +}, +{ + "name":"kotlin.Unit" +}, +{ + "name":"org.apache.commons.logging.LogFactory" +}, +{ + "name":"org.apache.commons.logging.impl.Jdk14Logger", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }, {"name":"setLogFactory","parameterTypes":["org.apache.commons.logging.LogFactory"] }] +}, +{ + "name":"org.apache.commons.logging.impl.Log4JLogger" +}, +{ + "name":"org.apache.commons.logging.impl.LogFactoryImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.commons.logging.impl.WeakHashtable", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.joda.time.DateTime" +}, +{ + "name":"scala.util.Properties" +}, +{ + "name":"software.amazon.awssdk.enhanced.dynamodb.internal.ApplyUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.MD5", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA2$SHA256", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.NetscapeCertTypeExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.PrivateKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"software.amazon.lambda.powertools.idempotency.dynamodb.internal.IdempotencyDynamodbUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/resource-config.json b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/resource-config.json new file mode 100644 index 000000000..98e775bb6 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/resource-config.json @@ -0,0 +1,27 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.commons.logging.LogFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qcom/amazonaws/xray/interceptors/DefaultOperationParameterWhitelist.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/xray/sdk.properties\\E" + }, { + "pattern":"\\Qcom/amazonaws/xray/strategy/sampling/DefaultSamplingRules.json\\E" + }, { + "pattern":"\\Qcommons-logging.properties\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/services/dynamodb/execution.interceptors\\E" + }]}, + "bundles":[] +} diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..a04caa403 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.idempotency.dynamodb.internal.IdempotencyDynamodbUserAgentInterceptor diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptorTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptorTest.java new file mode 100644 index 000000000..e6940a760 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.idempotency.dynamodb.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class IdempotencyDynamodbUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/IDEMPOTENCY-DYNAMODB/"); + } +} diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java new file mode 100644 index 000000000..9f6875689 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; + +import java.net.URI; + +import org.junit.jupiter.api.BeforeAll; + +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; +import software.amazon.awssdk.services.dynamodb.model.BillingMode; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.KeyType; +import software.amazon.awssdk.services.dynamodb.model.ResourceInUseException; +import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; + +class DynamoDBConfig { + protected static final String TABLE_NAME = "idempotency_table"; + protected static DynamoDbClient client; + + @BeforeAll + static void setupDynamo() { + String endpoint = System.getProperty("dynamodb.endpoint", "http://localhost:8000"); + + client = DynamoDbClient.builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.EU_WEST_1) + .endpointOverride(URI.create(endpoint)) + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create("FAKE", "FAKE"))) + .build(); + + try { + client.createTable(CreateTableRequest.builder() + .tableName(TABLE_NAME) + .keySchema(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("id").build()) + .attributeDefinitions( + AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S) + .build()) + .billingMode(BillingMode.PAY_PER_REQUEST) + .build()); + } catch (ResourceInUseException e) { + // Table already exists, ignore + } catch (Exception e) { + throw new RuntimeException("Failed to create DynamoDB table", e); + } + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java similarity index 75% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java index b19cebfe1..b5c816286 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.idempotency.persistence; +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -22,10 +22,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.SetEnvironmentVariable; + import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.BillingMode; @@ -39,38 +41,36 @@ import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.lambda.powertools.idempotency.Constants; -import software.amazon.lambda.powertools.idempotency.DynamoDBConfig; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; /** * These test are using DynamoDBLocal and sqlite, see https://nickolasfisher.com/blog/Configuring-an-In-Memory-DynamoDB-instance-with-Java-for-Integration-Testing * NOTE: on a Mac with Apple Chipset, you need to use the Oracle JDK x86 64-bit */ -public class DynamoDBPersistenceStoreTest extends DynamoDBConfig { +class DynamoDBPersistenceStoreTest extends DynamoDBConfig { protected static final String TABLE_NAME_CUSTOM = "idempotency_table_custom"; private Map<String, AttributeValue> key; private DynamoDBPersistenceStore dynamoDBPersistenceStore; - // ================================================================= - //<editor-fold desc="putRecord"> @Test - public void putRecord_shouldCreateRecordInDynamoDB() throws IdempotencyItemAlreadyExistsException { + void putRecord_shouldCreateRecordInDynamoDB() throws IdempotencyItemAlreadyExistsException { Instant now = Instant.now(); long expiry = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); dynamoDBPersistenceStore.putRecord(new DataRecord("key", DataRecord.Status.COMPLETED, expiry, null, null), now); key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); - Map<String, AttributeValue> item = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map<String, AttributeValue> item = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(item).isNotNull(); assertThat(item.get("status").s()).isEqualTo("COMPLETED"); assertThat(item.get("expiration").n()).isEqualTo(String.valueOf(expiry)); } @Test - public void putRecord_shouldCreateRecordInDynamoDB_IfPreviousExpired() { + void putRecord_shouldCreateRecordInDynamoDB_IfPreviousExpired() { key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); // GIVEN: Insert a fake item with same id and expired @@ -89,22 +89,23 @@ public void putRecord_shouldCreateRecordInDynamoDB_IfPreviousExpired() { DataRecord.Status.INPROGRESS, expiry2, null, - null - ), now); + null), + now); // THEN: an item is inserted - Map<String, AttributeValue> itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map<String, AttributeValue> itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("INPROGRESS"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry2)); } @Test - public void putRecord_shouldCreateRecordInDynamoDB_IfLambdaWasInProgressAndTimedOut() { + void putRecord_shouldCreateRecordInDynamoDB_IfLambdaWasInProgressAndTimedOut() { key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); - // GIVEN: Insert a fake item with same id and progress expired (Lambda timed out before and we allow a new execution) + // GIVEN: Insert a fake item with same id and progress expired (Lambda timed out before and we allow a new + // execution) Map<String, AttributeValue> item = new HashMap<>(key); Instant now = Instant.now(); long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); @@ -122,19 +123,19 @@ public void putRecord_shouldCreateRecordInDynamoDB_IfLambdaWasInProgressAndTimed DataRecord.Status.INPROGRESS, expiry2, null, - null - ), now); + null), + now); // THEN: an item is inserted - Map<String, AttributeValue> itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map<String, AttributeValue> itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("INPROGRESS"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry2)); } @Test - public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordAlreadyExist() { + void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordAlreadyExist() { key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); // GIVEN: Insert a fake item with same id @@ -148,18 +149,19 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA // WHEN: call putRecord long expiry2 = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); - assertThatThrownBy(() -> dynamoDBPersistenceStore.putRecord( - new DataRecord("key", - DataRecord.Status.INPROGRESS, - expiry2, - null, - null - ), now) - ).isInstanceOf(IdempotencyItemAlreadyExistsException.class); + DataRecord recordToInsert = new DataRecord("key", + DataRecord.Status.INPROGRESS, + expiry2, + null, + null); + assertThatThrownBy(() -> dynamoDBPersistenceStore.putRecord(recordToInsert, now)) + .isInstanceOf(IdempotencyItemAlreadyExistsException.class) + // DataRecord should be present due to returnValuesOnConditionCheckFailure("ALL_OLD") + .matches(e -> ((IdempotencyItemAlreadyExistsException) e).getDataRecord().isPresent()); // THEN: item was not updated, retrieve the initial one - Map<String, AttributeValue> itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map<String, AttributeValue> itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("COMPLETED"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); @@ -167,7 +169,7 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA } @Test - public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpiredAfterLambdaTimedOut() { + void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpiredAfterLambdaTimedOut() { key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); // GIVEN: Insert a fake item with same id @@ -183,33 +185,27 @@ public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpire // WHEN: call putRecord long expiry2 = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); - assertThatThrownBy(() -> dynamoDBPersistenceStore.putRecord( - new DataRecord("key", - DataRecord.Status.INPROGRESS, - expiry2, - "Fake Data 2", - null - ), now)) - .isInstanceOf(IdempotencyItemAlreadyExistsException.class); + DataRecord recordToInsert = new DataRecord("key", + DataRecord.Status.INPROGRESS, + expiry2, + "Fake Data 2", + null); + assertThatThrownBy(() -> dynamoDBPersistenceStore.putRecord(recordToInsert, now)) + .isInstanceOf(IdempotencyItemAlreadyExistsException.class) + // DataRecord should be present due to returnValuesOnConditionCheckFailure("ALL_OLD") + .matches(e -> ((IdempotencyItemAlreadyExistsException) e).getDataRecord().isPresent()); // THEN: item was not updated, retrieve the initial one - Map<String, AttributeValue> itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map<String, AttributeValue> itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("INPROGRESS"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); assertThat(itemInDb.get("data").s()).isEqualTo("Fake Data"); } - - //</editor-fold> - // ================================================================= - - // ================================================================= - //<editor-fold desc="getRecord"> - @Test - public void getRecord_shouldReturnExistingRecord() throws IdempotencyItemNotFoundException { + void getRecord_shouldReturnExistingRecord() throws IdempotencyItemNotFoundException { key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); // GIVEN: Insert a fake item with same id @@ -222,29 +218,23 @@ public void getRecord_shouldReturnExistingRecord() throws IdempotencyItemNotFoun client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); // WHEN - DataRecord record = dynamoDBPersistenceStore.getRecord("key"); + DataRecord dr = dynamoDBPersistenceStore.getRecord("key"); // THEN - assertThat(record.getIdempotencyKey()).isEqualTo("key"); - assertThat(record.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); - assertThat(record.getResponseData()).isEqualTo("Fake Data"); - assertThat(record.getExpiryTimestamp()).isEqualTo(expiry); + assertThat(dr.getIdempotencyKey()).isEqualTo("key"); + assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); + assertThat(dr.getResponseData()).isEqualTo("Fake Data"); + assertThat(dr.getExpiryTimestamp()).isEqualTo(expiry); } @Test - public void getRecord_shouldThrowException_whenRecordIsAbsent() { - assertThatThrownBy(() -> dynamoDBPersistenceStore.getRecord("key")).isInstanceOf( - IdempotencyItemNotFoundException.class); + void getRecord_shouldThrowException_whenRecordIsAbsent() { + assertThatThrownBy(() -> dynamoDBPersistenceStore.getRecord("key")) + .isInstanceOf(IdempotencyItemNotFoundException.class); } - //</editor-fold> - // ================================================================= - - // ================================================================= - //<editor-fold desc="updateRecord"> - @Test - public void updateRecord_shouldUpdateRecord() { + void updateRecord_shouldUpdateRecord() { // GIVEN: Insert a fake item with same id key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); Map<String, AttributeValue> item = new HashMap<>(key); @@ -259,26 +249,20 @@ public void updateRecord_shouldUpdateRecord() { // WHEN expiry = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); - DataRecord record = new DataRecord("key", DataRecord.Status.COMPLETED, expiry, "Fake result", "hash"); - dynamoDBPersistenceStore.updateRecord(record); + DataRecord dr = new DataRecord("key", DataRecord.Status.COMPLETED, expiry, "Fake result", "hash"); + dynamoDBPersistenceStore.updateRecord(dr); // THEN - Map<String, AttributeValue> itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map<String, AttributeValue> itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb.get("status").s()).isEqualTo("COMPLETED"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); assertThat(itemInDb.get("data").s()).isEqualTo("Fake result"); assertThat(itemInDb.get("validation").s()).isEqualTo("hash"); } - //</editor-fold> - // ================================================================= - - // ================================================================= - //<editor-fold desc="deleteRecord"> - @Test - public void deleteRecord_shouldDeleteRecord() { + void deleteRecord_shouldDeleteRecord() { // GIVEN: Insert a fake item with same id key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); Map<String, AttributeValue> item = new HashMap<>(key); @@ -293,27 +277,22 @@ public void deleteRecord_shouldDeleteRecord() { dynamoDBPersistenceStore.deleteRecord("key"); // THEN - assertThat(client.scan(ScanRequest.builder().tableName(TABLE_NAME).build()).count()).isEqualTo(0); + assertThat(client.scan(ScanRequest.builder().tableName(TABLE_NAME).build()).count()).isZero(); } - //</editor-fold> - // ================================================================= - @Test - public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFoundException { + void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFoundException { try { client.createTable(CreateTableRequest.builder() .tableName(TABLE_NAME_CUSTOM) .keySchema( KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("key").build(), - KeySchemaElement.builder().keyType(KeyType.RANGE).attributeName("sortkey").build() - ) + KeySchemaElement.builder().keyType(KeyType.RANGE).attributeName("sortkey").build()) .attributeDefinitions( AttributeDefinition.builder().attributeName("key").attributeType(ScalarAttributeType.S) .build(), AttributeDefinition.builder().attributeName("sortkey").attributeType(ScalarAttributeType.S) - .build() - ) + .build()) .billingMode(BillingMode.PAY_PER_REQUEST) .build()); @@ -330,22 +309,21 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou .build(); Instant now = Instant.now(); - DataRecord record = new DataRecord( + DataRecord dr = new DataRecord( "mykey", DataRecord.Status.INPROGRESS, now.plus(400, ChronoUnit.SECONDS).getEpochSecond(), null, - null - ); + null); // PUT - persistenceStore.putRecord(record, now); + persistenceStore.putRecord(dr, now); Map<String, AttributeValue> customKey = new HashMap<>(); customKey.put("key", AttributeValue.builder().s("pk").build()); customKey.put("sortkey", AttributeValue.builder().s("mykey").build()); - Map<String, AttributeValue> itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME_CUSTOM).key(customKey).build()).item(); + Map<String, AttributeValue> itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME_CUSTOM).key(customKey).build()).item(); // GET DataRecord recordInDb = persistenceStore.getRecord("mykey"); @@ -362,8 +340,7 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou DataRecord.Status.COMPLETED, now.plus(500, ChronoUnit.SECONDS).getEpochSecond(), "response", - null - ); + null); persistenceStore.updateRecord(updatedRecord); recordInDb = persistenceStore.getRecord("mykey"); assertThat(recordInDb).isEqualTo(updatedRecord); @@ -383,13 +360,14 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou @Test @SetEnvironmentVariable(key = Constants.IDEMPOTENCY_DISABLED_ENV, value = "true") - public void idempotencyDisabled_noClientShouldBeCreated() { + void idempotencyDisabled_noClientShouldBeCreated() { DynamoDBPersistenceStore store = DynamoDBPersistenceStore.builder().withTableName(TABLE_NAME).build(); - assertThatThrownBy(() -> store.getRecord("fake")).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> store.getRecord("fake")) + .isInstanceOf(NullPointerException.class); } @BeforeEach - public void setup() { + void setup() { dynamoDBPersistenceStore = DynamoDBPersistenceStore.builder() .withTableName(TABLE_NAME) .withDynamoDbClient(client) @@ -397,10 +375,14 @@ public void setup() { } @AfterEach - public void emptyDB() { - if (key != null) { - client.deleteItem(DeleteItemRequest.builder().tableName(TABLE_NAME).key(key).build()); - key = null; - } + void emptyDB() { + // Clear all items from the table + client.scan(ScanRequest.builder().tableName(TABLE_NAME).build()) + .items() + .forEach(item -> { + Map<String, AttributeValue> itemKey = Collections.singletonMap("id", item.get("id")); + client.deleteItem(DeleteItemRequest.builder().tableName(TABLE_NAME).key(itemKey).build()); + }); + key = null; } } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java similarity index 65% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java index c94fec3db..e85614580 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java @@ -12,43 +12,36 @@ * */ -package software.amazon.lambda.powertools.idempotency; - +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyFunction; -public class IdempotencyTest extends DynamoDBConfig { +import software.amazon.awssdk.services.dynamodb.model.ScanRequest; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.handlers.IdempotencyFunction; - @Mock - private Context context; +class IdempotencyTest extends DynamoDBConfig { - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - } + private Context context = new TestLambdaContext(); @Test - public void endToEndTest() { + void endToEndTest() { IdempotencyFunction function = new IdempotencyFunction(client); - APIGatewayProxyResponseEvent response = - function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); + APIGatewayProxyResponseEvent response = function + .handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); assertThat(function.handlerExecuted).isTrue(); function.handlerExecuted = false; - APIGatewayProxyResponseEvent response2 = - function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); + APIGatewayProxyResponseEvent response2 = function + .handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); assertThat(function.handlerExecuted).isFalse(); assertThat(response).isEqualTo(response2); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java similarity index 59% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java index 76c36ae9f..d816af801 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java @@ -12,45 +12,37 @@ * */ -package software.amazon.lambda.powertools.idempotency.handlers; +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb.handlers; + +import java.util.HashMap; +import java.util.Map; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.Idempotent; -import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; -import software.amazon.lambda.powertools.utilities.JsonConfig; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; public class IdempotencyFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger LOG = LogManager.getLogger(IdempotencyFunction.class); - public boolean handlerExecuted = false; public IdempotencyFunction(DynamoDbClient client) { // we need to initialize idempotency configuration before the handleRequest method is called Idempotency.config().withConfig( - IdempotencyConfig.builder() - .withEventKeyJMESPath("powertools_json(body).address") - .build()) + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).address") + .build()) .withPersistenceStore( DynamoDBPersistenceStore.builder() .withTableName("idempotency_table") .withDynamoDbClient(client) - .build() - ).configure(); + .build()) + .configure(); } @Idempotent @@ -65,28 +57,10 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() .withHeaders(headers); - try { - String address = JsonConfig.get().getObjectMapper().readTree(input.getBody()).get("address").asText(); - final String pageContents = this.getPageContents(address); - String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); - LOG.debug("ip is {}", pageContents); - return response - .withStatusCode(200) - .withBody(output); - - } catch (IOException e) { - return response - .withBody("{}") - .withStatusCode(500); - } - } + return response + .withStatusCode(200) + .withBody("{ \"message\": \"hello world\"}"); - // we could actually also put the @Idempotent annotation here - private String getPageContents(String address) throws IOException { - URL url = new URL(address); - try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { - return br.lines().collect(Collectors.joining(System.lineSeparator())); - } } } diff --git a/powertools-idempotency/src/test/resources/apigw_event2.json b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/resources/apigw_event2.json similarity index 100% rename from powertools-idempotency/src/test/resources/apigw_event2.json rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/resources/apigw_event2.json diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/resources/simplelogger.properties b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..6d188691f --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/resources/simplelogger.properties @@ -0,0 +1,7 @@ +org.slf4j.simpleLogger.logFile=target/idempotency-dynamodb-test.log +org.slf4j.simpleLogger.defaultLogLevel=warn +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS +org.slf4j.simpleLogger.showThreadName=false +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false \ No newline at end of file diff --git a/powertools-idempotency/spotbugs-exclude.xml b/powertools-idempotency/spotbugs-exclude.xml new file mode 100644 index 000000000..9a2369c75 --- /dev/null +++ b/powertools-idempotency/spotbugs-exclude.xml @@ -0,0 +1,46 @@ +<!-- This file specifies a spotbugs filter for excluding reports that + should not be considered errors. + The format of this file is documented at: + https://spotbugs.readthedocs.io/en/latest/filter.html + When possible, please specify the full names of the bug codes, + using the pattern attribute, to make it clearer what reports are + being suppressed. You can find a listing of codes at: + https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html + --> +<FindBugsFilter> + <!-- Internals of Log event for apache log4j--> + <Match> + <Bug pattern="EI_EXPOSE_REP"/> + <Or> + <And> + <Class name="software.amazon.lambda.powertools.idempotency.Idempotency"/> + <Method name="getPersistenceStore"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.idempotency.Idempotency"/> + <Method name="getConfig"/> + </And> + </Or> + </Match> + <Match> + <Bug pattern="EI_EXPOSE_REP2"/> + <Or> + <And> + <Class name="software.amazon.lambda.powertools.idempotency.Idempotency$Config"/> + <Field name="store"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.idempotency.Idempotency$Config"/> + <Field name="config"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.idempotency.internal.IdempotencyHandler"/> + <Field name="pjp"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore$Builder"/> + <Field name="dynamoDbClient"/> + </And> + </Or> + </Match> +</FindBugsFilter> \ No newline at end of file diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java deleted file mode 100644 index 6da826c45..000000000 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.idempotency; - -import com.amazonaws.services.lambda.runtime.Context; -import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; - -/** - * Holds the configuration for idempotency: - * <ul> - * <li>The persistence layer to use for persisting the request and response of the function (mandatory).</li> - * <li>The general configuration for idempotency (optional, see {@link IdempotencyConfig.Builder} methods to see defaults values.</li> - * </ul> - * <br/> - * Use it before the function handler ({@link com.amazonaws.services.lambda.runtime.RequestHandler#handleRequest(Object, Context)}) - * get called. - * <br/> - * Example: - * <pre> - * Idempotency.config().withPersistenceStore(...).configure(); - * </pre> - */ -public class Idempotency { - private IdempotencyConfig config; - private BasePersistenceStore persistenceStore; - - private Idempotency() { - } - - public static Idempotency getInstance() { - return Holder.instance; - } - - /** - * Can be used in a method which is not the handler to capture the Lambda context, - * to calculate the remaining time before the invocation times out. - * - * @param lambdaContext - */ - public static void registerLambdaContext(Context lambdaContext) { - getInstance().getConfig().setLambdaContext(lambdaContext); - } - - /** - * Acts like a builder that can be used to configure {@link Idempotency} - * - * @return a new instance of {@link Config} - */ - public static Config config() { - return new Config(); - } - - public IdempotencyConfig getConfig() { - return config; - } - - private void setConfig(IdempotencyConfig config) { - this.config = config; - } - - public BasePersistenceStore getPersistenceStore() { - if (persistenceStore == null) { - throw new IllegalStateException("Persistence Store is null, did you call 'configure()'?"); - } - return persistenceStore; - } - - private void setPersistenceStore(BasePersistenceStore persistenceStore) { - this.persistenceStore = persistenceStore; - } - - private static class Holder { - private final static Idempotency instance = new Idempotency(); - } - - public static class Config { - - private IdempotencyConfig config; - private BasePersistenceStore store; - - /** - * Use this method after configuring persistence layer (mandatory) and idem potency configuration (optional) - */ - public void configure() { - if (store == null) { - throw new IllegalStateException( - "Persistence Layer is null, configure one with 'withPersistenceStore()'"); - } - if (config == null) { - config = IdempotencyConfig.builder().build(); - } - Idempotency.getInstance().setConfig(config); - Idempotency.getInstance().setPersistenceStore(store); - } - - public Config withPersistenceStore(BasePersistenceStore persistenceStore) { - this.store = persistenceStore; - return this; - } - - public Config withConfig(IdempotencyConfig config) { - this.config = config; - return this; - } - } - - -} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java deleted file mode 100644 index 66ddb53ac..000000000 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.idempotency; - -import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; -import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.URI; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.BillingMode; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -public class DynamoDBConfig { - protected static final String TABLE_NAME = "idempotency_table"; - protected static DynamoDBProxyServer dynamoProxy; - protected static DynamoDbClient client; - - @BeforeAll - public static void setupDynamo() { - int port = getFreePort(); - try { - dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[] { - "-inMemory", - "-port", - Integer.toString(port) - }); - dynamoProxy.start(); - } catch (Exception e) { - throw new RuntimeException(); - } - - client = DynamoDbClient.builder() - .httpClient(UrlConnectionHttpClient.builder().build()) - .region(Region.EU_WEST_1) - .endpointOverride(URI.create("http://localhost:" + port)) - .credentialsProvider(StaticCredentialsProvider.create( - AwsBasicCredentials.create("FAKE", "FAKE"))) - .build(); - - client.createTable(CreateTableRequest.builder() - .tableName(TABLE_NAME) - .keySchema(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("id").build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S).build() - ) - .billingMode(BillingMode.PAY_PER_REQUEST) - .build()); - - DescribeTableResponse response = - client.describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); - if (response == null) { - throw new RuntimeException("Table was not created within expected time"); - } - } - - @AfterAll - public static void teardownDynamo() { - try { - dynamoProxy.stop(); - } catch (Exception e) { - throw new RuntimeException(); - } - } - - private static int getFreePort() { - try { - ServerSocket socket = new ServerSocket(0); - int port = socket.getLocalPort(); - socket.close(); - return port; - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } -} diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml new file mode 100644 index 000000000..c71ef94f6 --- /dev/null +++ b/powertools-kafka/pom.xml @@ -0,0 +1,266 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2023 Amazon.com, Inc. or its affiliates. + ~ Licensed under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>powertools-parent</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>2.9.0</version> + </parent> + + <artifactId>powertools-kafka</artifactId> + <packaging>jar</packaging> + + <name>Powertools for AWS Lambda (Java) - Kafka Consumer</name> + <description> + The Kafka utility transparently handles message deserialization, provides an intuitive developer experience, + and integrates seamlessly with the rest of the Powertools for AWS Lambda ecosystem. + </description> + + <properties> + <kafka-clients.version>4.1.1</kafka-clients.version> + <avro.version>1.12.1</avro.version> + <protobuf.version>4.33.1</protobuf.version> + <lambda-serialization.version>1.1.6</lambda-serialization.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>org.apache.kafka</groupId> + <artifactId>kafka-clients</artifactId> + <version>${kafka-clients.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.avro</groupId> + <artifactId>avro</artifactId> + <version>${avro.version}</version> + </dependency> + <dependency> + <groupId>com.google.protobuf</groupId> + <artifactId>protobuf-java</artifactId> + <version>${protobuf.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-serialization</artifactId> + <version>${lambda-serialization.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> + <testResources> + <testResource> + <directory>src/test/resources</directory> + </testResource> + </testResources> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>${aspectj-maven-plugin.version}</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <!-- https://junit-pioneer.org/docs/environment-variables/#warnings-for-reflective-access --> + <!-- @{argLine} makes sure not other args are lost. They are required for jacoco coverage reports. --> + <argLine> + @{argLine} + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + <!-- Avro plugin for test classes only --> + <plugin> + <groupId>org.apache.avro</groupId> + <artifactId>avro-maven-plugin</artifactId> + <version>${avro.version}</version> + <executions> + <execution> + <id>generate-test-sources</id> + <phase>generate-test-sources</phase> + <goals> + <goal>schema</goal> + </goals> + <configuration> + <sourceDirectory>${project.basedir}/src/test/avro/</sourceDirectory> + <outputDirectory>${project.basedir}/target/generated-test-sources/avro/</outputDirectory> + <stringType>String</stringType> + <testSourceDirectory>${project.basedir}/src/test/avro/</testSourceDirectory> + <testOutputDirectory>${project.basedir}/target/generated-test-sources/avro/</testOutputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <!-- Protobuf plugin for test classes only --> + <plugin> + <groupId>io.github.ascopes</groupId> + <artifactId>protobuf-maven-plugin</artifactId> + <version>3.10.3</version> + <executions> + <execution> + <id>generate-test-sources</id> + <goals> + <goal>generate-test</goal> + </goals> + <phase>generate-test-sources</phase> + <configuration> + <protocVersion>${protobuf.version}</protocVersion> + <sourceDirectories> + <sourceDirectory>${project.basedir}/src/test/proto</sourceDirectory> + </sourceDirectories> + <outputDirectory>${project.basedir}/target/generated-test-sources/protobuf</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <!-- Add generated test sources to build --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>${project.basedir}/target/generated-test-sources/avro</source> + <source>${project.basedir}/target/generated-test-sources/protobuf</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>generate-classesloaded-file</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Xlog:class+load=info:classesloaded.txt + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + +</project> diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/Deserialization.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/Deserialization.java new file mode 100644 index 000000000..4b96c49db --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/Deserialization.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to specify the deserialization type for Kafka messages. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Deserialization { + /** + * The type of deserialization to use. + * @return the deserialization type + */ + DeserializationType type(); +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/DeserializationType.java similarity index 75% rename from powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java rename to powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/DeserializationType.java index a553abbbd..a4ac95389 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/DeserializationType.java @@ -9,14 +9,9 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ +package software.amazon.lambda.powertools.kafka; -package software.amazon.lambda.powertools.metrics; - -public class ValidationException extends RuntimeException { - - public ValidationException(String message) { - super(message); - } +public enum DeserializationType { + LAMBDA_DEFAULT, KAFKA_JSON, KAFKA_AVRO, KAFKA_PROTOBUF } diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializer.java new file mode 100644 index 000000000..619e46729 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializer.java @@ -0,0 +1,142 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +import com.amazonaws.services.lambda.runtime.CustomPojoSerializer; +import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory; +import com.amazonaws.services.lambda.runtime.events.KafkaEvent; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.crac.Context; +import org.crac.Core; +import org.crac.Resource; +import software.amazon.lambda.powertools.common.internal.ClassPreLoader; +import software.amazon.lambda.powertools.kafka.internal.DeserializationUtils; +import software.amazon.lambda.powertools.kafka.serializers.KafkaAvroDeserializer; +import software.amazon.lambda.powertools.kafka.serializers.KafkaJsonDeserializer; +import software.amazon.lambda.powertools.kafka.serializers.KafkaProtobufDeserializer; +import software.amazon.lambda.powertools.kafka.serializers.LambdaDefaultDeserializer; +import software.amazon.lambda.powertools.kafka.serializers.PowertoolsDeserializer; + +/** + * Custom Lambda serializer supporting Kafka events. It delegates to the appropriate deserializer based on the + * deserialization type specified by {@link software.amazon.lambda.powertools.kafka.Deserialization} annotation. + * + * Kafka serializers need to be specified explicitly, otherwise, the default Lambda serializer from + * {@link com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory} will be used. + */ +public class PowertoolsSerializer implements CustomPojoSerializer, Resource { + private static final Map<DeserializationType, PowertoolsDeserializer> DESERIALIZERS = Map.of( + DeserializationType.KAFKA_JSON, new KafkaJsonDeserializer(), + DeserializationType.KAFKA_AVRO, new KafkaAvroDeserializer(), + DeserializationType.KAFKA_PROTOBUF, new KafkaProtobufDeserializer(), + DeserializationType.LAMBDA_DEFAULT, new LambdaDefaultDeserializer()); + + private final PowertoolsDeserializer deserializer; + + private static final PowertoolsSerializer INSTANCE = new PowertoolsSerializer(); + + // CRaC registration happens at class loading time + static { + Core.getGlobalContext().register(INSTANCE); + } + + public PowertoolsSerializer() { + this.deserializer = DESERIALIZERS.getOrDefault( + DeserializationUtils.determineDeserializationType(), + new LambdaDefaultDeserializer()); + } + + @Override + public <T> T fromJson(InputStream input, Type type) { + return deserializer.fromJson(input, type); + } + + @Override + public <T> T fromJson(String input, Type type) { + return deserializer.fromJson(input, type); + } + + @Override + public <T> void toJson(T value, OutputStream output, Type type) { + // This is the Lambda default Output serialization + JacksonFactory.getInstance().getSerializer(type).toJson(value, output); + } + + @Override + public void beforeCheckpoint(Context<? extends Resource> context) throws Exception { + JacksonFactory.getInstance().getSerializer(KafkaEvent.class); + JacksonFactory.getInstance().getSerializer(ConsumerRecord.class); + JacksonFactory.getInstance().getSerializer(String.class); + + DeserializationUtils.determineDeserializationType(); + + jsonPriming(); + + ClassPreLoader.preloadClasses(); + } + + @Override + public void afterRestore(Context<? extends Resource> context) throws Exception { + // No action needed after restore + } + + private void jsonPriming() { + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"prime-topic-1\": [\n" + + " {\n" + + " \"topic\": \"prime-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 0,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": null,\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type consumerRecords = createConsumerRecordsType(String.class, String.class); + PowertoolsDeserializer deserializers = DESERIALIZERS.get(DeserializationType.KAFKA_JSON); + deserializers.fromJson(kafkaJson, consumerRecords); + } + + private Type createConsumerRecordsType(Class<?> keyClass, Class<?> valueClass) { + return new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return new Type[] { keyClass, valueClass }; + } + + @Override + public Type getRawType() { + return ConsumerRecords.class; + } + + @Override + public Type getOwnerType() { + return null; + } + }; + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtils.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtils.java new file mode 100644 index 000000000..1d2fe9aca --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.internal; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; + +/** + * Utility class to determine the deserialization type from Lambda request handler methods annotated with + * {@link Deserialization} utility. + * + * Relies on the Lambda _HANDLER environment variable to detect the currently active handler method. + */ +public final class DeserializationUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(DeserializationUtils.class); + + private DeserializationUtils() { + } + + public static DeserializationType determineDeserializationType() { + String handler = System.getenv("_HANDLER"); + if (handler == null || handler.trim().isEmpty()) { + LOGGER.error("Cannot determine deserialization type. No valid handler found in _HANDLER: {}", handler); + return DeserializationType.LAMBDA_DEFAULT; + } + + try { + HandlerInfo handlerInfo = parseHandler(handler); + Class<?> handlerClazz = Class.forName(handlerInfo.className); + + if (!RequestHandler.class.isAssignableFrom(handlerClazz)) { + LOGGER.warn("Class '{}' does not implement RequestHandler. Ignoring.", handlerInfo.className); + return DeserializationType.LAMBDA_DEFAULT; + } + + return findDeserializationType(handlerClazz, handlerInfo.methodName); + } catch (Exception e) { + LOGGER.warn("Cannot determine deserialization type. Defaulting to standard.", e); + return DeserializationType.LAMBDA_DEFAULT; + } + } + + private static HandlerInfo parseHandler(String handler) { + if (handler.contains("::")) { + int separatorIndex = handler.indexOf("::"); + String className = handler.substring(0, separatorIndex); + String methodName = handler.substring(separatorIndex + 2); + return new HandlerInfo(className, methodName); + } + + return new HandlerInfo(handler); + } + + private static DeserializationType findDeserializationType(Class<?> handlerClass, String methodName) { + for (Method method : handlerClass.getDeclaredMethods()) { + if (method.getName().equals(methodName) && method.isAnnotationPresent(Deserialization.class)) { + Deserialization annotation = method.getAnnotation(Deserialization.class); + LOGGER.debug("Found deserialization type: {}", annotation.type()); + return annotation.type(); + } + } + + return DeserializationType.LAMBDA_DEFAULT; + } + + private static class HandlerInfo { + final String className; + final String methodName; + + HandlerInfo(String className) { + this(className, "handleRequest"); + } + + HandlerInfo(String className, String methodName) { + this.className = className; + this.methodName = methodName; + } + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptor.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptor.java new file mode 100644 index 000000000..2db4789b3 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.kafka.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-kafka module is on the classpath. + */ +public final class KafkaUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("kafka"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializer.java new file mode 100644 index 000000000..3de8320a5 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializer.java @@ -0,0 +1,332 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.header.Headers; +import org.apache.kafka.common.header.internals.RecordHeaders; +import org.apache.kafka.common.record.TimestampType; + +import com.amazonaws.services.lambda.runtime.events.KafkaEvent; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Abstract base class for Kafka deserializers that implements common functionality. + */ +abstract class AbstractKafkaDeserializer implements PowertoolsDeserializer { + protected static final ObjectMapper objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + private static final Integer GLUE_SCHEMA_ID_LENGTH = 36; + + public enum SchemaRegistryType { + CONFLUENT, GLUE, NONE + } + + /** + * Deserialize JSON from InputStream into ConsumerRecords + * + * @param input InputStream containing JSON data + * @param type Type representing ConsumerRecords<K, V> + * @param <T> The type to deserialize to + * @return Deserialized ConsumerRecords object + * @throws IllegalArgumentException if type is not ConsumerRecords + */ + @SuppressWarnings("unchecked") + @Override + public <T> T fromJson(InputStream input, Type type) { + if (!isConsumerRecordsType(type)) { + throw new IllegalArgumentException("Type must be ConsumerRecords<K, V> when using this deserializer"); + } + + try { + // Parse the KafkaEvent from the input stream + KafkaEvent kafkaEvent = objectMapper.readValue(input, KafkaEvent.class); + + // Extract the key and value types from the ConsumerRecords<K, V> type + ParameterizedType parameterizedType = (ParameterizedType) type; + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + Class<?> keyType = (Class<?>) typeArguments[0]; + Class<?> valueType = (Class<?>) typeArguments[1]; + + // Convert KafkaEvent to ConsumerRecords + return (T) convertToConsumerRecords(kafkaEvent, keyType, valueType); + } catch (IOException e) { + throw new RuntimeException("Failed to deserialize Lambda handler input to ConsumerRecords", e); + } + } + + /** + * Deserialize JSON from String into ConsumerRecords + * + * @param input String containing JSON data + * @param type Type representing ConsumerRecords<K, V> + * @param <T> The type to deserialize to + * @return Deserialized ConsumerRecords object + * @throws IllegalArgumentException if type is not ConsumerRecords + */ + @SuppressWarnings("unchecked") + @Override + public <T> T fromJson(String input, Type type) { + if (!isConsumerRecordsType(type)) { + throw new IllegalArgumentException("Type must be ConsumerRecords<K, V> when using this deserializer"); + } + + try { + // Parse the KafkaEvent from the input string + KafkaEvent kafkaEvent = objectMapper.readValue(input, KafkaEvent.class); + + // Extract the key and value types from the ConsumerRecords<K, V> type + ParameterizedType parameterizedType = (ParameterizedType) type; + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + Class<?> keyType = (Class<?>) typeArguments[0]; + Class<?> valueType = (Class<?>) typeArguments[1]; + + // Convert KafkaEvent to ConsumerRecords + return (T) convertToConsumerRecords(kafkaEvent, keyType, valueType); + } catch (IOException e) { + throw new RuntimeException("Failed to deserialize Lambda handler input to ConsumerRecords", e); + } + } + + private boolean isConsumerRecordsType(Type type) { + if (!(type instanceof ParameterizedType)) { + return false; + } + + ParameterizedType parameterizedType = (ParameterizedType) type; + return parameterizedType.getRawType().equals(ConsumerRecords.class); + } + + private <K, V> ConsumerRecords<K, V> convertToConsumerRecords(KafkaEvent kafkaEvent, Class<K> keyType, + Class<V> valueType) { + // Validate that this is actually a Kafka event by checking for required properties + if (kafkaEvent == null || kafkaEvent.getEventSource() == null) { + throw new RuntimeException( + "Failed to deserialize Lambda handler input to ConsumerRecords: Input is not a valid Kafka event."); + } + + if (kafkaEvent.getRecords() == null) { + return ConsumerRecords.empty(); + } + + Map<TopicPartition, List<ConsumerRecord<K, V>>> recordsMap = new HashMap<>(); + + for (Map.Entry<String, List<KafkaEvent.KafkaEventRecord>> entry : kafkaEvent.getRecords().entrySet()) { + String topic = entry.getKey(); + + for (KafkaEvent.KafkaEventRecord eventRecord : entry.getValue()) { + ConsumerRecord<K, V> consumerRecord = convertToConsumerRecord(topic, eventRecord, keyType, valueType); + + TopicPartition topicPartition = new TopicPartition(topic, eventRecord.getPartition()); + recordsMap.computeIfAbsent(topicPartition, k -> new ArrayList<>()).add(consumerRecord); + } + } + + return createConsumerRecords(recordsMap); + } + + /** + * Creates ConsumerRecords with compatibility for both Kafka 3.x.x and 4.x.x. + * + * @param <K> Key type + * @param <V> Value type + * @param records Map of records by topic partition + * @return ConsumerRecords instance + */ + protected <K, V> ConsumerRecords<K, V> createConsumerRecords( + Map<TopicPartition, List<ConsumerRecord<K, V>>> records) { + try { + // Try to use the Kafka 4.x.x constructor with nextOffsets parameter + return new ConsumerRecords<>(records, Map.of()); + } catch (NoSuchMethodError e) { + // Fall back to Kafka 3.x.x constructor if 4.x.x is not available + return new ConsumerRecords<>(records); + } + } + + private <K, V> ConsumerRecord<K, V> convertToConsumerRecord( + String topic, + KafkaEvent.KafkaEventRecord eventRecord, + Class<K> keyType, + Class<V> valueType) { + + K key = deserializeField(eventRecord.getKey(), keyType, "key", extractSchemaRegistryType(eventRecord)); + V value = deserializeField(eventRecord.getValue(), valueType, "value", extractSchemaRegistryType(eventRecord)); + Headers headers = extractHeaders(eventRecord); + + return new ConsumerRecord<>( + topic, + eventRecord.getPartition(), + eventRecord.getOffset(), + eventRecord.getTimestamp(), + TimestampType.valueOf(eventRecord.getTimestampType()), + // We set these to NULL_SIZE since they are not relevant in the Lambda environment due to ESM + // pre-processing. + ConsumerRecord.NULL_SIZE, + ConsumerRecord.NULL_SIZE, + key, + value, + headers, + Optional.empty()); + } + + private <T> T deserializeField(String encodedData, Class<T> type, String fieldName, + SchemaRegistryType schemaRegistryType) { + if (encodedData == null) { + return null; + } + + try { + byte[] decodedBytes = Base64.getDecoder().decode(encodedData); + return deserialize(decodedBytes, type, schemaRegistryType); + } catch (Exception e) { + throw new RuntimeException("Failed to deserialize Kafka record " + fieldName + ".", e); + } + } + + private Headers extractHeaders(KafkaEvent.KafkaEventRecord eventRecord) { + Headers headers = new RecordHeaders(); + if (eventRecord.getHeaders() != null) { + for (Map<String, byte[]> headerMap : eventRecord.getHeaders()) { + for (Map.Entry<String, byte[]> header : headerMap.entrySet()) { + if (header.getValue() != null) { + headers.add(header.getKey(), header.getValue()); + } + } + } + } + + return headers; + } + + private String extractKeySchemaId(KafkaEvent.KafkaEventRecord eventRecord) { + if (eventRecord.getKeySchemaMetadata() != null) { + return eventRecord.getKeySchemaMetadata().getSchemaId(); + } + return null; + } + + private String extractValueSchemaId(KafkaEvent.KafkaEventRecord eventRecord) { + if (eventRecord.getValueSchemaMetadata() != null) { + return eventRecord.getValueSchemaMetadata().getSchemaId(); + } + return null; + } + + private SchemaRegistryType extractSchemaRegistryType(KafkaEvent.KafkaEventRecord eventRecord) { + // This method is used for both key and value, so we try to extract the schema id from both fields + String schemaId = extractValueSchemaId(eventRecord); + if (schemaId == null) { + schemaId = extractKeySchemaId(eventRecord); + } + + if (schemaId == null) { + return SchemaRegistryType.NONE; + } + + return schemaId.length() == GLUE_SCHEMA_ID_LENGTH ? SchemaRegistryType.GLUE : SchemaRegistryType.CONFLUENT; + } + + /** + * Template method to be implemented by subclasses for specific deserialization logic + * for complex types (non-primitives) and for specific Schema Registry type. + * + * @param <T> The type to deserialize to + * @param data The byte array to deserialize coming from the base64 decoded Kafka field + * @param type The class type to deserialize to + * @param schemaRegistryType Schema Registry type + * @return The deserialized object + * @throws IOException If deserialization fails + */ + protected abstract <T> T deserializeObject(byte[] data, Class<T> type, SchemaRegistryType schemaRegistryType) + throws IOException; + + /** + * Main deserialize method that handles primitive types and delegates to subclasses for complex types and + * for specific Schema Registry type. + * + * @param <T> The type to deserialize to + * @param data The byte array to deserialize + * @param type The class type to deserialize to + * @param schemaRegistryType Schema Registry type + * @return The deserialized object + * @throws IOException If deserialization fails + */ + private <T> T deserialize(byte[] data, Class<T> type, SchemaRegistryType schemaRegistryType) throws IOException { + // First try to deserialize as a primitive type + T result = deserializePrimitive(data, type); + if (result != null) { + return result; + } + + // Delegate to subclass for complex type deserialization + return deserializeObject(data, type, schemaRegistryType); + } + + /** + * Helper method for handling primitive types and String deserialization. + * + * @param <T> The type to deserialize to + * @param data The byte array to deserialize + * @param type The class type to deserialize to + * @return The deserialized primitive or String, or null if not a primitive or String + */ + @SuppressWarnings("unchecked") + private <T> T deserializePrimitive(byte[] data, Class<T> type) { + // Handle String type + if (type == String.class) { + return (T) new String(data, StandardCharsets.UTF_8); + } + + // Handle primitive types and their wrappers + String str = new String(data, StandardCharsets.UTF_8); + + if (type == Integer.class || type == int.class) { + return (T) Integer.valueOf(str); + } else if (type == Long.class || type == long.class) { + return (T) Long.valueOf(str); + } else if (type == Double.class || type == double.class) { + return (T) Double.valueOf(str); + } else if (type == Float.class || type == float.class) { + return (T) Float.valueOf(str); + } else if (type == Boolean.class || type == boolean.class) { + return (T) Boolean.valueOf(str); + } else if (type == Byte.class || type == byte.class) { + return (T) Byte.valueOf(str); + } else if (type == Short.class || type == short.class) { + return (T) Short.valueOf(str); + } else if (type == Character.class || type == char.class) { + if (!str.isEmpty()) { + return (T) Character.valueOf(str.charAt(0)); + } + throw new IllegalArgumentException("Cannot convert empty string to char"); + } + + return null; + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializer.java new file mode 100644 index 000000000..70e4affac --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import java.io.IOException; + +import org.apache.avro.io.DatumReader; +import org.apache.avro.io.Decoder; +import org.apache.avro.io.DecoderFactory; +import org.apache.avro.specific.SpecificDatumReader; +import org.apache.avro.specific.SpecificRecordBase; + +/** + * Deserializer for Kafka records using Avro format. + */ +public class KafkaAvroDeserializer extends AbstractKafkaDeserializer { + + @Override + protected <T> T deserializeObject(byte[] data, Class<T> type, SchemaRegistryType schemaRegistryType) + throws IOException { + // If no Avro generated class is passed we cannot deserialize using Avro + if (SpecificRecordBase.class.isAssignableFrom(type)) { + try { + DatumReader<T> datumReader = new SpecificDatumReader<>(type); + Decoder decoder = DecoderFactory.get().binaryDecoder(data, null); + + return datumReader.read(null, decoder); + } catch (Exception e) { + throw new IOException("Failed to deserialize Avro data.", e); + } + } else { + throw new IOException("Unsupported type for Avro deserialization: " + type.getName() + ". " + + "Avro deserialization requires a type of org.apache.avro.specific.SpecificRecord. " + + "Consider using an alternative Deserializer."); + } + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializer.java new file mode 100644 index 000000000..733fe5d7d --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * Deserializer for Kafka records using JSON format. + */ +public class KafkaJsonDeserializer extends AbstractKafkaDeserializer { + + @Override + protected <T> T deserializeObject(byte[] data, Class<T> type, SchemaRegistryType schemaRegistryType) + throws IOException { + String decodedStr = new String(data, StandardCharsets.UTF_8); + + return objectMapper.readValue(decodedStr, type); + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializer.java new file mode 100644 index 000000000..1a8085a61 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializer.java @@ -0,0 +1,110 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; + +import org.apache.kafka.common.utils.ByteUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.Message; +import com.google.protobuf.Parser; + +/** + * Deserializer for Kafka records using Protocol Buffers format. + * Supports both standard protobuf serialization and Confluent / Glue Schema Registry serialization using messages + * indices. + * + * For Confluent-serialized data, assumes the magic byte and schema ID have already been stripped + * by the Kafka ESM, leaving only the message index (if present) and protobuf data. + * + * @see {@link https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format} + */ +public class KafkaProtobufDeserializer extends AbstractKafkaDeserializer { + + private static final Logger LOGGER = LoggerFactory.getLogger(KafkaProtobufDeserializer.class); + private static final String PROTOBUF_PARSER_METHOD = "parser"; + + @Override + protected <T> T deserializeObject(byte[] data, Class<T> type, SchemaRegistryType schemaRegistryType) + throws IOException { + // If no Protobuf generated class is passed, we cannot deserialize using Protobuf + if (Message.class.isAssignableFrom(type)) { + try { + switch (schemaRegistryType) { + case GLUE: + return glueDeserializer(data, type); + case CONFLUENT: + return confluentDeserializer(data, type); + default: + return defaultDeserializer(data, type); + } + } catch (Exception e) { + throw new IOException("Failed to deserialize Protobuf data.", e); + } + } else { + throw new IOException("Unsupported type for Protobuf deserialization: " + type.getName() + ". " + + "Protobuf deserialization requires a type of com.google.protobuf.Message. " + + "Consider using an alternative Deserializer."); + } + } + + @SuppressWarnings("unchecked") + private <T> T defaultDeserializer(byte[] data, Class<T> type) throws IOException { + try { + LOGGER.debug("Using default Protobuf deserializer"); + Parser<Message> parser = (Parser<Message>) type.getMethod(PROTOBUF_PARSER_METHOD).invoke(null); + Message message = parser.parseFrom(data); + return type.cast(message); + } catch (Exception e) { + throw new IOException("Failed to deserialize Protobuf data.", e); + } + } + + @SuppressWarnings("unchecked") + private <T> T confluentDeserializer(byte[] data, Class<T> type) + throws IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { + LOGGER.debug("Using Confluent Deserializer"); + Parser<Message> parser = (Parser<Message>) type.getMethod(PROTOBUF_PARSER_METHOD).invoke(null); + ByteBuffer buffer = ByteBuffer.wrap(data); + int size = ByteUtils.readVarint(buffer); + + // Only if the size is greater than zero, continue reading varInt. + // https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format + if (size > 0) { + for (int i = 0; i < size; i++) { + ByteUtils.readVarint(buffer); + } + } + Message message = parser.parseFrom(buffer); + return type.cast(message); + } + + @SuppressWarnings("unchecked") + private <T> T glueDeserializer(byte[] data, Class<T> type) + throws IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { + LOGGER.debug("Using Glue Deserializer"); + CodedInputStream codedInputStream = CodedInputStream.newInstance(data); + Parser<Message> parser = (Parser<Message>) type.getMethod(PROTOBUF_PARSER_METHOD).invoke(null); + + // Seek one byte forward. Based on Glue Proto deserializer implementation + codedInputStream.readUInt32(); + + Message message = parser.parseFrom(codedInputStream); + return type.cast(message); + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/LambdaDefaultDeserializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/LambdaDefaultDeserializer.java new file mode 100644 index 000000000..a7ea15d2f --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/LambdaDefaultDeserializer.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory; + +/** + * Default deserializer for Kafka events proxying to Lambda default behavior. + * + * This deserializer uses the default Jackson ObjectMapper to deserialize the event from + * {@link com.amazonaws.services.lambda.runtime.serialization}. + */ +public class LambdaDefaultDeserializer implements PowertoolsDeserializer { + + @SuppressWarnings("unchecked") + @Override + public <T> T fromJson(InputStream input, Type type) { + // If the target type does not require conversion, simply return the value itself + if (type.equals(InputStream.class)) { + return (T) input; + } + + // If the target type is String, read the input stream as a String + if (type.equals(String.class)) { + try { + return (T) new String(input.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException("Failed to read input stream as String", e); + } + } + + return (T) JacksonFactory.getInstance().getSerializer(type).fromJson(input); + } + + @SuppressWarnings("unchecked") + @Override + public <T> T fromJson(String input, Type type) { + // If the target type does not require conversion, simply return the value itself + if (type.equals(String.class)) { + return (T) input; + } + + // If the target type is InputStream, read the input stream as a String + if (type.equals(InputStream.class)) { + return (T) input.getBytes(StandardCharsets.UTF_8); + } + + return (T) JacksonFactory.getInstance().getSerializer(type).fromJson(input); + } +} diff --git a/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/PowertoolsDeserializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/PowertoolsDeserializer.java new file mode 100644 index 000000000..1ac0ca0ba --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/PowertoolsDeserializer.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import java.io.InputStream; +import java.lang.reflect.Type; + +/** + * Interface for deserializers that can handle both String and InputStream inputs. + * + * Similar to {@link com.amazonaws.services.lambda.runtime.CustomPojoSerializer} but only for input deserialization. + */ +public interface PowertoolsDeserializer { + <T> T fromJson(InputStream input, Type type); + + <T> T fromJson(String input, Type type); +} diff --git a/powertools-kafka/src/main/resources/META-INF/services/com.amazonaws.services.lambda.runtime.CustomPojoSerializer b/powertools-kafka/src/main/resources/META-INF/services/com.amazonaws.services.lambda.runtime.CustomPojoSerializer new file mode 100644 index 000000000..abc84b035 --- /dev/null +++ b/powertools-kafka/src/main/resources/META-INF/services/com.amazonaws.services.lambda.runtime.CustomPojoSerializer @@ -0,0 +1 @@ +software.amazon.lambda.powertools.kafka.PowertoolsSerializer diff --git a/powertools-kafka/src/main/resources/classesloaded.txt b/powertools-kafka/src/main/resources/classesloaded.txt new file mode 100644 index 000000000..6b8c000bc --- /dev/null +++ b/powertools-kafka/src/main/resources/classesloaded.txt @@ -0,0 +1,6642 @@ +java.lang.Object +java.io.Serializable +java.lang.Comparable +java.lang.CharSequence +java.lang.constant.Constable +java.lang.constant.ConstantDesc +java.lang.String +java.lang.reflect.AnnotatedElement +java.lang.reflect.GenericDeclaration +java.lang.reflect.Type +java.lang.invoke.TypeDescriptor +java.lang.invoke.TypeDescriptor$OfField +java.lang.Class +java.lang.Cloneable +java.lang.ClassLoader +java.lang.System +java.lang.Throwable +java.lang.Error +java.lang.ThreadDeath +java.lang.Exception +java.lang.RuntimeException +java.lang.SecurityManager +java.security.ProtectionDomain +java.security.AccessControlContext +java.security.AccessController +java.security.SecureClassLoader +java.lang.ReflectiveOperationException +java.lang.ClassNotFoundException +java.lang.Record +java.lang.LinkageError +java.lang.NoClassDefFoundError +java.lang.ClassCastException +java.lang.ArrayStoreException +java.lang.VirtualMachineError +java.lang.InternalError +java.lang.OutOfMemoryError +java.lang.StackOverflowError +java.lang.IllegalMonitorStateException +java.lang.ref.Reference +java.lang.ref.SoftReference +java.lang.ref.WeakReference +java.lang.ref.FinalReference +java.lang.ref.PhantomReference +java.lang.ref.Finalizer +java.lang.Runnable +java.lang.Thread +java.lang.Thread$UncaughtExceptionHandler +java.lang.ThreadGroup +java.util.Dictionary +java.util.Map +java.util.Hashtable +java.util.Properties +java.lang.Module +java.lang.reflect.AccessibleObject +java.lang.reflect.Member +java.lang.reflect.Field +java.lang.reflect.Parameter +java.lang.reflect.Executable +java.lang.reflect.Method +java.lang.reflect.Constructor +jdk.internal.reflect.MagicAccessorImpl +jdk.internal.reflect.MethodAccessor +jdk.internal.reflect.MethodAccessorImpl +jdk.internal.reflect.ConstructorAccessor +jdk.internal.reflect.ConstructorAccessorImpl +jdk.internal.reflect.DelegatingClassLoader +jdk.internal.reflect.ConstantPool +jdk.internal.reflect.FieldAccessor +jdk.internal.reflect.FieldAccessorImpl +jdk.internal.reflect.UnsafeFieldAccessorImpl +jdk.internal.reflect.UnsafeStaticFieldAccessorImpl +java.lang.annotation.Annotation +jdk.internal.reflect.CallerSensitive +jdk.internal.reflect.NativeConstructorAccessorImpl +java.lang.invoke.MethodHandle +java.lang.invoke.DirectMethodHandle +java.lang.invoke.VarHandle +java.lang.invoke.MemberName +java.lang.invoke.ResolvedMethodName +java.lang.invoke.MethodHandleNatives +java.lang.invoke.LambdaForm +java.lang.invoke.TypeDescriptor$OfMethod +java.lang.invoke.MethodType +java.lang.BootstrapMethodError +java.lang.invoke.CallSite +jdk.internal.invoke.NativeEntryPoint +java.lang.invoke.MethodHandleNatives$CallSiteContext +java.lang.invoke.ConstantCallSite +java.lang.invoke.MutableCallSite +java.lang.invoke.VolatileCallSite +java.lang.AssertionStatusDirectives +java.lang.Appendable +java.lang.AbstractStringBuilder +java.lang.StringBuffer +java.lang.StringBuilder +jdk.internal.misc.UnsafeConstants +jdk.internal.misc.Unsafe +jdk.internal.module.Modules +java.lang.AutoCloseable +java.io.Closeable +java.io.InputStream +java.io.ByteArrayInputStream +java.net.URL +java.util.jar.Manifest +jdk.internal.loader.BuiltinClassLoader +jdk.internal.loader.ClassLoaders +jdk.internal.loader.ClassLoaders$AppClassLoader +jdk.internal.loader.ClassLoaders$PlatformClassLoader +java.security.CodeSource +java.util.AbstractMap +java.util.concurrent.ConcurrentMap +java.util.concurrent.ConcurrentHashMap +java.lang.Iterable +java.util.Collection +java.util.AbstractCollection +java.util.List +java.util.AbstractList +java.util.RandomAccess +java.util.ArrayList +java.lang.StackTraceElement +java.nio.Buffer +java.lang.StackWalker +java.lang.StackStreamFactory$AbstractStackWalker +java.lang.StackWalker$StackFrame +java.lang.StackFrameInfo +java.lang.LiveStackFrame +java.lang.LiveStackFrameInfo +java.util.concurrent.locks.AbstractOwnableSynchronizer +java.lang.Boolean +java.lang.Character +java.lang.Number +java.lang.Float +java.lang.Double +java.lang.Byte +java.lang.Short +java.lang.Integer +java.lang.Long +java.util.Iterator +java.lang.reflect.RecordComponent +jdk.internal.vm.vector.VectorSupport +jdk.internal.vm.vector.VectorSupport$VectorPayload +jdk.internal.vm.vector.VectorSupport$Vector +jdk.internal.vm.vector.VectorSupport$VectorMask +jdk.internal.vm.vector.VectorSupport$VectorShuffle +java.lang.Integer$IntegerCache +java.lang.Long$LongCache +java.lang.Byte$ByteCache +java.lang.Short$ShortCache +java.lang.Character$CharacterCache +java.util.jar.Attributes$Name +java.util.ImmutableCollections$AbstractImmutableMap +java.util.ImmutableCollections$MapN +sun.util.locale.BaseLocale +jdk.internal.module.ArchivedModuleGraph +java.lang.module.ModuleFinder +jdk.internal.module.SystemModuleFinders$SystemModuleFinder +java.util.ImmutableCollections$AbstractImmutableCollection +java.util.Set +java.util.ImmutableCollections$AbstractImmutableSet +java.util.ImmutableCollections$SetN +java.lang.module.ModuleReference +jdk.internal.module.ModuleReferenceImpl +java.lang.module.ModuleDescriptor +java.lang.module.ModuleDescriptor$Version +java.util.ImmutableCollections$Set12 +java.lang.module.ModuleDescriptor$Requires +java.lang.Enum +java.lang.module.ModuleDescriptor$Requires$Modifier +java.lang.module.ModuleDescriptor$Exports +java.net.URI +java.util.function.Supplier +jdk.internal.module.SystemModuleFinders$2 +jdk.internal.module.ModuleHashes$HashSupplier +jdk.internal.module.SystemModuleFinders$3 +java.lang.module.ModuleDescriptor$Provides +java.util.ImmutableCollections$AbstractImmutableList +java.util.ImmutableCollections$List12 +java.util.ImmutableCollections$ListN +java.lang.module.ModuleDescriptor$Opens +jdk.internal.module.ModuleTarget +jdk.internal.module.ModuleHashes +java.util.Collections$UnmodifiableMap +java.util.HashMap +java.util.Map$Entry +java.util.HashMap$Node +java.lang.module.Configuration +java.lang.module.ResolvedModule +java.util.function.Function +jdk.internal.module.ModuleLoaderMap$Mapper +java.util.ImmutableCollections +java.lang.ModuleLayer +jdk.internal.math.FDBigInteger +java.lang.NullPointerException +java.lang.ArithmeticException +java.io.ObjectStreamField +java.util.Comparator +java.lang.String$CaseInsensitiveComparator +java.lang.Module$ArchivedData +jdk.internal.misc.CDS +java.util.Objects +jdk.internal.access.JavaLangReflectAccess +java.lang.reflect.ReflectAccess +jdk.internal.access.SharedSecrets +java.lang.invoke.MethodHandles +java.lang.invoke.MemberName$Factory +java.security.Guard +java.security.Permission +java.security.BasicPermission +java.lang.reflect.ReflectPermission +java.lang.StringLatin1 +java.lang.invoke.MethodHandles$Lookup +jdk.internal.reflect.Reflection +java.lang.Math +java.util.AbstractSet +java.util.ImmutableCollections$MapN$1 +java.util.ImmutableCollections$MapN$MapNIterator +java.util.KeyValueHolder +java.util.LinkedHashMap$Entry +java.util.HashMap$TreeNode +java.lang.Runtime +java.util.concurrent.locks.Lock +java.util.concurrent.locks.ReentrantLock +java.util.concurrent.ConcurrentHashMap$Segment +java.util.concurrent.ConcurrentHashMap$CounterCell +java.util.concurrent.ConcurrentHashMap$Node +java.util.concurrent.locks.LockSupport +java.util.concurrent.ConcurrentHashMap$ReservationNode +java.security.PrivilegedAction +jdk.internal.reflect.ReflectionFactory$GetReflectionFactoryAction +jdk.internal.reflect.ReflectionFactory +java.lang.ref.Reference$ReferenceHandler +jdk.internal.ref.Cleaner +java.lang.ref.ReferenceQueue +java.lang.ref.ReferenceQueue$Null +java.lang.ref.ReferenceQueue$Lock +jdk.internal.access.JavaLangRefAccess +java.lang.ref.Reference$1 +java.lang.ref.Finalizer$FinalizerThread +jdk.internal.access.JavaLangAccess +java.lang.System$2 +jdk.internal.misc.VM +jdk.internal.util.SystemProps +jdk.internal.util.SystemProps$Raw +java.lang.StringConcatHelper +java.lang.VersionProps +java.util.Arrays +java.lang.CharacterData +java.lang.CharacterDataLatin1 +java.util.HashMap$EntrySet +java.util.HashMap$HashIterator +java.util.HashMap$EntryIterator +jdk.internal.util.StaticProperty +java.io.FileInputStream +java.io.FileDescriptor +jdk.internal.access.JavaIOFileDescriptorAccess +java.io.FileDescriptor$1 +java.io.Flushable +java.io.OutputStream +java.io.FileOutputStream +java.io.FilterInputStream +java.io.BufferedInputStream +java.io.FilterOutputStream +java.io.PrintStream +java.io.BufferedOutputStream +java.io.Writer +java.io.OutputStreamWriter +java.nio.charset.Charset +java.nio.charset.spi.CharsetProvider +sun.nio.cs.StandardCharsets +java.lang.ThreadLocal +java.util.concurrent.atomic.AtomicInteger +sun.security.action.GetPropertyAction +sun.nio.cs.HistoricallyNamedCharset +sun.nio.cs.Unicode +sun.nio.cs.UTF_8 +sun.nio.cs.StreamEncoder +java.nio.charset.CharsetEncoder +sun.nio.cs.UTF_8$Encoder +java.nio.charset.CodingErrorAction +java.nio.ByteBuffer +jdk.internal.misc.ScopedMemoryAccess +jdk.internal.access.JavaNioAccess +java.nio.Buffer$1 +java.nio.HeapByteBuffer +java.nio.ByteOrder +java.io.BufferedWriter +java.lang.Terminator +jdk.internal.misc.Signal$Handler +java.lang.Terminator$1 +jdk.internal.misc.Signal +java.util.Hashtable$Entry +jdk.internal.misc.Signal$NativeHandler +jdk.internal.misc.OSEnvironment +java.util.Collections +java.util.Collections$EmptySet +java.util.Collections$EmptyList +java.util.Collections$EmptyMap +java.lang.IllegalArgumentException +java.lang.invoke.MethodHandleStatics +jdk.internal.module.ModuleBootstrap +sun.invoke.util.VerifyAccess +java.lang.reflect.Modifier +jdk.internal.access.JavaLangModuleAccess +java.lang.module.ModuleDescriptor$1 +java.io.File +java.io.DefaultFileSystem +java.io.FileSystem +java.io.UnixFileSystem +jdk.internal.util.ArraysSupport +jdk.internal.module.ModulePatcher +jdk.internal.module.ModuleBootstrap$Counters +jdk.internal.module.ArchivedBootLayer +jdk.internal.access.JavaNetUriAccess +java.net.URI$1 +jdk.internal.loader.ArchivedClassLoaders +jdk.internal.loader.ClassLoaders$BootClassLoader +java.security.cert.Certificate +java.lang.ClassLoader$ParallelLoaders +java.util.WeakHashMap +java.util.WeakHashMap$Entry +java.util.Collections$SetFromMap +java.util.WeakHashMap$KeySet +jdk.internal.access.JavaSecurityAccess +java.security.ProtectionDomain$JavaSecurityAccessImpl +java.security.ProtectionDomain$Key +java.security.Principal +jdk.internal.loader.NativeLibraries +jdk.internal.loader.ClassLoaderHelper +java.util.HashSet +java.util.Queue +java.util.Deque +java.util.ArrayDeque +jdk.internal.loader.URLClassPath +java.net.URLStreamHandlerFactory +java.net.URL$DefaultFactory +jdk.internal.access.JavaNetURLAccess +java.net.URL$3 +java.io.File$PathStatus +sun.net.www.ParseUtil +java.util.HexFormat +java.net.URLStreamHandler +sun.net.www.protocol.file.Handler +sun.net.util.IPAddressUtil +jdk.internal.util.Preconditions +sun.net.www.protocol.jar.Handler +jdk.internal.module.ServicesCatalog +jdk.internal.loader.AbstractClassLoaderValue +jdk.internal.loader.ClassLoaderValue +java.util.Optional +jdk.internal.loader.BootLoader +jdk.internal.loader.BuiltinClassLoader$LoadedModule +java.util.ImmutableCollections$SetN$SetNIterator +java.util.ImmutableCollections$Set12$1 +java.util.ListIterator +java.util.ImmutableCollections$ListItr +jdk.internal.module.ModuleLoaderMap +jdk.internal.loader.AbstractClassLoaderValue$Memoizer +jdk.internal.module.ServicesCatalog$ServiceProvider +java.util.concurrent.CopyOnWriteArrayList +java.util.HashMap$KeySet +java.util.HashMap$KeyIterator +java.lang.ModuleLayer$Controller +java.lang.invoke.LambdaMetafactory +java.lang.invoke.MethodType$ConcurrentWeakInternSet +java.lang.Void +java.lang.invoke.MethodTypeForm +java.lang.invoke.MethodType$ConcurrentWeakInternSet$WeakEntry +sun.invoke.util.Wrapper +sun.invoke.util.Wrapper$Format +java.lang.invoke.LambdaForm$NamedFunction +java.lang.invoke.DirectMethodHandle$Holder +sun.invoke.util.ValueConversions +java.lang.invoke.MethodHandleImpl +java.lang.invoke.Invokers +java.lang.invoke.LambdaForm$Kind +java.lang.NoSuchMethodException +java.lang.invoke.LambdaForm$BasicType +java.lang.reflect.Array +java.lang.invoke.LambdaForm$Name +java.lang.invoke.LambdaForm$Holder +java.lang.invoke.InvokerBytecodeGenerator +java.lang.invoke.InvokerBytecodeGenerator$2 +java.lang.invoke.MethodHandleImpl$Intrinsic +java.lang.invoke.BootstrapMethodInvoker +java.lang.invoke.VarHandle$AccessMode +java.lang.invoke.VarHandle$AccessType +java.lang.invoke.Invokers$Holder +jdk.internal.access.JavaLangInvokeAccess +java.lang.invoke.MethodHandleImpl$1 +java.lang.invoke.AbstractValidatingLambdaMetafactory +java.lang.invoke.InnerClassLambdaMetafactory +jdk.internal.org.objectweb.asm.Type +sun.security.action.GetBooleanAction +jdk.internal.org.objectweb.asm.Handle +sun.invoke.util.BytecodeDescriptor +jdk.internal.org.objectweb.asm.ConstantDynamic +java.lang.invoke.MethodHandleInfo +java.lang.invoke.InfoFromMemberName +jdk.internal.org.objectweb.asm.ClassVisitor +jdk.internal.org.objectweb.asm.ClassWriter +jdk.internal.org.objectweb.asm.SymbolTable +jdk.internal.org.objectweb.asm.Symbol +jdk.internal.org.objectweb.asm.SymbolTable$Entry +jdk.internal.org.objectweb.asm.ByteVector +java.lang.invoke.LambdaProxyClassArchive +jdk.internal.org.objectweb.asm.MethodVisitor +jdk.internal.org.objectweb.asm.MethodWriter +jdk.internal.org.objectweb.asm.Label +java.lang.invoke.TypeConvertingMethodAdapter +java.lang.invoke.InnerClassLambdaMetafactory$ForwardingMethodGenerator +jdk.internal.org.objectweb.asm.Handler +jdk.internal.org.objectweb.asm.Attribute +jdk.internal.org.objectweb.asm.AnnotationVisitor +jdk.internal.org.objectweb.asm.AnnotationWriter +java.lang.invoke.MethodHandles$Lookup$ClassOption +java.lang.invoke.MethodHandles$Lookup$ClassFile +jdk.internal.org.objectweb.asm.ClassReader +java.lang.StringUTF16 +java.lang.invoke.MethodHandles$Lookup$ClassDefiner +jdk.internal.module.ModuleBootstrap$$Lambda$1/0x00007f4204040850 +java.lang.invoke.InnerClassLambdaMetafactory$1 +java.lang.Class$ReflectionData +java.lang.Class$Atomic +jdk.internal.reflect.DelegatingConstructorAccessorImpl +java.lang.invoke.BoundMethodHandle +java.lang.invoke.ClassSpecializer +java.lang.invoke.BoundMethodHandle$Specializer +java.lang.invoke.ClassSpecializer$1 +java.lang.invoke.ClassSpecializer$SpeciesData +java.lang.invoke.BoundMethodHandle$SpeciesData +java.lang.invoke.ClassSpecializer$Factory +java.lang.invoke.BoundMethodHandle$Specializer$Factory +java.lang.invoke.SimpleMethodHandle +java.lang.NoSuchFieldException +java.lang.invoke.BoundMethodHandle$Species_L +sun.invoke.util.VerifyType +sun.invoke.empty.Empty +java.lang.invoke.DirectMethodHandle$2 +java.lang.invoke.DirectMethodHandle$Accessor +java.lang.invoke.DelegatingMethodHandle +java.lang.invoke.MethodHandleImpl$IntrinsicMethodHandle +java.lang.invoke.DelegatingMethodHandle$Holder +sun.invoke.util.Wrapper$1 +java.lang.invoke.LambdaFormEditor +java.lang.invoke.LambdaFormEditor$TransformKey +java.lang.invoke.LambdaFormBuffer +java.lang.invoke.LambdaFormEditor$Transform +jdk.internal.org.objectweb.asm.Frame +java.lang.invoke.InvokerBytecodeGenerator$ClassData +java.util.ArrayList$Itr +jdk.internal.org.objectweb.asm.FieldVisitor +jdk.internal.org.objectweb.asm.FieldWriter +java.lang.invoke.LambdaForm$MH/0x00007f4204000400 +jdk.internal.ref.CleanerFactory +java.util.concurrent.ThreadFactory +jdk.internal.ref.CleanerFactory$1 +java.lang.ref.Cleaner +java.lang.ref.Cleaner$1 +jdk.internal.ref.CleanerImpl +java.lang.ref.Cleaner$Cleanable +jdk.internal.ref.PhantomCleanable +jdk.internal.ref.CleanerImpl$PhantomCleanableRef +jdk.internal.ref.CleanerImpl$CleanerCleanable +jdk.internal.misc.InnocuousThread +java.util.ArrayList$SubList +java.lang.Module$ReflectionData +java.lang.WeakPairMap +java.lang.WeakPairMap$Pair +java.lang.WeakPairMap$Pair$Lookup +java.util.function.BiFunction +java.lang.Module$$Lambda$2/0x00007f4204040a90 +java.lang.WeakPairMap$WeakRefPeer +java.lang.WeakPairMap$Pair$Weak +java.lang.WeakPairMap$Pair$Weak$1 +java.lang.WeakPairMap$$Lambda$3/0x00007f42040413e8 +java.lang.invoke.DirectMethodHandle$Constructor +java.lang.invoke.StringConcatFactory +java.lang.invoke.StringConcatFactory$1 +java.lang.invoke.StringConcatFactory$2 +java.lang.invoke.StringConcatFactory$3 +sun.launcher.LauncherHelper +java.lang.StringCoding +java.util.zip.ZipConstants +java.util.zip.ZipFile +java.util.jar.JarFile +jdk.internal.access.JavaUtilZipFileAccess +java.util.zip.ZipFile$1 +jdk.internal.access.JavaUtilJarAccess +java.util.jar.JavaUtilJarAccessImpl +java.lang.Runtime$Version +java.util.zip.ZipFile$CleanableResource +java.util.zip.ZipCoder +java.util.zip.ZipCoder$UTF8ZipCoder +java.util.zip.ZipFile$Source +sun.nio.fs.DefaultFileSystemProvider +java.nio.file.spi.FileSystemProvider +sun.nio.fs.AbstractFileSystemProvider +sun.nio.fs.UnixFileSystemProvider +sun.nio.fs.LinuxFileSystemProvider +java.nio.file.OpenOption +java.nio.file.StandardOpenOption +java.nio.file.FileSystem +sun.nio.fs.UnixFileSystem +sun.nio.fs.LinuxFileSystem +java.nio.file.Watchable +java.nio.file.Path +sun.nio.fs.UnixPath +sun.nio.fs.Util +sun.nio.fs.UnixNativeDispatcher +jdk.internal.loader.NativeLibraries$LibraryPaths +jdk.internal.loader.NativeLibraries$1 +java.util.ArrayDeque$DeqIterator +jdk.internal.loader.NativeLibrary +jdk.internal.loader.NativeLibraries$NativeLibraryImpl +java.util.concurrent.ConcurrentHashMap$CollectionView +java.util.concurrent.ConcurrentHashMap$ValuesView +java.util.concurrent.ConcurrentHashMap$Traverser +java.util.concurrent.ConcurrentHashMap$BaseIterator +java.util.Enumeration +java.util.concurrent.ConcurrentHashMap$ValueIterator +java.nio.file.attribute.BasicFileAttributes +java.nio.file.attribute.PosixFileAttributes +sun.nio.fs.UnixFileAttributes +sun.nio.fs.UnixFileStoreAttributes +sun.nio.fs.UnixMountEntry +java.util.zip.ZipFile$Source$Key +java.nio.file.CopyOption +java.nio.file.LinkOption +java.nio.file.Files +java.nio.file.attribute.DosFileAttributes +java.nio.file.attribute.AttributeView +java.nio.file.attribute.FileAttributeView +java.nio.file.attribute.BasicFileAttributeView +java.nio.file.attribute.DosFileAttributeView +java.nio.file.attribute.UserDefinedFileAttributeView +sun.nio.fs.UnixFileAttributeViews +sun.nio.fs.DynamicFileAttributeView +sun.nio.fs.AbstractBasicFileAttributeView +sun.nio.fs.UnixFileAttributeViews$Basic +sun.nio.fs.NativeBuffers +jdk.internal.misc.TerminatingThreadLocal +sun.nio.fs.NativeBuffers$1 +jdk.internal.misc.TerminatingThreadLocal$1 +java.lang.ThreadLocal$ThreadLocalMap +java.lang.ThreadLocal$ThreadLocalMap$Entry +java.util.IdentityHashMap +java.util.IdentityHashMap$KeySet +sun.nio.fs.NativeBuffer +sun.nio.fs.NativeBuffer$Deallocator +sun.nio.fs.UnixFileAttributes$UnixAsBasicFileAttributes +java.io.DataOutput +java.io.DataInput +java.io.RandomAccessFile +jdk.internal.access.JavaIORandomAccessFileAccess +java.io.RandomAccessFile$2 +java.io.FileCleanable +java.util.zip.ZipFile$Source$End +java.util.zip.ZipUtils +java.util.concurrent.TimeUnit +java.nio.file.attribute.FileTime +jdk.internal.perf.PerfCounter +jdk.internal.perf.Perf$GetPerfAction +jdk.internal.perf.Perf +jdk.internal.perf.PerfCounter$CoreCounters +sun.nio.ch.DirectBuffer +java.nio.MappedByteBuffer +java.nio.DirectByteBuffer +java.nio.Bits +java.util.concurrent.atomic.AtomicLong +jdk.internal.misc.VM$BufferPool +java.nio.Bits$1 +java.nio.LongBuffer +java.nio.DirectLongBufferU +java.util.zip.ZipEntry +java.util.jar.JarEntry +java.util.jar.JarFile$JarFileEntry +java.util.zip.ZipFile$ZipFileInputStream +java.util.zip.InflaterInputStream +java.util.zip.ZipFile$ZipFileInflaterInputStream +java.util.zip.Inflater +java.util.zip.Inflater$InflaterZStreamRef +java.util.zip.ZipFile$InflaterCleanupAction +sun.security.util.SignatureFileVerifier +sun.security.util.Debug +java.util.Locale +sun.util.locale.LocaleUtils +sun.security.action.GetIntegerAction +java.util.jar.JarVerifier +java.security.CodeSigner +java.io.ByteArrayOutputStream +java.util.jar.Attributes +java.util.LinkedHashMap +java.util.jar.Manifest$FastInputStream +java.io.RandomAccessFile$1 +sun.net.util.URLUtil +java.security.PrivilegedExceptionAction +jdk.internal.loader.URLClassPath$3 +jdk.internal.loader.URLClassPath$Loader +jdk.internal.loader.URLClassPath$JarLoader +jdk.internal.loader.URLClassPath$JarLoader$1 +jdk.internal.loader.FileURLMapper +jdk.internal.util.jar.JarIndex +java.util.StringTokenizer +jdk.internal.loader.Resource +jdk.internal.loader.URLClassPath$JarLoader$2 +java.lang.NamedPackage +java.lang.Package +java.lang.Package$VersionInfo +sun.nio.ByteBuffered +java.util.zip.Checksum +java.util.zip.CRC32 +java.util.zip.Checksum$1 +java.security.SecureClassLoader$CodeSourceKey +java.security.SecureClassLoader$1 +java.security.PermissionCollection +sun.security.util.LazyCodeSourcePermissionCollection +java.security.Permissions +java.lang.RuntimePermission +java.security.BasicPermissionCollection +java.security.AllPermission +java.security.UnresolvedPermission +java.security.SecureClassLoader$DebugHolder +org.apache.maven.surefire.booter.ForkedBooter +org.apache.maven.surefire.api.fork.ForkNodeArguments +org.apache.maven.plugin.surefire.log.api.ConsoleLogger +java.lang.SecurityException +java.security.AccessControlException +java.io.IOException +org.apache.maven.surefire.api.provider.CommandListener +org.apache.maven.surefire.api.report.ReporterFactory +java.util.concurrent.ConcurrentHashMap$ForwardingNode +org.apache.maven.surefire.api.provider.CommandChainReader +java.lang.InterruptedException +java.util.concurrent.Executor +java.util.concurrent.ExecutorService +java.util.concurrent.ScheduledExecutorService +java.lang.PublicMethods$MethodList +java.lang.PublicMethods$Key +java.util.concurrent.Semaphore +java.util.concurrent.locks.AbstractQueuedSynchronizer +java.util.concurrent.Semaphore$Sync +java.util.concurrent.Semaphore$NonfairSync +org.apache.maven.surefire.booter.BooterDeserializer +org.apache.maven.surefire.booter.SystemPropertyManager +java.util.Properties$LineReader +java.util.Properties$EntrySet +java.util.concurrent.ConcurrentHashMap$EntrySetView +java.util.Collections$SynchronizedCollection +java.util.Collections$SynchronizedSet +java.util.concurrent.ConcurrentHashMap$EntryIterator +java.util.concurrent.ConcurrentHashMap$MapEntry +java.util.Collections$UnmodifiableCollection +java.util.Collections$UnmodifiableSet +java.util.Collections$UnmodifiableCollection$1 +org.apache.maven.surefire.booter.KeyValueSource +org.apache.maven.surefire.booter.PropertiesWrapper +java.lang.IllegalStateException +java.io.FileInputStream$1 +org.apache.maven.surefire.booter.TypeEncodedValue +org.apache.maven.surefire.api.testset.DirectoryScannerParameters +org.apache.maven.surefire.api.util.RunOrder +org.apache.maven.surefire.api.testset.RunOrderParameters +org.apache.maven.surefire.api.testset.TestArtifactInfo +org.apache.maven.surefire.api.testset.TestRequest +org.apache.maven.surefire.api.testset.TestFilter +org.apache.maven.surefire.api.testset.GenericTestPattern +org.apache.maven.surefire.api.testset.TestListResolver +java.util.Collections$SingletonSet +org.apache.maven.surefire.api.testset.IncludedExcludedPatterns +java.util.LinkedHashSet +java.util.Collections$1 +org.apache.maven.surefire.shared.utils.StringUtils +java.lang.IndexOutOfBoundsException +java.lang.StringIndexOutOfBoundsException +org.apache.maven.surefire.api.testset.ResolvedTest +org.apache.maven.surefire.api.testset.ResolvedTest$Type +org.apache.maven.surefire.api.testset.ResolvedTest$ClassMatcher +org.apache.maven.surefire.api.testset.ResolvedTest$MethodMatcher +org.apache.maven.surefire.api.report.ReporterConfiguration +org.apache.maven.surefire.api.booter.Shutdown +java.lang.Class$3 +jdk.internal.reflect.NativeMethodAccessorImpl +jdk.internal.reflect.DelegatingMethodAccessorImpl +org.apache.maven.surefire.booter.ProviderConfiguration +org.apache.maven.surefire.api.cli.CommandLineOption +org.apache.maven.surefire.api.booter.DumpErrorSingleton +org.apache.maven.surefire.api.util.internal.DumpFileUtils +java.lang.ProcessEnvironment +java.lang.ProcessEnvironment$ExternalData +java.lang.ProcessEnvironment$Variable +java.lang.ProcessEnvironment$Value +java.lang.ProcessEnvironment$StringEnvironment +java.lang.management.ManagementFactory +java.lang.IncompatibleClassChangeError +java.lang.NoSuchMethodError +java.lang.invoke.LambdaForm$DMH/0x00007f4204006000 +java.lang.management.ManagementFactory$$Lambda$4/0x00007f42040451f8 +java.lang.management.PlatformManagedObject +java.lang.management.RuntimeMXBean +java.lang.management.ManagementFactory$PlatformMBeanFinder +java.lang.management.ManagementFactory$PlatformMBeanFinder$1 +java.io.FilePermission +jdk.internal.access.JavaIOFilePermissionAccess +java.io.FilePermission$1 +sun.security.util.FilePermCompat +sun.security.util.SecurityProperties +java.security.Security +jdk.internal.access.JavaSecuritySystemConfiguratorAccess +java.security.Security$1 +java.security.Security$2 +java.security.SystemConfigurator +java.security.SystemConfigurator$1 +sun.security.util.PropertyExpander +java.net.URLConnection +sun.net.www.URLConnection +sun.net.www.protocol.file.FileURLConnection +sun.net.www.MessageHeader +sun.net.ProgressMonitor +sun.net.ProgressMeteringPolicy +sun.net.DefaultProgressMeteringPolicy +jdk.internal.access.JavaSecurityPropertiesAccess +java.security.Security$3 +sun.management.spi.PlatformMBeanProvider +java.util.ServiceLoader +java.util.ServiceLoader$ModuleServicesLookupIterator +java.util.ServiceLoader$LazyClassPathLookupIterator +java.util.ServiceLoader$2 +java.util.ServiceLoader$3 +java.util.concurrent.CopyOnWriteArrayList$COWIterator +com.sun.management.internal.PlatformMBeanProviderImpl +java.util.ServiceLoader$1 +java.util.ServiceLoader$Provider +java.util.ServiceLoader$ProviderImpl +com.sun.management.internal.PlatformMBeanProviderImpl$$Lambda$5/0x00007f42040468a0 +sun.management.spi.PlatformMBeanProvider$PlatformComponent +com.sun.management.internal.PlatformMBeanProviderImpl$1 +java.util.stream.BaseStream +java.util.stream.Stream +java.util.Spliterators +java.util.Spliterators$EmptySpliterator +java.util.Spliterator +java.util.Spliterators$EmptySpliterator$OfRef +java.util.Spliterator$OfPrimitive +java.util.Spliterator$OfInt +java.util.Spliterators$EmptySpliterator$OfInt +java.util.Spliterator$OfLong +java.util.Spliterators$EmptySpliterator$OfLong +java.util.Spliterator$OfDouble +java.util.Spliterators$EmptySpliterator$OfDouble +java.util.Spliterators$ArraySpliterator +java.util.stream.StreamSupport +java.util.stream.PipelineHelper +java.util.stream.AbstractPipeline +java.util.stream.ReferencePipeline +java.util.stream.ReferencePipeline$Head +java.util.stream.StreamOpFlag +java.util.stream.StreamOpFlag$Type +java.util.stream.StreamOpFlag$MaskBuilder +java.util.EnumMap +java.util.EnumMap$1 +sun.reflect.annotation.AnnotationParser +java.util.stream.Collectors +java.util.stream.Collector$Characteristics +java.util.EnumSet +java.util.RegularEnumSet +java.util.stream.Collector +java.util.stream.Collectors$CollectorImpl +java.util.stream.Collectors$$Lambda$30/0x800000041 +java.util.function.BiConsumer +java.lang.invoke.DirectMethodHandle$Interface +java.util.stream.Collectors$$Lambda$22/0x800000035 +java.util.function.BinaryOperator +java.util.stream.Collectors$$Lambda$25/0x80000003c +java.util.stream.Collectors$$Lambda$27/0x80000003e +java.util.stream.ReduceOps +java.util.stream.TerminalOp +java.util.stream.ReduceOps$ReduceOp +java.util.stream.ReduceOps$3 +java.util.stream.StreamShape +java.util.stream.ReduceOps$Box +java.util.function.Consumer +java.util.stream.Sink +java.util.stream.TerminalSink +java.util.stream.ReduceOps$AccumulatingSink +java.util.stream.ReduceOps$3ReducingSink +com.sun.management.internal.PlatformMBeanProviderImpl$2 +com.sun.management.internal.PlatformMBeanProviderImpl$3 +com.sun.management.internal.PlatformMBeanProviderImpl$4 +javax.management.DynamicMBean +com.sun.management.DiagnosticCommandMBean +javax.management.NotificationBroadcaster +javax.management.NotificationEmitter +sun.management.NotificationEmitterSupport +com.sun.management.internal.DiagnosticCommandImpl +sun.management.ManagementFactoryHelper +sun.management.VMManagement +sun.management.VMManagementImpl +com.sun.management.internal.PlatformMBeanProviderImpl$5 +java.util.Collections$UnmodifiableList +java.util.Collections$UnmodifiableRandomAccessList +jdk.management.jfr.internal.FlightRecorderMXBeanProvider +java.util.concurrent.Callable +java.util.Collections$EmptyEnumeration +java.lang.management.DefaultPlatformMBeanProvider +java.lang.management.DefaultPlatformMBeanProvider$1 +java.lang.management.DefaultPlatformMBeanProvider$2 +java.lang.management.DefaultPlatformMBeanProvider$3 +java.lang.management.DefaultPlatformMBeanProvider$4 +java.lang.management.DefaultPlatformMBeanProvider$5 +java.lang.management.DefaultPlatformMBeanProvider$6 +java.lang.management.DefaultPlatformMBeanProvider$7 +java.lang.management.DefaultPlatformMBeanProvider$8 +sun.management.ManagementFactoryHelper$LoggingMXBeanAccess +sun.management.ManagementFactoryHelper$LoggingMXBeanAccess$1 +java.util.logging.LogManager +java.lang.management.DefaultPlatformMBeanProvider$9 +java.lang.management.DefaultPlatformMBeanProvider$10 +java.lang.management.DefaultPlatformMBeanProvider$11 +jdk.management.jfr.FlightRecorderMXBean +jdk.management.jfr.internal.FlightRecorderMXBeanProvider$SingleMBeanComponent +java.util.Collections$SingletonList +java.util.HashMap$Values +java.util.HashMap$HashMapSpliterator +java.util.HashMap$ValueSpliterator +java.util.function.Predicate +java.lang.management.ManagementFactory$PlatformMBeanFinder$$Lambda$10/0x00007f420404c288 +java.util.stream.ReferencePipeline$StatelessOp +java.util.stream.ReferencePipeline$2 +java.lang.management.ManagementFactory$PlatformMBeanFinder$$Lambda$11/0x00007f420404c4e0 +java.util.stream.ReduceOps$2 +java.util.stream.ReduceOps$2ReducingSink +java.util.stream.Sink$ChainedReference +java.util.stream.ReferencePipeline$2$1 +sun.management.RuntimeImpl +java.util.Collections$SingletonMap +java.util.Collections$2 +java.lang.invoke.LambdaForm$DMH/0x00007f4204006400 +sun.management.spi.PlatformMBeanProvider$PlatformComponent$$Lambda$12/0x00007f420404d2d0 +sun.management.spi.PlatformMBeanProvider$PlatformComponent$$Lambda$13/0x00007f420404d528 +java.util.stream.ReferencePipeline$3 +java.util.stream.Collectors$$Lambda$14/0x00007f420404d770 +java.util.stream.Collectors$$Lambda$15/0x00007f420404d990 +java.util.stream.Collectors$$Lambda$16/0x00007f420404dbc0 +java.util.stream.ReferencePipeline$3$1 +sun.management.Util +java.lang.management.ManagementPermission +java.util.Arrays$ArrayList +java.util.Arrays$ArrayItr +org.apache.maven.surefire.booter.ClassLoaderConfiguration +org.apache.maven.surefire.booter.AbstractPathConfiguration +org.apache.maven.surefire.booter.ClasspathConfiguration +org.apache.maven.surefire.booter.Classpath +java.net.MalformedURLException +java.net.URLClassLoader +org.apache.maven.surefire.booter.IsolatedClassLoader +org.apache.maven.surefire.booter.SurefireExecutionException +org.apache.maven.surefire.booter.ProcessCheckerType +org.apache.maven.surefire.booter.StartupConfiguration +org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory +java.util.Spliterators$1Adapter +java.util.HashMap$ValueIterator +jdk.internal.module.Resources +jdk.internal.loader.BuiltinClassLoader$2 +jdk.internal.loader.BuiltinClassLoader$5 +java.lang.module.ModuleReader +jdk.internal.module.SystemModuleFinders$SystemModuleReader +jdk.internal.module.SystemModuleFinders$SystemImage +jdk.internal.jimage.ImageReaderFactory +java.nio.file.Paths +java.nio.file.FileSystems +java.nio.file.FileSystems$DefaultFileSystemHolder +java.nio.file.FileSystems$DefaultFileSystemHolder$1 +java.net.URI$Parser +jdk.internal.jimage.ImageReaderFactory$1 +jdk.internal.jimage.ImageReader +jdk.internal.jimage.BasicImageReader +jdk.internal.jimage.ImageReader$SharedImageReader +jdk.internal.jimage.BasicImageReader$1 +jdk.internal.jimage.NativeImageBuffer +jdk.internal.jimage.NativeImageBuffer$1 +jdk.internal.jimage.ImageHeader +java.nio.IntBuffer +java.nio.DirectIntBufferU +java.nio.DirectByteBufferR +java.nio.DirectIntBufferRU +jdk.internal.jimage.ImageStrings +jdk.internal.jimage.ImageStringsReader +jdk.internal.jimage.decompressor.Decompressor +jdk.internal.jimage.ImageLocation +java.util.Collections$EmptyIterator +jdk.internal.loader.BuiltinClassLoader$1 +java.lang.CompoundEnumeration +jdk.internal.loader.URLClassPath$1 +jdk.internal.loader.URLClassPath$FileLoader +java.util.SortedSet +java.util.NavigableSet +java.util.TreeSet +java.util.SortedMap +java.util.NavigableMap +java.util.TreeMap +java.util.TreeMap$Entry +java.util.TreeMap$KeySet +java.util.TreeMap$PrivateEntryIterator +java.util.TreeMap$KeyIterator +java.lang.Readable +java.io.Reader +java.io.BufferedReader +java.io.InputStreamReader +sun.nio.cs.StreamDecoder +java.nio.charset.CharsetDecoder +sun.nio.cs.UTF_8$Decoder +java.util.Vector +java.nio.CharBuffer +java.nio.HeapCharBuffer +java.nio.charset.CoderResult +java.util.AbstractSequentialList +java.util.LinkedList +java.util.LinkedList$Node +java.net.JarURLConnection +sun.net.www.protocol.jar.JarURLConnection +sun.net.www.protocol.jar.URLJarFile$URLJarFileCloseController +sun.net.www.protocol.jar.JarFileFactory +sun.net.www.protocol.jar.URLJarFile +sun.nio.fs.UnixFileKey +sun.net.www.protocol.jar.URLJarFile$URLJarFileEntry +sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream +java.util.LinkedHashMap$LinkedKeySet +java.util.LinkedHashMap$LinkedHashIterator +java.util.LinkedHashMap$LinkedKeyIterator +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory +org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory +org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder +org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder +org.apache.maven.surefire.api.util.internal.DaemonThreadFactory +java.util.concurrent.Executors +java.util.concurrent.Executors$DefaultThreadFactory +org.apache.maven.surefire.api.util.internal.DaemonThreadFactory$NamedThreadFactory +java.util.concurrent.AbstractExecutorService +java.util.concurrent.ThreadPoolExecutor +java.util.concurrent.ScheduledThreadPoolExecutor +java.util.concurrent.RejectedExecutionHandler +java.util.concurrent.ThreadPoolExecutor$AbortPolicy +java.util.concurrent.BlockingQueue +java.util.AbstractQueue +java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue +java.util.concurrent.Future +java.util.concurrent.RunnableFuture +java.util.concurrent.Delayed +java.util.concurrent.ScheduledFuture +java.util.concurrent.RunnableScheduledFuture +java.util.concurrent.locks.ReentrantLock$Sync +java.util.concurrent.locks.ReentrantLock$NonfairSync +java.util.concurrent.locks.Condition +java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject +org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory +java.net.URISyntaxException +java.util.concurrent.ExecutionException +java.net.SocketAddress +java.net.InetSocketAddress +java.nio.channels.Channel +java.nio.channels.AsynchronousChannel +java.nio.channels.AsynchronousByteChannel +org.apache.maven.surefire.booter.ForkedNodeArg +java.lang.UnsupportedOperationException +org.apache.maven.plugin.surefire.log.api.NullConsoleLogger +org.apache.maven.surefire.api.util.internal.Channels +org.apache.maven.surefire.api.util.internal.Channels$2 +org.apache.maven.surefire.api.util.internal.Channels$1 +java.nio.channels.WritableByteChannel +org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel +java.nio.channels.ReadableByteChannel +org.apache.maven.surefire.api.util.internal.AbstractNoninterruptibleWritableChannel +org.apache.maven.surefire.api.util.internal.Channels$4 +java.nio.channels.ClosedChannelException +java.nio.channels.NonWritableChannelException +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory$1 +java.util.concurrent.FutureTask +java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask +java.lang.invoke.VarHandles +java.lang.invoke.VarHandleInts$FieldInstanceReadOnly +java.lang.invoke.VarHandleInts$FieldInstanceReadWrite +java.lang.invoke.VarHandle$1 +jdk.internal.util.Preconditions$1 +java.lang.invoke.VarHandleGuards +java.lang.invoke.VarForm +java.lang.invoke.VarHandleReferences$FieldInstanceReadOnly +java.lang.invoke.VarHandleReferences$FieldInstanceReadWrite +java.util.concurrent.FutureTask$WaitNode +java.util.concurrent.Executors$RunnableAdapter +java.util.concurrent.ThreadPoolExecutor$Worker +java.lang.Thread$State +java.util.concurrent.TimeUnit$1 +java.time.temporal.TemporalUnit +java.time.temporal.ChronoUnit +java.time.temporal.TemporalAmount +java.time.Duration +java.math.BigInteger +java.lang.invoke.VarHandle$AccessDescriptor +org.apache.maven.surefire.api.stream.AbstractStreamEncoder +java.util.concurrent.ForkJoinPool$ManagedBlocker +org.apache.maven.surefire.booter.stream.EventEncoder +org.apache.maven.surefire.booter.spi.EventChannelEncoder +java.util.concurrent.locks.AbstractQueuedSynchronizer$Node +java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode +java.lang.AssertionError +org.apache.maven.surefire.api.booter.ForkedProcessEventType +org.apache.maven.surefire.api.report.ReportEntry +java.util.concurrent.atomic.AtomicBoolean +org.apache.maven.surefire.booter.spi.CommandChannelDecoder +org.apache.maven.surefire.api.stream.MalformedChannelException +org.apache.maven.surefire.api.stream.AbstractStreamDecoder +org.apache.maven.surefire.booter.stream.CommandDecoder +org.apache.maven.surefire.api.util.internal.AbstractNoninterruptibleReadableChannel +org.apache.maven.surefire.api.util.internal.Channels$3 +java.nio.channels.NonReadableChannelException +java.io.EOFException +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$MalformedFrameException +java.io.FileNotFoundException +org.apache.maven.surefire.api.stream.SegmentType +org.apache.maven.surefire.api.booter.Constants +java.nio.charset.StandardCharsets +sun.nio.cs.US_ASCII +sun.nio.cs.ISO_8859_1 +sun.nio.cs.UTF_16BE +sun.nio.cs.UTF_16LE +sun.nio.cs.UTF_16 +org.apache.maven.surefire.api.booter.MasterProcessCommand +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$Segment +org.apache.maven.surefire.booter.ForkedBooter$8 +org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils +java.lang.ApplicationShutdownHooks +java.lang.ApplicationShutdownHooks$1 +java.lang.Shutdown +java.lang.Shutdown$Lock +org.apache.maven.surefire.api.booter.ForkingReporterFactory +org.apache.maven.surefire.api.report.RunListener +org.apache.maven.surefire.api.report.TestOutputReceiver +org.apache.maven.surefire.api.report.TestReportListener +org.apache.maven.surefire.api.booter.ForkingRunListener +org.apache.maven.surefire.booter.CommandReader +org.apache.maven.surefire.api.testset.TestSetFailedException +java.util.concurrent.ConcurrentLinkedQueue +java.util.concurrent.ConcurrentLinkedQueue$Node +org.apache.maven.surefire.booter.CommandReader$CommandRunnable +java.util.concurrent.atomic.AtomicReference +java.util.concurrent.CountDownLatch +java.util.concurrent.CountDownLatch$Sync +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$Memento +org.apache.maven.surefire.booter.PpidChecker +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$BufferedStream +org.apache.maven.surefire.booter.PpidChecker$ProcessInfoConsumer +org.apache.maven.surefire.booter.PpidChecker$1 +org.apache.maven.surefire.booter.PpidChecker$2 +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$StreamReadStatus +org.apache.maven.surefire.booter.stream.CommandDecoder$1 +java.lang.NoSuchFieldError +org.apache.maven.surefire.shared.lang3.SystemUtils +org.apache.maven.surefire.api.booter.Command +org.apache.maven.surefire.booter.CommandReader$1 +java.util.concurrent.ConcurrentLinkedQueue$Itr +org.apache.maven.surefire.shared.lang3.SystemProperties +org.apache.maven.surefire.shared.lang3.function.Suppliers +org.apache.maven.surefire.shared.lang3.function.Suppliers$$Lambda$17/0x00007f4204010000 +org.apache.maven.surefire.shared.lang3.StringUtils +java.util.regex.Pattern +java.util.regex.Pattern$Node +java.util.regex.Pattern$LastNode +java.util.regex.Pattern$GroupHead +java.util.regex.CharPredicates +java.lang.Character$Subset +java.lang.Character$UnicodeBlock +java.util.regex.Pattern$CharPredicate +java.lang.invoke.LambdaForm$DMH/0x00007f4204014000 +java.util.regex.CharPredicates$$Lambda$18/0x00007f420405bdc8 +java.util.regex.Pattern$BmpCharPredicate +java.util.regex.Pattern$CharProperty +java.util.regex.Pattern$Qtype +java.util.regex.Pattern$BmpCharProperty +java.util.regex.Pattern$CharPropertyGreedy +java.util.regex.Pattern$SliceNode +java.util.regex.Pattern$Slice +java.util.regex.Pattern$Begin +java.util.regex.Pattern$First +java.util.regex.Pattern$Start +java.util.regex.Pattern$StartS +java.util.regex.Pattern$TreeInfo +org.apache.maven.surefire.shared.lang3.JavaVersion +org.apache.maven.surefire.shared.lang3.SystemProperties$$Lambda$19/0x00007f4204010678 +org.apache.maven.surefire.shared.lang3.math.NumberUtils +java.lang.NumberFormatException +java.math.BigDecimal +jdk.internal.math.FloatingDecimal +jdk.internal.math.FloatingDecimal$BinaryToASCIIConverter +jdk.internal.math.FloatingDecimal$ExceptionalBinaryToASCIIBuffer +jdk.internal.math.FloatingDecimal$BinaryToASCIIBuffer +jdk.internal.math.FloatingDecimal$1 +jdk.internal.math.FloatingDecimal$ASCIIToBinaryConverter +jdk.internal.math.FloatingDecimal$PreparedASCIIToBinaryBuffer +jdk.internal.math.FloatingDecimal$ASCIIToBinaryBuffer +org.apache.maven.surefire.shared.lang3.SystemUtils$$Lambda$20/0x00007f4204010ca0 +java.util.regex.Pattern$GroupTail +java.util.regex.CharPredicates$$Lambda$16/0x800000024 +java.util.regex.Pattern$BmpCharPropertyGreedy +java.util.regex.Pattern$$Lambda$18/0x800000028 +java.util.regex.Pattern$Ques +java.util.regex.Pattern$BranchConn +java.util.regex.Pattern$Branch +java.util.regex.ASCII +java.util.regex.Pattern$Curly +java.util.regex.CharPredicates$$Lambda$17/0x800000025 +java.util.regex.Pattern$Dollar +java.util.regex.Pattern$BitClass +org.apache.maven.surefire.booter.ForkedBooter$4 +org.apache.maven.surefire.api.booter.BiProperty +org.apache.maven.surefire.booter.ForkedBooter$3 +org.apache.maven.surefire.booter.ForkedBooter$PingScheduler +org.apache.maven.surefire.api.provider.ProviderParameters +org.apache.maven.surefire.api.booter.BaseProviderFactory +org.apache.maven.surefire.api.util.DirectoryScanner +org.apache.maven.surefire.api.util.ScanResult +org.apache.maven.surefire.api.util.RunOrderCalculator +org.apache.maven.surefire.api.util.ReflectionUtils +org.apache.maven.surefire.api.util.SurefireReflectionException +java.lang.reflect.InvocationTargetException +java.lang.IllegalAccessException +org.apache.maven.surefire.api.provider.SurefireProvider +org.apache.maven.surefire.api.provider.AbstractProvider +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +org.junit.platform.launcher.Launcher +org.apache.maven.surefire.api.util.ScannerFilter +java.io.StringReader +java.io.UncheckedIOException +org.apache.maven.surefire.junitplatform.LazyLauncher +org.junit.platform.launcher.TagFilter +org.junit.platform.commons.JUnitException +org.junit.platform.commons.util.PreconditionViolationException +org.junit.platform.commons.PreconditionViolationException +org.junit.platform.engine.Filter +org.junit.platform.launcher.PostDiscoveryFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$24/0x00007f4204016200 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$25/0x00007f4204016440 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$26/0x00007f4204016678 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$27/0x00007f42040168b8 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$28/0x00007f4204016af0 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$29/0x00007f4204016d40 +org.apache.maven.surefire.junitplatform.TestMethodFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$30/0x00007f42040171f0 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$31/0x00007f4204017430 +org.junit.platform.launcher.EngineFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$32/0x00007f42040178c0 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$33/0x00007f4204017b00 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$34/0x00007f4204017d38 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$35/0x00007f4204015000 +org.junit.platform.launcher.TestExecutionListener +org.apache.maven.surefire.report.RunModeSetter +org.apache.maven.surefire.junitplatform.RunListenerAdapter +org.apache.maven.surefire.api.report.OutputReportEntry +org.apache.maven.surefire.api.report.TestSetReportEntry +org.apache.maven.surefire.api.report.StackTraceWriter +org.apache.maven.surefire.report.ClassMethodIndexer +org.apache.maven.surefire.api.report.RunMode +org.apache.maven.surefire.api.report.ConsoleOutputCapture +org.apache.maven.surefire.api.report.ConsoleOutputCapture$ForwardingPrintStream +org.apache.maven.surefire.api.report.ConsoleOutputCapture$NullOutputStream +java.util.logging.Logger +java.util.logging.Handler +java.util.logging.Level +java.util.logging.Level$KnownLevel +java.util.logging.Level$KnownLevel$$Lambda$13/0x800000021 +java.util.logging.Level$KnownLevel$$Lambda$14/0x800000022 +java.util.logging.Logger$LoggerBundle +java.util.logging.Logger$ConfigurationData +java.util.logging.LogManager$1 +java.util.logging.LogManager$LoggerContext +java.util.logging.LogManager$SystemLoggerContext +java.util.logging.LogManager$LogNode +java.util.Collections$SynchronizedMap +java.util.logging.LogManager$Cleaner +java.util.logging.LoggingPermission +sun.util.logging.internal.LoggingProviderImpl$LogManagerAccess +java.util.logging.LogManager$LoggingProviderAccess +java.lang.System$LoggerFinder +jdk.internal.logger.DefaultLoggerFinder +sun.util.logging.internal.LoggingProviderImpl +java.util.logging.LogManager$2 +java.util.logging.LogManager$RootLogger +java.util.logging.LogManager$LoggerWeakRef +java.lang.invoke.MethodHandleImpl$AsVarargsCollector +java.lang.invoke.BoundMethodHandle$Species_LL +java.lang.invoke.LambdaForm$MH/0x00007f420401c000 +java.util.logging.LogManager$VisitedLoggers +java.util.logging.LogManager$LoggerContext$1 +java.util.concurrent.ConcurrentHashMap$KeySetView +java.util.Collections$3 +java.util.concurrent.ConcurrentHashMap$KeyIterator +java.util.Hashtable$Enumerator +java.util.logging.Level$$Lambda$12/0x800000010 +java.util.ArrayList$ArrayListSpliterator +java.util.logging.Level$KnownLevel$$Lambda$15/0x800000023 +java.util.stream.ReferencePipeline$7 +java.util.stream.FindOps +java.util.stream.FindOps$FindSink +java.util.stream.FindOps$FindSink$OfRef +java.util.stream.FindOps$FindOp +java.util.stream.FindOps$FindSink$OfRef$$Lambda$40/0x80000004b +java.util.stream.FindOps$FindSink$OfRef$$Lambda$38/0x800000049 +java.util.stream.FindOps$FindSink$OfRef$$Lambda$39/0x80000004a +java.util.stream.FindOps$FindSink$OfRef$$Lambda$37/0x800000048 +java.util.stream.ReferencePipeline$7$1 +java.util.stream.Streams$AbstractStreamBuilderImpl +java.util.stream.Stream$Builder +java.util.stream.Streams$StreamBuilderImpl +java.util.stream.Streams +java.util.IdentityHashMap$Values +java.lang.System$Logger +sun.util.logging.PlatformLogger$Bridge +sun.util.logging.PlatformLogger$ConfigurableBridge +jdk.internal.logger.BootstrapLogger +jdk.internal.logger.BootstrapLogger$DetectBackend +jdk.internal.logger.BootstrapLogger$DetectBackend$1 +jdk.internal.logger.BootstrapLogger$LoggingBackend +jdk.internal.logger.BootstrapLogger$RedirectedLoggers +jdk.internal.logger.BootstrapLogger$BootstrapExecutors +java.util.logging.LogManager$4 +java.util.logging.Logger$SystemLoggerHelper +java.util.logging.Logger$SystemLoggerHelper$1 +jdk.internal.logger.DefaultLoggerFinder$1 +org.apache.maven.surefire.junitplatform.TestPlanScannerFilter +org.apache.maven.surefire.api.util.DefaultScanResult +jdk.internal.loader.URLClassPath$FileLoader$1 +software.amazon.lambda.powertools.kafka.internal.DeserializationUtilsTest +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder +org.junit.platform.engine.ConfigurationParameters +org.junit.platform.engine.EngineDiscoveryRequest +org.junit.platform.launcher.LauncherDiscoveryRequest +org.junit.platform.engine.reporting.OutputDirectoryProvider +org.junit.platform.engine.DiscoverySelector +org.junit.platform.engine.discovery.DiscoverySelectors +org.junit.platform.commons.util.Preconditions +org.junit.platform.commons.util.StringUtils +org.junit.platform.commons.util.StringUtils$TwoPartSplitResult +java.util.regex.CharPredicates$$Lambda$44/0x00007f420405d900 +org.junit.platform.engine.discovery.ClassSelector +java.lang.invoke.LambdaForm$DMH/0x00007f420401c400 +org.junit.platform.commons.util.Preconditions$$Lambda$45/0x00007f420401a5b0 +org.junit.platform.commons.util.Preconditions$$Lambda$46/0x00007f420401a7e8 +java.lang.invoke.LambdaForm$DMH/0x00007f420401c800 +java.lang.invoke.DirectMethodHandle$Special +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$47/0x00007f420401aa20 +org.junit.platform.launcher.core.LauncherConfigurationParameters +org.junit.platform.commons.logging.LoggerFactory +org.junit.platform.commons.logging.Logger +org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger +org.junit.platform.launcher.core.LauncherConfigurationParameters$Builder +org.junit.platform.launcher.core.LauncherConfigurationParameters$Builder$$Lambda$48/0x00007f420401b760 +org.junit.platform.commons.util.CollectionUtils +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$2 +org.junit.platform.commons.util.ClassLoaderUtils +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$3 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$49/0x00007f420401e458 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$50/0x00007f420401e6a0 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners +org.junit.platform.engine.EngineDiscoveryListener +org.junit.platform.launcher.LauncherDiscoveryListener +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType$$Lambda$51/0x00007f420401f140 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType$$Lambda$52/0x00007f420401f360 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$53/0x00007f420401f778 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$54/0x00007f420401f9d0 +org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener +org.junit.platform.engine.EngineDiscoveryListener$1 +org.junit.platform.launcher.LauncherDiscoveryListener$1 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$55/0x00007f420401d4c0 +org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider +java.util.regex.Pattern$$Lambda$56/0x00007f420405df20 +java.util.regex.Pattern$CharPredicate$$Lambda$57/0x00007f420405e180 +java.util.regex.Pattern$CharPredicate$$Lambda$21/0x80000002d +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$59/0x00007f420401d928 +org.junit.platform.launcher.core.DefaultDiscoveryRequest +org.junit.platform.launcher.LauncherSession +org.junit.platform.launcher.core.LauncherFactory +org.junit.platform.launcher.core.LauncherConfig +org.junit.platform.launcher.core.LauncherConfig$Builder +org.junit.platform.launcher.core.DefaultLauncherConfig +org.junit.platform.launcher.core.DefaultLauncherSession +org.junit.platform.launcher.LauncherInterceptor +org.junit.platform.launcher.core.DefaultLauncherSession$1 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$60/0x00007f4204020dd0 +org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor +org.junit.platform.launcher.LauncherSessionListener +org.junit.platform.launcher.core.LauncherFactory$$Lambda$61/0x00007f4204021448 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore +org.junit.platform.launcher.core.LauncherFactory$$Lambda$62/0x00007f4204021898 +org.junit.platform.engine.support.store.NamespacedHierarchicalStoreException +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CloseAction +java.lang.invoke.LambdaForm$DMH/0x00007f4204024000 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CloseAction$$Lambda$63/0x00007f4204021f40 +java.util.stream.SliceOps +java.util.stream.ReferencePipeline$StatefulOp +java.util.stream.SliceOps$1 +org.junit.platform.launcher.core.DefaultLauncherSession$$Lambda$64/0x00007f4204022160 +java.util.stream.ReduceOps$1 +java.util.stream.ReduceOps$1ReducingSink +java.util.stream.SliceOps$1$1 +org.junit.platform.launcher.LauncherInterceptor$Invocation +java.lang.invoke.LambdaForm$DMH/0x00007f4204024400 +org.junit.platform.launcher.core.DefaultLauncherSession$$Lambda$65/0x00007f42040225a8 +org.junit.platform.launcher.core.ListenerRegistry +org.junit.platform.launcher.listeners.session.LauncherSessionListeners +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$66/0x00007f4204022c08 +org.junit.platform.launcher.core.ServiceLoaderRegistry +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$67/0x00007f4204023050 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$68/0x00007f42040232a0 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$69/0x00007f42040234e8 +org.junit.platform.commons.util.ServiceLoaderUtils +java.util.ServiceLoader$ProviderSpliterator +org.junit.platform.commons.util.ServiceLoaderUtils$$Lambda$70/0x00007f4204023948 +org.junit.platform.commons.util.ServiceLoaderUtils$$Lambda$71/0x00007f4204023ba0 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$72/0x00007f4204026000 +java.lang.invoke.LambdaForm$DMH/0x00007f4204024800 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$73/0x00007f4204026228 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$74/0x00007f4204026460 +org.junit.platform.launcher.LauncherSessionListener$1 +org.junit.platform.launcher.core.DelegatingLauncher +org.junit.platform.launcher.core.InterceptingLauncher +org.junit.platform.launcher.core.DefaultLauncherSession$$Lambda$75/0x00007f4204026db0 +org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry +org.junit.platform.engine.TestEngine +org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry$$Lambda$76/0x00007f42040271d8 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$77/0x00007f4204027400 +org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine +org.junit.jupiter.engine.JupiterTestEngine +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService +org.junit.jupiter.engine.config.JupiterConfiguration +org.junit.platform.engine.TestDescriptor +org.junit.platform.engine.support.hierarchical.EngineExecutionContext +org.junit.platform.launcher.core.LauncherFactory$$Lambda$78/0x00007f4204025400 +org.junit.platform.launcher.core.DefaultLauncher +org.junit.platform.launcher.TestPlan +org.junit.platform.launcher.core.InternalTestPlan +org.junit.platform.launcher.core.LauncherListenerRegistry +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$79/0x00007f4204024c00 +org.junit.platform.launcher.core.CompositeTestExecutionListener +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$80/0x00007f42040282a0 +org.junit.platform.launcher.core.EngineExecutionOrchestrator +org.junit.platform.launcher.TestPlan$Visitor +org.junit.platform.engine.EngineExecutionListener +org.junit.platform.launcher.core.DiscoveryIssueException +org.junit.platform.launcher.core.DefaultLauncher$$Lambda$81/0x00007f4204028d68 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator +org.junit.platform.launcher.core.EngineDiscoveryResultValidator +org.junit.platform.launcher.core.EngineIdValidator +org.junit.platform.launcher.core.EngineIdValidator$$Lambda$82/0x00007f42040295d0 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$83/0x00007f42040297f8 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$84/0x00007f4204029a30 +org.junit.platform.commons.util.ClassNamePatternFilterUtils +org.junit.platform.launcher.core.LauncherFactory$$Lambda$85/0x00007f4204029e70 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$86/0x00007f420402a0b0 +java.lang.invoke.LambdaForm$DMH/0x00007f420402c000 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$87/0x00007f420402a300 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$88/0x00007f420402a558 +org.junitpioneer.jupiter.issue.IssueExtensionExecutionListener +org.junitpioneer.jupiter.IssueProcessor +org.junit.platform.launcher.listeners.UniqueIdTrackingListener +org.junit.platform.launcher.core.LauncherFactory$$Lambda$89/0x00007f420402aee8 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$90/0x00007f420402b120 +org.junit.platform.launcher.core.InterceptingLauncher$$Lambda$91/0x00007f420402b358 +org.junit.platform.launcher.core.LauncherPhase +org.junit.platform.engine.UniqueId +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$92/0x00007f420402bbf0 +org.junit.platform.launcher.core.DiscoveryIssueCollector +org.junit.platform.engine.TestSource +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener +org.junit.platform.launcher.core.DelegatingLauncherDiscoveryRequest +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$1 +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$93/0x00007f420402ecc8 +org.junit.platform.launcher.core.EngineFilterer +org.junit.platform.engine.FilterResult +org.junit.platform.launcher.core.EngineFilterer$$Lambda$94/0x00007f420402f348 +java.lang.invoke.LambdaForm$DMH/0x00007f420402c400 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$95/0x00007f420402f590 +java.util.stream.MatchOps$MatchKind +java.util.stream.MatchOps +java.util.stream.MatchOps$MatchOp +java.util.stream.MatchOps$BooleanTerminalSink +java.util.stream.MatchOps$$Lambda$96/0x00007f4204060150 +java.util.stream.MatchOps$1MatchSink +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$97/0x00007f420402f7e0 +org.junit.platform.engine.UniqueIdFormat +java.io.UnsupportedEncodingException +java.util.Formatter +java.util.regex.Pattern$$Lambda$19/0x800000029 +java.util.regex.Pattern$BmpCharPredicate$$Lambda$20/0x80000002b +java.util.Locale$Category +java.util.Formatter$Conversion +java.util.Formatter$FormatString +java.util.Formatter$FormatSpecifier +java.util.Formatter$Flags +java.util.Formatter$FixedString +java.util.Formattable +java.util.regex.Pattern$$Lambda$100/0x00007f4204060cd8 +java.lang.invoke.LambdaForm$DMH/0x00007f420402c800 +org.junit.platform.engine.UniqueIdFormat$$Lambda$101/0x00007f420402fc28 +java.net.URLEncoder +java.util.BitSet +java.io.CharArrayWriter +org.junit.platform.engine.UniqueIdFormat$$Lambda$102/0x00007f420402d000 +org.junit.platform.engine.UniqueIdFormat$$Lambda$103/0x00007f420402d240 +org.junit.platform.engine.UniqueIdFormat$$Lambda$104/0x00007f420402d480 +org.junit.platform.engine.UniqueIdFormat$$Lambda$105/0x00007f420402d6c0 +org.junit.platform.engine.UniqueIdFormat$$Lambda$106/0x00007f420402d900 +org.junit.platform.engine.UniqueId$Segment +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$107/0x00007f420402dd60 +org.junit.jupiter.engine.config.CachingJupiterConfiguration +org.junit.jupiter.engine.config.DefaultJupiterConfiguration +org.junit.jupiter.api.parallel.ExecutionMode +org.junit.jupiter.api.TestInstance$Lifecycle +org.junit.jupiter.api.io.CleanupMode +org.junit.jupiter.api.extension.TestInstantiationAwareExtension$ExtensionContextScope +org.junit.jupiter.engine.config.EnumConfigurationParameterConverter +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter +org.junit.jupiter.api.DisplayNameGenerator +org.junit.jupiter.api.MethodOrderer +org.junit.jupiter.api.ClassOrderer +org.junit.jupiter.api.io.TempDirFactory +org.junit.platform.engine.support.hierarchical.Node +org.junit.platform.engine.support.descriptor.AbstractTestDescriptor +org.junit.platform.engine.support.descriptor.EngineDescriptor +org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor +org.junit.jupiter.engine.extension.ExtensionRegistry +org.junit.jupiter.api.extension.ExtensionContext +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver +org.junit.platform.engine.support.discovery.SelectorResolver +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$InitializationContext +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$Builder +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$108/0x00007f42040333f8 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$Builder$$Lambda$109/0x00007f4204033638 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$110/0x00007f4204033880 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$111/0x00007f4204033ac0 +org.junit.platform.engine.TestDescriptor$Visitor +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$112/0x00007f4204033f00 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter +org.junit.platform.engine.DiscoveryIssue +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$113/0x00007f4204034540 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$114/0x00007f4204034788 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$DefaultInitializationContext +org.junit.platform.engine.DiscoveryFilter +org.junit.platform.engine.discovery.ClassNameFilter +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$115/0x00007f4204035040 +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$116/0x00007f4204035298 +org.junit.platform.engine.discovery.PackageNameFilter +org.junit.platform.engine.CompositeFilter +org.junit.platform.engine.CompositeFilter$1 +org.junit.platform.engine.CompositeFilter$1$$Lambda$117/0x00007f4204035b58 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$$Lambda$118/0x00007f4204035da8 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$$Lambda$119/0x00007f4204035ff0 +java.util.stream.Collectors$$Lambda$120/0x00007f4204061750 +java.util.stream.Collectors$$Lambda$121/0x00007f4204061980 +org.junit.platform.engine.support.discovery.ClassContainerSelectorResolver +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$122/0x00007f4204036740 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$123/0x00007f4204036990 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$124/0x00007f4204036be0 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$125/0x00007f4204036e38 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod +org.junit.jupiter.engine.discovery.predicates.IsTestMethod +org.junit.jupiter.api.Test +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition +org.junit.jupiter.engine.discovery.predicates.IsTestMethod$$Lambda$126/0x00007f4204037960 +org.junit.platform.commons.support.ModifierSupport +java.lang.invoke.LambdaForm$DMH/0x00007f4204038000 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$127/0x00007f4204037d98 +java.lang.invoke.MethodHandle$1 +java.lang.invoke.LambdaForm$DMH/0x00007f4204038400 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$128/0x00007f420403c000 +java.lang.invoke.LambdaForm$DMH/0x00007f4204038800 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$129/0x00007f420403c248 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$130/0x00007f420403c4a0 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$131/0x00007f420403c6f0 +java.lang.invoke.LambdaForm$DMH/0x00007f4204038c00 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition$$Lambda$132/0x00007f420403c938 +org.junit.platform.commons.util.ReflectionUtils +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$133/0x00007f420403cd98 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$134/0x00007f420403cfe8 +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod +org.junit.jupiter.api.DynamicNode +java.util.regex.MatchResult +java.util.regex.Matcher +java.util.regex.IntHashSet +org.junit.jupiter.api.TestFactory +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod$$Lambda$135/0x00007f420403d8b8 +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod$$Lambda$136/0x00007f420403dae8 +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod$$Lambda$137/0x00007f420403dd40 +java.util.function.Predicate$$Lambda$138/0x00007f4204062210 +org.junit.jupiter.engine.discovery.predicates.IsTestTemplateMethod +org.junit.jupiter.api.TestTemplate +org.junit.jupiter.engine.discovery.predicates.IsTestTemplateMethod$$Lambda$139/0x00007f420403e3f0 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$140/0x00007f420403e620 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$141/0x00007f420403e870 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$142/0x00007f420403eab8 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$143/0x00007f420403ed08 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$144/0x00007f420403ef48 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$145/0x00007f420403f198 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$146/0x00007f420403f3d8 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$147/0x00007f420403f628 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$148/0x00007f420403f868 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$149/0x00007f420403fab8 +org.junit.jupiter.engine.discovery.ClassSelectorResolver +org.junit.jupiter.engine.descriptor.ResourceLockAware +org.junit.jupiter.engine.descriptor.TestClassAware +org.junit.jupiter.engine.descriptor.Validatable +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor +org.junit.jupiter.engine.descriptor.ClassTestDescriptor +org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor +org.junit.jupiter.api.extension.ClassTemplateInvocationContext +org.junit.jupiter.engine.descriptor.Filterable +org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor +org.junit.jupiter.engine.discovery.MethodSelectorResolver +org.junit.jupiter.engine.discovery.MethodFinder +java.util.regex.Pattern$$Lambda$150/0x00007f4204062660 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$TestDescriptorFactory +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor +org.junit.jupiter.engine.extension.ExtensionRegistrar +java.lang.invoke.LambdaForm$DMH/0x00007f4204084000 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$151/0x00007f4204080f00 +org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor +org.junit.jupiter.engine.descriptor.DynamicNodeTestDescriptor +org.junit.jupiter.engine.descriptor.DynamicContainerTestDescriptor +org.junit.jupiter.engine.descriptor.DynamicTestTestDescriptor +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$152/0x00007f4204082438 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$153/0x00007f4204082bf0 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor +org.junit.jupiter.engine.discovery.ClassOrderingVisitor +org.junit.jupiter.api.ClassOrdererContext +org.junit.platform.commons.util.LruCache +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$154/0x00007f4204083af0 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$155/0x00007f4204083d38 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$156/0x00007f4204086000 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$157/0x00007f4204086250 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$158/0x00007f4204086498 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$MessageGenerator +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer$$Lambda$159/0x00007f4204086ad0 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer$$Lambda$160/0x00007f4204086cf0 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$161/0x00007f4204086f10 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$162/0x00007f4204087160 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor +org.junit.jupiter.api.MethodOrdererContext +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$163/0x00007f42040877e8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$164/0x00007f4204087a38 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$165/0x00007f4204087c78 +java.lang.invoke.LambdaForm$MH/0x00007f4204084400 +java.util.Comparator$$Lambda$166/0x00007f42040628c0 +java.util.Collections$ReverseComparator +java.util.Comparators$NaturalOrderComparator +java.util.Collections$ReverseComparator2 +java.util.function.UnaryOperator +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$167/0x00007f4204085000 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$168/0x00007f4204085260 +org.junit.platform.engine.CompositeTestDescriptorVisitor +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution +org.junit.platform.engine.support.discovery.SelectorResolver$Context +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$DefaultContext +org.junit.platform.engine.support.discovery.SelectorResolver$Match +org.junit.platform.engine.support.discovery.SelectorResolver$Match$$Lambda$169/0x00007f4204084800 +org.junit.platform.engine.support.discovery.SelectorResolver$Match$Type +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$170/0x00007f4204088000 +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$171/0x00007f4204088258 +java.util.ArrayDeque$$Lambda$172/0x00007f4204063970 +org.junit.platform.engine.discovery.UniqueIdSelector +org.junit.platform.engine.support.discovery.SelectorResolver$Resolution +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$173/0x00007f4204088900 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$174/0x00007f4204088b48 +org.junit.platform.engine.discovery.ClasspathResourceSelector +org.junit.platform.engine.discovery.ClasspathRootSelector +org.junit.platform.commons.support.ReflectionSupport +org.junit.platform.commons.util.ClasspathScannerLoader +org.junit.platform.commons.support.scanning.ClasspathScanner +java.util.Spliterators$IteratorSpliterator +jdk.internal.misc.ScopedMemoryAccess$Scope +org.junit.platform.commons.support.scanning.DefaultClasspathScanner +java.nio.file.FileVisitor +org.junit.platform.commons.util.ClasspathScannerLoader$$Lambda$175/0x00007f4204089a88 +org.junit.platform.commons.function.Try +java.lang.invoke.LambdaForm$DMH/0x00007f420408c000 +org.junit.platform.commons.util.ClasspathScannerLoader$$Lambda$176/0x00007f4204089ef8 +java.lang.invoke.LambdaForm$DMH/0x00007f420408c400 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$177/0x00007f420408a128 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$178/0x00007f420408a360 +org.junit.platform.commons.function.Try$Failure +org.junit.platform.commons.function.Try$Success +org.junit.platform.commons.function.Try$$Lambda$179/0x00007f420408aa38 +java.util.regex.Pattern$1 +org.junit.platform.engine.discovery.ClassSelector$$Lambda$180/0x00007f420408ac60 +org.junit.jupiter.api.Nested +org.junit.platform.commons.support.AnnotationSupport +org.junit.platform.commons.util.AnnotationUtils +java.lang.annotation.Inherited +sun.reflect.generics.parser.SignatureParser +sun.reflect.generics.tree.Tree +sun.reflect.generics.tree.TypeTree +sun.reflect.generics.tree.TypeArgument +sun.reflect.generics.tree.ReturnType +sun.reflect.generics.tree.TypeSignature +sun.reflect.generics.tree.BaseType +sun.reflect.generics.tree.FieldTypeSignature +sun.reflect.generics.tree.SimpleClassTypeSignature +sun.reflect.generics.tree.ClassTypeSignature +sun.reflect.generics.scope.Scope +sun.reflect.generics.scope.AbstractScope +sun.reflect.generics.scope.ClassScope +sun.reflect.generics.factory.GenericsFactory +sun.reflect.generics.factory.CoreReflectionFactory +sun.reflect.generics.visitor.TypeTreeVisitor +sun.reflect.generics.visitor.Reifier +java.lang.annotation.Target +java.lang.reflect.GenericArrayType +sun.reflect.annotation.AnnotationType +sun.reflect.annotation.AnnotationType$1 +java.lang.annotation.ElementType +java.lang.annotation.Retention +java.lang.annotation.Documented +java.lang.annotation.RetentionPolicy +sun.reflect.annotation.ExceptionProxy +sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy +sun.reflect.annotation.AnnotationParser$1 +java.lang.reflect.InvocationHandler +sun.reflect.annotation.AnnotationInvocationHandler +java.lang.reflect.Proxy +java.lang.ClassValue +java.lang.reflect.Proxy$1 +java.lang.ClassValue$Entry +java.lang.ClassValue$Identity +java.lang.ClassValue$Version +jdk.internal.loader.AbstractClassLoaderValue$Sub +java.lang.reflect.Proxy$$Lambda$181/0x00007f420406bf50 +java.lang.reflect.Proxy$ProxyBuilder +java.lang.PublicMethods +java.util.LinkedHashMap$LinkedValues +java.util.LinkedHashMap$LinkedValueIterator +java.lang.reflect.Proxy$ProxyBuilder$$Lambda$182/0x00007f420406cb68 +java.lang.module.ModuleDescriptor$Modifier +java.lang.module.ModuleDescriptor$Builder +jdk.internal.module.Checks +java.lang.module.ModuleDescriptor$Builder$$Lambda$1/0x800000002 +java.lang.reflect.Proxy$$Lambda$184/0x00007f420406cd98 +java.lang.reflect.ProxyGenerator +java.lang.reflect.ProxyGenerator$ProxyMethod +java.util.StringJoiner +java.lang.reflect.ProxyGenerator$$Lambda$185/0x00007f420406d4e8 +java.lang.reflect.ProxyGenerator$$Lambda$186/0x00007f420406d728 +java.lang.reflect.ProxyGenerator$PrimitiveTypeInfo +jdk.internal.org.objectweb.asm.Edge +jdk.proxy1.$Proxy0 +java.lang.reflect.Proxy$ProxyBuilder$1 +sun.reflect.annotation.AnnotationParser$$Lambda$187/0x00007f420406e218 +java.lang.invoke.LambdaForm$DMH/0x00007f420408c800 +jdk.proxy1.$Proxy1 +jdk.proxy1.$Proxy2 +org.apiguardian.api.API +org.apiguardian.api.API$Status +jdk.proxy2.$Proxy3 +java.lang.reflect.UndeclaredThrowableException +java.lang.Class$AnnotationData +org.junit.platform.commons.util.KotlinReflectionUtils +org.junit.platform.commons.function.Try$Transformer +org.junit.platform.commons.util.KotlinReflectionUtils$$Lambda$188/0x00007f420408e200 +org.junit.jupiter.api.ClassTemplate +jdk.proxy1.$Proxy4 +org.junit.platform.commons.annotation.Testable +jdk.proxy2.$Proxy5 +org.junit.platform.commons.util.ReflectionUtils$HierarchyTraversalMode +org.junit.platform.commons.util.ReflectionUtils$$Lambda$189/0x00007f420408eeb0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$190/0x00007f420408f100 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$191/0x00007f420408f320 +java.util.Arrays$LegacyMergeSort +java.util.TimSort +jdk.proxy2.$Proxy6 +org.junitpioneer.jupiter.SetEnvironmentVariable +java.lang.annotation.Repeatable +org.junitpioneer.jupiter.WritesEnvironmentVariable +org.junit.jupiter.api.extension.ExtendWith +jdk.proxy2.$Proxy7 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$192/0x00007f420408d260 +org.junit.jupiter.api.extension.ExtensionConfigurationException +org.junit.jupiter.api.extension.TestInstanceFactoryContext +org.junit.jupiter.api.extension.Extension +org.junit.jupiter.api.extension.TestInstantiationAwareExtension +org.junit.jupiter.api.extension.TestInstantiationException +org.junit.jupiter.engine.execution.ConditionEvaluator +org.junit.jupiter.engine.execution.ConditionEvaluationException +org.junit.jupiter.api.extension.ConditionEvaluationResult +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker +org.junit.jupiter.api.extension.ReflectiveInvocationContext +org.junit.jupiter.api.extension.InvocationInterceptor$Invocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain +org.junit.jupiter.engine.descriptor.DisplayNameUtils +org.junit.jupiter.api.DisplayNameGenerator$Standard +org.junit.jupiter.api.DisplayNameGenerator$Simple +org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores +org.junit.jupiter.api.DisplayNameGenerator$IndicativeSentences +org.junit.jupiter.api.DisplayNameGenerator$IndicativeSentences$$Lambda$193/0x00007f4204091850 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$194/0x00007f4204091aa0 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$195/0x00007f4204091cc0 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$196/0x00007f4204091ef8 +org.junit.platform.engine.support.descriptor.ClassSource +org.junit.jupiter.api.DisplayName +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$197/0x00007f4204092540 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$198/0x00007f4204092780 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$199/0x00007f42040929d0 +org.junit.jupiter.api.DisplayNameGeneration +java.util.AbstractList$Itr +java.util.AbstractList$ListItr +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$200/0x00007f4204092e10 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$201/0x00007f4204093050 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$202/0x00007f4204093290 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$203/0x00007f42040934d8 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$204/0x00007f4204093700 +org.junit.jupiter.engine.config.DefaultJupiterConfiguration$$Lambda$205/0x00007f4204093948 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo$$Lambda$206/0x00007f4204093d78 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo$$Lambda$207/0x00007f4204093fa0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo$$Lambda$208/0x00007f42040941c8 +org.junit.jupiter.api.Tag +org.junit.jupiter.api.Tags +jdk.proxy1.$Proxy8 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$209/0x00007f4204094800 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$210/0x00007f4204094a28 +java.lang.invoke.LambdaForm$DMH/0x00007f4204098000 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$211/0x00007f4204094c68 +org.junit.platform.engine.TestTag +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$212/0x00007f42040950d0 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$213/0x00007f4204095310 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$214/0x00007f4204095530 +java.lang.invoke.LambdaForm$DMH/0x00007f4204098400 +java.util.function.Function$$Lambda$215/0x00007f420406fd40 +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils +org.junit.jupiter.api.TestInstance +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils$$Lambda$216/0x00007f4204095b78 +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils$$Lambda$217/0x00007f4204095db8 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$218/0x00007f4204095fe0 +java.lang.invoke.LambdaForm$DMH/0x00007f4204098800 +org.junit.jupiter.engine.config.EnumConfigurationParameterConverter$$Lambda$219/0x00007f4204096228 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$1 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$DefaultExclusiveResourceCollector +org.junit.jupiter.api.parallel.ResourceLock +jdk.internal.reflect.ClassFileConstants +jdk.internal.reflect.AccessorGenerator +jdk.internal.reflect.MethodAccessorGenerator +jdk.internal.reflect.ByteVectorFactory +jdk.internal.reflect.ByteVector +jdk.internal.reflect.ByteVectorImpl +jdk.internal.reflect.ClassFileAssembler +jdk.internal.reflect.UTF8 +jdk.internal.reflect.Label +jdk.internal.reflect.Label$PatchInfo +jdk.internal.reflect.MethodAccessorGenerator$1 +jdk.internal.reflect.ClassDefiner +jdk.internal.reflect.ClassDefiner$1 +jdk.internal.reflect.GeneratedConstructorAccessor1 +java.lang.Class$1 +jdk.internal.reflect.BootstrapConstructorAccessorImpl +org.junit.jupiter.api.parallel.ResourceLocks +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$LifecycleMethods +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$220/0x00007f4204097110 +java.lang.invoke.LambdaForm$DMH/0x00007f4204099000 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$221/0x00007f4204097348 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils +org.junit.jupiter.api.BeforeAll +org.junit.platform.commons.support.HierarchyTraversalMode +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$222/0x00007f420409c000 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$223/0x00007f420409c248 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$224/0x00007f420409c498 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$225/0x00007f420409c6e0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$226/0x00007f420409c938 +java.util.function.IntFunction +org.junit.platform.commons.util.ReflectionUtils$$Lambda$227/0x00007f4204097dd8 +java.util.stream.Nodes +java.util.stream.Node +java.util.stream.Nodes$EmptyNode +java.util.stream.Nodes$EmptyNode$OfRef +java.util.stream.Node$OfPrimitive +java.util.stream.Node$OfInt +java.util.stream.Nodes$EmptyNode$OfInt +java.util.stream.Node$OfLong +java.util.stream.Nodes$EmptyNode$OfLong +java.util.stream.Node$OfDouble +java.util.stream.Nodes$EmptyNode$OfDouble +java.util.stream.Node$Builder +java.util.stream.AbstractSpinedBuffer +java.util.stream.SpinedBuffer +java.util.stream.Nodes$SpinedNodeBuilder +org.junit.platform.commons.util.ReflectionUtils$$Lambda$228/0x00007f420409cb88 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$229/0x00007f420409cde0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$230/0x00007f420409d000 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$231/0x00007f420409d258 +java.util.stream.DistinctOps +java.util.stream.DistinctOps$1 +org.junit.platform.commons.util.CollectionUtils$$Lambda$232/0x00007f420409d478 +java.util.stream.DistinctOps$1$2 +java.util.LinkedHashMap$LinkedEntrySet +java.util.LinkedHashMap$LinkedEntryIterator +org.junitpioneer.jupiter.SetEnvironmentVariable$SetEnvironmentVariables +jdk.proxy2.$Proxy9 +sun.reflect.annotation.AnnotationParser$$Lambda$233/0x00007f42040748a8 +org.junit.jupiter.api.extension.BeforeEachCallback +org.junit.jupiter.api.extension.AfterEachCallback +org.junit.jupiter.api.extension.BeforeAllCallback +org.junit.jupiter.api.extension.AfterAllCallback +org.junitpioneer.jupiter.AbstractEntryBasedExtension +org.junitpioneer.jupiter.EnvironmentVariableExtension +jdk.proxy2.$Proxy10 +org.junit.jupiter.api.parallel.ResourceLockTarget +org.junit.jupiter.api.parallel.ResourceAccessMode +jdk.proxy2.$Proxy11 +jdk.internal.reflect.GeneratedConstructorAccessor2 +org.junit.jupiter.api.extension.Extensions +sun.reflect.annotation.AnnotationInvocationHandler$1 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$234/0x00007f420409f808 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$235/0x00007f420409fa30 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$236/0x00007f420409fc80 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition$$Lambda$237/0x00007f420409a000 +java.util.stream.ReferencePipeline$15 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$238/0x00007f420409a238 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$239/0x00007f420409a480 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$240/0x00007f420409a6d0 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition$$Lambda$241/0x00007f420409a918 +java.util.stream.ReferencePipeline$15$1 +org.junit.jupiter.api.AfterAll +jdk.internal.reflect.GeneratedConstructorAccessor3 +org.junit.jupiter.api.BeforeEach +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$242/0x00007f420409af70 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$243/0x00007f420409b1b8 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$244/0x00007f420409b408 +org.junit.jupiter.api.AfterEach +jdk.internal.reflect.GeneratedConstructorAccessor4 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$245/0x00007f420409b850 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$246/0x00007f420409ba98 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$247/0x00007f420409bcc0 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$248/0x00007f42040a0000 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$249/0x00007f42040a0248 +org.junit.platform.engine.SelectorResolutionResult +org.junit.platform.engine.SelectorResolutionResult$Status +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$250/0x00007f42040a0ae0 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$251/0x00007f42040a0d18 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$252/0x00007f42040a0f68 +java.util.stream.ForEachOps +java.util.stream.ForEachOps$ForEachOp +java.util.stream.ForEachOps$ForEachOp$OfRef +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$253/0x00007f42040a11a0 +org.junit.platform.commons.util.ReflectionUtils$CycleErrorHandling +org.junit.platform.commons.util.ReflectionUtils$CycleErrorHandling$1 +org.junit.platform.commons.util.ReflectionUtils$CycleErrorHandling$2 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$254/0x00007f42040a1cd0 +java.lang.invoke.LambdaForm$DMH/0x00007f42040a4000 +java.util.function.Predicate$$Lambda$255/0x00007f42040754b8 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$256/0x00007f42040a1f08 +java.util.function.Predicate$$Lambda$257/0x00007f4204075710 +java.util.stream.Streams$ConcatSpliterator +java.util.stream.Streams$ConcatSpliterator$OfRef +java.util.stream.Streams$2 +org.junit.platform.engine.discovery.NestedClassSelector +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$258/0x00007f42040a23b0 +java.util.stream.AbstractPipeline$$Lambda$259/0x00007f42040760d8 +java.util.stream.StreamSpliterators$AbstractWrappingSpliterator +java.util.stream.StreamSpliterators$WrappingSpliterator +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$260/0x00007f42040a25f8 +java.util.stream.StreamSpliterators +java.util.stream.StreamSpliterators$WrappingSpliterator$$Lambda$261/0x00007f4204076a38 +org.junit.platform.engine.discovery.MethodSelector +org.junit.platform.engine.discovery.MethodSelector$$Lambda$262/0x00007f42040a2a88 +org.junit.platform.commons.util.ClassUtils +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$263/0x00007f42040a2ed0 +org.junit.platform.engine.discovery.IterationSelector +org.junit.platform.engine.discovery.DirectorySelector +org.junit.platform.engine.discovery.FileSelector +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$264/0x00007f42040a37e0 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$265/0x00007f42040a3a08 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$266/0x00007f42040a3c38 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$267/0x00007f42040a6000 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$268/0x00007f42040a6250 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$269/0x00007f42040a6490 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$270/0x00007f42040a66d8 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$271/0x00007f42040a6900 +org.junit.platform.commons.util.ClassUtils$$Lambda$272/0x00007f42040a6b48 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$273/0x00007f42040a6d88 +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$VoidMethodInterceptorCall +org.junit.jupiter.api.extension.InvocationInterceptor +java.lang.invoke.LambdaForm$DMH/0x00007f42040a4400 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$274/0x00007f42040a73b0 +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$$Lambda$275/0x00007f42040a77d0 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$276/0x00007f42040a79f8 +org.junit.jupiter.api.DisplayNameGenerator$$Lambda$277/0x00007f42040a7c30 +org.junit.platform.engine.support.descriptor.MethodSource +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo$$Lambda$278/0x00007f42040a5438 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo$$Lambda$279/0x00007f42040a5660 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo$$Lambda$280/0x00007f42040a5888 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$281/0x00007f42040a5ac0 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$282/0x00007f42040a5d00 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$283/0x00007f42040a4800 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$284/0x00007f42040a4a40 +jdk.internal.reflect.GeneratedConstructorAccessor5 +java.util.HashMap$KeySpliterator +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$285/0x00007f42040a4c68 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$286/0x00007f42040ac000 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$287/0x00007f42040ac238 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$288/0x00007f42040ac478 +org.junit.jupiter.api.ClassDescriptor +org.junit.jupiter.engine.discovery.AbstractAnnotatedDescriptorWrapper +org.junit.jupiter.engine.discovery.DefaultClassDescriptor +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$289/0x00007f42040acd28 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$290/0x00007f42040acf68 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$291/0x00007f42040ad1c0 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$292/0x00007f42040ad408 +org.junit.jupiter.api.Order +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$293/0x00007f42040ad840 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$294/0x00007f42040ada78 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$295/0x00007f42040adcb8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$296/0x00007f42040adef0 +org.junit.platform.engine.TestDescriptor$$Lambda$297/0x00007f42040ae130 +org.junit.jupiter.api.TestClassOrder +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$298/0x00007f42040ae568 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$299/0x00007f42040ae7a8 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$300/0x00007f42040ae9e8 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$301/0x00007f42040aec30 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$302/0x00007f42040aee58 +org.junit.jupiter.api.TestMethodOrder +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$303/0x00007f42040af298 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$304/0x00007f42040af4d8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$305/0x00007f42040af718 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$306/0x00007f42040af958 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$307/0x00007f42040afb80 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$308/0x00007f42040aa000 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$309/0x00007f42040afdc8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$310/0x00007f42040aa248 +org.junit.jupiter.api.MethodDescriptor +org.junit.jupiter.engine.discovery.DefaultMethodDescriptor +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$311/0x00007f42040aa8d8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$312/0x00007f42040aab18 +org.junit.platform.engine.support.hierarchical.Node$ExecutionMode +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$313/0x00007f42040aafa0 +org.junit.jupiter.engine.descriptor.Validatable$$Lambda$314/0x00007f42040ab1d8 +org.junit.jupiter.api.extension.ClassTemplateInvocationLifecycleMethod +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$315/0x00007f42040ab610 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$316/0x00007f42040ab848 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$317/0x00007f42040aba70 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$318/0x00007f42040abc98 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$319/0x00007f42040a9000 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$320/0x00007f42040a9250 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$321/0x00007f42040a9488 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$322/0x00007f42040a96b0 +org.junit.platform.launcher.core.EngineDiscoveryResultValidator$$Lambda$323/0x00007f42040a98d8 +org.junit.platform.engine.TestDescriptor$Type +org.junit.platform.launcher.core.EngineDiscoveryResultValidator$$Lambda$324/0x00007f42040a8800 +org.junit.platform.launcher.EngineDiscoveryResult +org.junit.platform.launcher.EngineDiscoveryResult$Status +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$325/0x00007f42040b0000 +java.util.Collections$UnmodifiableList$1 +java.util.ArrayList$ListItr +org.junit.platform.commons.util.ExceptionUtils +java.io.StringWriter +org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener$$Lambda$326/0x00007f42040b0238 +org.junit.platform.launcher.core.DiscoveryIssueNotifier +org.junit.platform.engine.DiscoveryIssue$Severity +org.junit.platform.launcher.core.LauncherDiscoveryResult$EngineResultInfo +org.junit.platform.launcher.core.EngineFilterer$$Lambda$327/0x00007f42040b0b18 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$328/0x00007f42040b0d58 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$329/0x00007f42040b0fa8 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$330/0x00007f42040b11e8 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$331/0x00007f42040b1428 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$332/0x00007f42040b1680 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$333/0x00007f42040b18a0 +java.lang.invoke.LambdaForm$DMH/0x00007f42040b4000 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$334/0x00007f42040b1af0 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$335/0x00007f42040b1d18 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$336/0x00007f42040b1f50 +java.lang.invoke.LambdaForm$DMH/0x00007f42040b4400 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$337/0x00007f42040b2180 +org.junit.platform.engine.TestDescriptor$$Lambda$338/0x00007f42040b23a0 +org.junit.platform.launcher.core.LauncherDiscoveryResult +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$339/0x00007f42040b2848 +org.junit.platform.launcher.core.LauncherPhase$$Lambda$340/0x00007f42040b2a80 +org.junit.platform.engine.ConfigurationParameters$$Lambda$341/0x00007f42040b2cc0 +org.junit.platform.launcher.core.LauncherDiscoveryResult$$Lambda$342/0x00007f42040b2f08 +org.junit.platform.launcher.core.LauncherDiscoveryResult$$Lambda$343/0x00007f42040b3158 +org.junit.platform.launcher.TestPlan$$Lambda$344/0x00007f42040b3398 +org.junit.platform.launcher.TestPlan$$Lambda$345/0x00007f42040b35c0 +org.junit.platform.launcher.TestIdentifier +org.junit.platform.launcher.TestIdentifier$SerializedForm +java.io.ObjectStreamClass +java.io.ObjectStreamClass$Caches +java.io.ClassCache +java.io.ObjectStreamClass$Caches$1 +java.io.ClassCache$1 +java.io.ObjectStreamClass$Caches$2 +java.lang.ClassValue$ClassValueMap +java.io.Externalizable +java.io.ObjectStreamClass$2 +jdk.internal.reflect.UnsafeFieldAccessorFactory +jdk.internal.reflect.UnsafeQualifiedStaticFieldAccessorImpl +jdk.internal.reflect.UnsafeQualifiedStaticLongFieldAccessorImpl +java.util.ComparableTimSort +jdk.internal.reflect.SerializationConstructorAccessorImpl +jdk.internal.reflect.GeneratedSerializationConstructorAccessor1 +java.io.ObjectOutput +java.io.ObjectStreamConstants +java.io.ObjectOutputStream +java.io.ObjectInput +java.io.ObjectInputStream +java.lang.Class$$Lambda$346/0x00007f420407a758 +java.util.stream.Collectors$$Lambda$31/0x800000042 +java.util.stream.Collectors$$Lambda$23/0x80000003a +java.util.stream.Collectors$$Lambda$26/0x80000003d +java.util.stream.Collectors$$Lambda$28/0x80000003f +java.lang.CloneNotSupportedException +java.io.ClassCache$CacheRef +java.io.ObjectStreamClass$FieldReflectorKey +java.io.ObjectStreamClass$FieldReflector +org.junit.platform.launcher.TestIdentifier$$Lambda$351/0x00007f42040b3c20 +org.junit.platform.launcher.TestPlan$$Lambda$352/0x00007f42040b6000 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$353/0x00007f42040b6240 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$354/0x00007f42040b6478 +software.amazon.lambda.powertools.kafka.PowertoolsSerializerTest +org.junit.jupiter.api.extension.ParameterResolver +org.mockito.junit.jupiter.MockitoExtension +software.amazon.lambda.powertools.kafka.PowertoolsSerializerTest$1 +org.apache.avro.generic.GenericContainer +org.apache.avro.generic.IndexedRecord +org.apache.avro.specific.SpecificRecord +software.amazon.lambda.powertools.kafka.PowertoolsSerializerTest$InputType +com.fasterxml.jackson.core.JacksonException +com.fasterxml.jackson.core.JsonProcessingException +software.amazon.lambda.powertools.kafka.serializers.PowertoolsDeserializer +software.amazon.lambda.powertools.kafka.serializers.LambdaDefaultDeserializer +org.junit.jupiter.params.ParameterizedTest +org.junit.jupiter.params.ArgumentCountValidationMode +jdk.proxy2.$Proxy12 +org.junit.jupiter.params.provider.MethodSource +org.junit.jupiter.params.provider.ArgumentsSource +jdk.proxy2.$Proxy13 +jdk.proxy2.$Proxy14 +org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider +org.junit.jupiter.params.ParameterizedInvocationContextProvider +org.junit.jupiter.params.ParameterizedTestExtension +org.junit.jupiter.params.provider.MethodSources +org.junit.jupiter.params.provider.ArgumentsProvider +org.junit.jupiter.params.support.AnnotationConsumer +org.junit.jupiter.params.provider.AnnotationBasedArgumentsProvider +org.junit.jupiter.params.provider.MethodArgumentsProvider +jdk.proxy2.$Proxy15 +org.junit.jupiter.params.provider.ArgumentsSources +jdk.internal.reflect.GeneratedConstructorAccessor6 +jdk.internal.reflect.GeneratedConstructorAccessor7 +org.junit.platform.commons.util.ClassUtils$$Lambda$355/0x00007f42040b9840 +java.util.function.BiPredicate +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter$WithoutIndexFiltering +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter$Mode +software.amazon.lambda.powertools.kafka.DeserializationTest +software.amazon.lambda.powertools.kafka.DeserializationTypeTest +software.amazon.lambda.powertools.kafka.serializers.KafkaJsonDeserializerTest +jdk.proxy2.$Proxy16 +com.google.protobuf.MessageLiteOrBuilder +com.google.protobuf.MessageOrBuilder +software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProductOrBuilder +com.google.protobuf.MessageLite +com.google.protobuf.Message +com.google.protobuf.AbstractMessageLite +com.google.protobuf.AbstractMessage +com.google.protobuf.GeneratedMessage +software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct +com.google.protobuf.CheckReturnValue +com.google.protobuf.$Proxy17 +com.google.protobuf.MessageLite$Builder +com.google.protobuf.Internal$ProtobufList +com.google.protobuf.GeneratedMessage$ExtensionDescriptorRetriever +com.google.protobuf.InvalidProtocolBufferException +com.google.protobuf.Reader +com.google.protobuf.Internal$IntList +com.google.protobuf.Internal$LongList +com.google.protobuf.Internal$FloatList +com.google.protobuf.Internal$DoubleList +com.google.protobuf.Internal$BooleanList +com.google.protobuf.MapFieldReflectionAccessor +com.google.protobuf.MutabilityOracle +com.google.protobuf.MapField +com.google.protobuf.Parser +com.google.protobuf.Message$Builder +com.google.protobuf.Descriptors$GenericDescriptor +com.google.protobuf.Descriptors$Descriptor +com.google.protobuf.ByteOutput +com.google.protobuf.CodedOutputStream +com.google.protobuf.ExtensionRegistryLite +com.google.protobuf.CodedInputStream +com.google.protobuf.ByteString +com.google.protobuf.AbstractMessageLite$Builder +com.google.protobuf.AbstractMessage$Builder +com.google.protobuf.GeneratedMessage$Builder +software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct$Builder +com.google.protobuf.FieldSet$FieldDescriptorLite +com.google.protobuf.Descriptors$FieldDescriptor +com.google.protobuf.ExtensionLite +com.google.protobuf.Extension +com.google.protobuf.GeneratedMessage$GeneratedExtension +com.google.protobuf.UnknownFieldSet +com.google.protobuf.Descriptors$OneofDescriptor +com.google.protobuf.AbstractMessage$BuilderParent +com.google.protobuf.GeneratedMessage$FieldAccessorTable +java.lang.reflect.Executable$$Lambda$356/0x00007f420407b2b8 +java.lang.reflect.Executable$$Lambda$357/0x00007f420407b4f8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$358/0x00007f42040c4030 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$359/0x00007f42040c4270 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$360/0x00007f42040c44b0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$361/0x00007f42040c4708 +com.google.protobuf.GeneratedMessage$UnusedPrivateParameter +java.io.ObjectStreamException +com.google.protobuf.UnknownFieldSet$Builder +com.google.protobuf.MapEntry +java.lang.Deprecated +jdk.proxy1.$Proxy18 +com.google.protobuf.UninitializedMessageException +com.google.protobuf.Schema +org.junit.platform.commons.util.ReflectionUtils$$Lambda$362/0x00007f42040c5760 +com.google.protobuf.GeneratedMessage$CachedDescriptorRetriever +com.google.protobuf.GeneratedMessage$ExtendableMessageOrBuilder +com.google.protobuf.GeneratedMessage$ExtendableBuilder +com.google.protobuf.GeneratedMessage$ExtendableMessage +com.google.protobuf.AbstractMessageLite$InternalOneOfEnum +java.lang.invoke.LambdaForm$DMH/0x00007f42040c8000 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$363/0x00007f42040c6f68 +software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProductOuterClass +com.google.protobuf.ExtensionRegistry +com.google.protobuf.Descriptors$FileDescriptor +org.apache.avro.generic.GenericRecord +org.apache.avro.specific.SpecificRecordBase +software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct +org.apache.avro.specific.AvroGenerated +jdk.proxy2.$Proxy19 +org.apache.avro.AvroRuntimeException +org.apache.avro.io.Encoder +org.apache.avro.io.BinaryEncoder +org.apache.avro.io.Decoder +org.apache.avro.io.BinaryDecoder +org.apache.avro.generic.GenericData +org.apache.avro.specific.SpecificData +org.apache.avro.message.SchemaStore +org.apache.avro.message.MessageDecoder +org.apache.avro.message.MessageDecoder$BaseDecoder +org.apache.avro.message.BinaryMessageDecoder +org.apache.avro.JsonProperties +org.apache.avro.Schema +org.apache.avro.message.MessageEncoder +org.apache.avro.message.BinaryMessageEncoder +org.apache.avro.data.RecordBuilder +org.apache.avro.data.RecordBuilderBase +org.apache.avro.specific.SpecificRecordBuilderBase +software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct$Builder +org.apache.avro.io.parsing.Parser$ActionHandler +org.apache.avro.io.parsing.SkipParser$SkipHandler +org.apache.avro.io.ParsingDecoder +org.apache.avro.io.ValidatingDecoder +org.apache.avro.io.ResolvingDecoder +org.apache.avro.Conversion +software.amazon.lambda.powertools.kafka.serializers.KafkaProtobufDeserializerTest +software.amazon.lambda.powertools.kafka.serializers.KafkaAvroDeserializerTest +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$InputType +jdk.internal.reflect.GeneratedConstructorAccessor8 +jdk.internal.reflect.GeneratedConstructorAccessor9 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializer +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$TestDeserializer +software.amazon.lambda.powertools.kafka.testutils.TestProductPojo +software.amazon.lambda.powertools.kafka.testutils.TestUtils +org.apache.avro.io.DatumWriter +org.apache.maven.surefire.api.util.TestsToRun +org.apache.maven.surefire.api.util.DefaultRunOrderCalculator +java.util.random.RandomGenerator +java.util.Random +org.apache.maven.surefire.api.util.CloseableIterator +org.apache.maven.surefire.api.util.TestsToRun$ClassesIterator +java.util.NoSuchElementException +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$364/0x00007f42040c9738 +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$365/0x00007f42040c9970 +org.junit.platform.launcher.core.InterceptingLauncher$$Lambda$366/0x00007f42040c9ba8 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$367/0x00007f42040c8c00 +org.junit.platform.launcher.core.CompositeTestExecutionListener$EagerTestExecutionListener +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$368/0x00007f42040d0000 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$369/0x00007f42040d0258 +org.junit.platform.engine.reporting.ReportEntry +java.lang.invoke.LambdaForm$DMH/0x00007f42040d4000 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$370/0x00007f42040d06b0 +org.junit.platform.launcher.core.StreamInterceptingTestExecutionListener +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$371/0x00007f42040d0bc0 +org.junit.platform.engine.EngineExecutionListener$1 +org.junit.platform.launcher.core.IterationOrder +org.junit.platform.launcher.core.IterationOrder$1 +org.junit.platform.launcher.core.IterationOrder$2 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$372/0x00007f42040d1958 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$373/0x00007f42040d1b90 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$374/0x00007f42040d1db8 +org.junit.platform.launcher.core.CompositeEngineExecutionListener +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$375/0x00007f42040d2270 +org.junit.platform.launcher.core.ExecutionListenerAdapter +org.junit.platform.launcher.core.DelegatingEngineExecutionListener +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$376/0x00007f42040d2c30 +org.junit.platform.launcher.core.OutcomeDelayingEngineExecutionListener +org.junit.platform.engine.ExecutionRequest +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$377/0x00007f42040d3330 +org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext +org.junit.jupiter.engine.descriptor.LauncherStoreFacade +org.junit.jupiter.api.extension.ExtensionContext$Store +org.junit.jupiter.engine.descriptor.LauncherStoreFacade$$Lambda$378/0x00007f42040d6000 +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext$State +org.junit.platform.engine.support.hierarchical.ThrowableCollector$Factory +org.junit.platform.engine.support.hierarchical.ThrowableCollector +org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector +org.junit.jupiter.engine.JupiterTestEngine$$Lambda$379/0x00007f42040d6cb8 +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService$TestTask +org.junit.platform.engine.support.hierarchical.NodeTreeWalker +org.junit.platform.engine.support.hierarchical.LockManager +org.junit.platform.engine.support.hierarchical.ResourceLock +java.util.concurrent.locks.ReadWriteLock +org.junit.platform.engine.support.hierarchical.SingleLock +org.junit.platform.engine.support.hierarchical.ExclusiveResource +org.junit.platform.engine.support.hierarchical.ExclusiveResource$LockMode +org.junit.platform.engine.support.hierarchical.ExclusiveResource$$Lambda$380/0x00007f42040d5248 +org.junit.platform.engine.support.hierarchical.ExclusiveResource$$Lambda$381/0x00007f42040d5488 +java.util.Comparator$$Lambda$382/0x00007f420407c258 +java.lang.invoke.LambdaForm$DMH/0x00007f42040d4400 +java.util.Comparator$$Lambda$383/0x00007f420407c4f8 +org.junit.platform.engine.support.hierarchical.ExclusiveResource$$Lambda$384/0x00007f42040d56c8 +org.junit.platform.engine.support.hierarchical.LockManager$$Lambda$385/0x00007f42040d5908 +java.util.concurrent.locks.ReentrantReadWriteLock +java.util.concurrent.locks.ReentrantReadWriteLock$Sync +java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync +java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter +java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock +java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock +org.junit.platform.commons.util.CollectionUtils$$Lambda$386/0x00007f42040d5b48 +org.junit.platform.engine.support.hierarchical.NodeUtils +org.junit.platform.engine.support.hierarchical.NodeUtils$1 +org.junit.platform.engine.support.hierarchical.NodeExecutionAdvisor +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$387/0x00007f42040d4d00 +org.junit.platform.engine.support.hierarchical.NopLock +org.junit.jupiter.api.parallel.ResourceLocksProvider +org.junit.jupiter.engine.descriptor.ClassTestDescriptor$$Lambda$388/0x00007f42040d8490 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$389/0x00007f42040d86d8 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$390/0x00007f42040d8910 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$391/0x00007f42040d8b38 +org.junit.jupiter.engine.descriptor.ResourceLockAware$1 +java.util.ArrayDeque$DeqSpliterator +org.junit.jupiter.engine.descriptor.ResourceLockAware$$Lambda$392/0x00007f42040d8fc8 +org.junit.jupiter.engine.descriptor.ResourceLockAware$$Lambda$393/0x00007f42040d9208 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$DefaultExclusiveResourceCollector$$Lambda$394/0x00007f42040d9450 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$DefaultExclusiveResourceCollector$$Lambda$395/0x00007f42040d96a0 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$DefaultExclusiveResourceCollector$$Lambda$396/0x00007f42040d98f8 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$DefaultExclusiveResourceCollector$$Lambda$397/0x00007f42040d9b38 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$DefaultExclusiveResourceCollector$$Lambda$398/0x00007f42040d9d78 +org.junit.jupiter.engine.descriptor.ResourceLockAware$$Lambda$399/0x00007f42040d9fb8 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$2 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$400/0x00007f42040da400 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$401/0x00007f42040da848 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$402/0x00007f42040daa80 +org.junit.platform.engine.support.hierarchical.NodeTestTaskContext +org.junit.platform.engine.support.hierarchical.NodeTestTask +org.junit.platform.engine.support.hierarchical.Node$DynamicTestExecutor +java.lang.invoke.LambdaForm$DMH/0x00007f42040dc000 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$403/0x00007f42040db348 +org.opentest4j.IncompleteExecutionException +org.opentest4j.TestAbortedException +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector$$Lambda$404/0x00007f42040dba28 +org.junit.platform.commons.util.UnrecoverableExceptions +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector$$Lambda$405/0x00007f42040de000 +org.junit.platform.engine.support.hierarchical.ThrowableCollector$Executable +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$406/0x00007f42040de420 +org.junit.jupiter.engine.extension.MutableExtensionRegistry +org.junit.jupiter.engine.extension.MutableExtensionRegistry$Entry +org.junit.jupiter.api.extension.ExecutionCondition +org.junit.jupiter.engine.extension.DisabledCondition +org.junit.jupiter.api.extension.TestInstancePreDestroyCallback +org.junit.jupiter.engine.extension.AutoCloseExtension +org.junit.jupiter.engine.extension.TimeoutExtension +org.junit.jupiter.api.Timeout +org.junit.jupiter.api.extension.ExtensionContext$Namespace +org.junit.jupiter.engine.extension.RepeatedTestExtension +org.junit.jupiter.api.extension.TestTemplateInvocationContext +org.junit.jupiter.engine.extension.TestInfoParameterResolver +org.junit.jupiter.api.TestInfo +org.junit.jupiter.engine.extension.TestReporterParameterResolver +org.junit.jupiter.api.TestReporter +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$407/0x00007f42040ddac0 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$408/0x00007f42040ddcf8 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$409/0x00007f42040dc800 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$Entry$$Lambda$410/0x00007f42040dca28 +org.junit.jupiter.engine.extension.TempDirectory +org.junit.jupiter.api.extension.AnnotatedElementContext +org.junit.jupiter.engine.extension.TempDirectory$Scope +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$411/0x00007f42040e0248 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$412/0x00007f42040e0490 +org.junit.jupiter.engine.extension.ExtensionContextInternal +org.junit.jupiter.engine.descriptor.AbstractExtensionContext +org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext +org.junit.jupiter.api.extension.ExecutableInvoker +org.junit.jupiter.engine.execution.DefaultExecutableInvoker +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$413/0x00007f42040e14f8 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$414/0x00007f42040e1738 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$415/0x00007f42040e1958 +org.junit.jupiter.engine.execution.NamespaceAwareStore +org.junit.jupiter.api.extension.ExtensionContextException +org.junit.platform.engine.support.store.Namespace +java.lang.invoke.LambdaForm$DMH/0x00007f42040e4000 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$416/0x00007f42040e22c0 +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext$Builder +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$417/0x00007f42040e2720 +org.junit.platform.engine.support.hierarchical.Node$SkipResult +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$418/0x00007f42040e2b68 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$419/0x00007f42040e2da0 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$420/0x00007f42040e2fc8 +org.junit.platform.launcher.TestPlan$$Lambda$421/0x00007f42040e3200 +org.junit.platform.launcher.TestPlan$$Lambda$422/0x00007f42040e3420 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$423/0x00007f42040e3648 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$424/0x00007f42040e3880 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$425/0x00007f42040e3aa8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$426/0x00007f42040e3ce0 +org.junit.platform.engine.UniqueIdFormat$$Lambda$427/0x00007f42040e6000 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$428/0x00007f42040e6248 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$429/0x00007f42040e64a0 +org.junit.platform.engine.support.hierarchical.Node$Invocation +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$430/0x00007f42040e68c8 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$431/0x00007f42040e6af0 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$432/0x00007f42040e6d18 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$433/0x00007f42040e6f60 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor +java.util.concurrent.CancellationException +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$434/0x00007f42040e73d0 +org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService$$Lambda$435/0x00007f42040e7608 +org.junit.jupiter.engine.descriptor.ExtensionUtils +java.util.function.ToIntFunction +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$436/0x00007f42040e7a40 +java.util.Comparator$$Lambda$437/0x00007f420407df18 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$438/0x00007f42040e7c60 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$439/0x00007f42040e5000 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$440/0x00007f42040e5240 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$LateInitEntry +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$441/0x00007f42040e56c8 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$442/0x00007f42040e5900 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$443/0x00007f42040e5b50 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$444/0x00007f42040e4800 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$445/0x00007f42040e4a50 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$446/0x00007f42040e4c70 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$447/0x00007f42040e4400 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$448/0x00007f42040e8000 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$449/0x00007f42040e8258 +java.util.stream.SortedOps +java.util.stream.SortedOps$OfRef +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$450/0x00007f42040e8478 +java.util.stream.SortedOps$AbstractRefSortingSink +java.util.stream.SortedOps$RefSortingSink +java.util.stream.SortedOps$RefSortingSink$$Lambda$451/0x00007f420407ee38 +org.junit.jupiter.api.extension.TestInstanceFactory +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$452/0x00007f42040e86b0 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$453/0x00007f42040e88f0 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$454/0x00007f42040e8b48 +org.junit.jupiter.engine.extension.ExtensionRegistry$$Lambda$455/0x00007f42040e8d90 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$456/0x00007f42040e8fb0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$457/0x00007f42040e9200 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$458/0x00007f42040e9420 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$459/0x00007f42040e9648 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$460/0x00007f42040e9890 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$461/0x00007f42040e9ad0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$462/0x00007f42040e9d08 +org.junit.jupiter.engine.execution.BeforeEachMethodAdapter +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$463/0x00007f42040ea140 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$464/0x00007f42040ea388 +org.junit.jupiter.engine.execution.AfterEachMethodAdapter +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$465/0x00007f42040ea7c0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$466/0x00007f42040eaa08 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$467/0x00007f42040eac40 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$468/0x00007f42040eae90 +org.junit.jupiter.engine.descriptor.ClassExtensionContext +org.junit.jupiter.engine.execution.TestInstancesProvider +org.junit.jupiter.api.extension.TestInstances +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$469/0x00007f42040eb9a8 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$470/0x00007f42040ebbe0 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$471/0x00007f42040ebe28 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$FilterType +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$472/0x00007f42040ec4a8 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$473/0x00007f42040ec6f8 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$474/0x00007f42040ec938 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$475/0x00007f42040ecb80 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$476/0x00007f42040ecdd0 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$477/0x00007f42040ed018 +org.junit.jupiter.api.Disabled +org.junit.jupiter.engine.extension.DisabledCondition$$Lambda$478/0x00007f42040ed468 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$479/0x00007f42040ed6b0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$480/0x00007f42040ed8d8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$481/0x00007f42040edb30 +org.junit.platform.launcher.TestIdentifier$$Lambda$482/0x00007f42040edd88 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$483/0x00007f42040edfc8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$484/0x00007f42040ee210 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$485/0x00007f42040ee458 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$486/0x00007f42040ee678 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$487/0x00007f42040ee8c8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$488/0x00007f42040eeb08 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$489/0x00007f42040eed60 +org.apache.maven.surefire.api.report.SimpleReportEntry +org.apache.maven.surefire.api.util.internal.ClassMethod +org.apache.maven.surefire.report.ClassMethodIndexer$$Lambda$490/0x00007f42040ef4b8 +org.apache.maven.surefire.api.util.internal.ImmutableMap +org.apache.maven.surefire.booter.spi.EventChannelEncoder$StackTrace +java.lang.StrictMath +java.nio.StringCharBuffer +org.junit.jupiter.engine.descriptor.CallbackSupport$CallbackInvoker +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$491/0x00007f42040f0200 +org.junit.jupiter.engine.descriptor.CallbackSupport +org.junit.jupiter.engine.descriptor.CallbackSupport$$Lambda$492/0x00007f42040f0628 +org.junit.jupiter.engine.extension.TimeoutDuration +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$493/0x00007f42040f0a78 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$494/0x00007f42040f0cb8 +org.junit.jupiter.api.Timeout$ThreadMode +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$495/0x00007f42040f1138 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$496/0x00007f42040f1378 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$497/0x00007f42040f15b0 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$498/0x00007f42040f1808 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$499/0x00007f42040f1a40 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$500/0x00007f42040f1c90 +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$501/0x00007f42040f1ed8 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CompositeKey +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$StoredValue +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$502/0x00007f42040f2520 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$MemoizingSupplier +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$503/0x00007f42040f2998 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$504/0x00007f42040f2bc0 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$MemoizingSupplier$Failure +org.junit.jupiter.api.io.TempDir +org.junit.platform.commons.util.AnnotationUtils$$Lambda$505/0x00007f42040f3410 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$506/0x00007f42040f3668 +org.junit.jupiter.engine.descriptor.ClassExtensionContext$$Lambda$507/0x00007f42040f38a0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$508/0x00007f42040f3ae0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$509/0x00007f42040f3d20 +java.util.stream.Nodes$ArrayNode +java.util.stream.Nodes$FixedNodeBuilder +org.junit.jupiter.engine.descriptor.MethodExtensionContext +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$510/0x00007f42040f4420 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$511/0x00007f42040f4648 +org.junit.jupiter.engine.execution.ExtensionContextSupplier +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$512/0x00007f42040f4a70 +org.junit.jupiter.engine.execution.ExtensionContextSupplier$ScopeBasedExtensionContextSupplier +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$513/0x00007f42040f50e0 +org.junit.jupiter.engine.descriptor.DefaultTestInstanceFactoryContext +org.junit.jupiter.api.extension.TestInstancePreConstructCallback +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$514/0x00007f42040f5760 +java.lang.invoke.LambdaForm$DMH/0x00007f42040f8000 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$515/0x00007f42040f5998 +org.junit.jupiter.engine.execution.ParameterResolutionUtils +org.junit.jupiter.api.extension.ParameterContext +org.junit.jupiter.api.extension.ParameterResolutionException +org.junit.jupiter.engine.execution.ConstructorInvocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptorCall +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$$Lambda$516/0x00007f42040f66b8 +org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation +org.junit.jupiter.engine.execution.DefaultTestInstances +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$517/0x00007f42040f6fc8 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$518/0x00007f42040f7210 +org.junit.jupiter.api.extension.TestInstancePostProcessor +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$519/0x00007f42040f7638 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$520/0x00007f42040f7870 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$521/0x00007f42040f7ab8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$522/0x00007f42040f7d08 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$523/0x00007f42040fc000 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$524/0x00007f42040fc248 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$525/0x00007f42040fc488 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$526/0x00007f42040fc6d8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$527/0x00007f42040fc918 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$528/0x00007f42040fcb70 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$529/0x00007f42040fcdc0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$530/0x00007f42040fd000 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$531/0x00007f42040fd250 +org.junit.jupiter.api.extension.ExtensionContext$Store$CloseableResource +org.junit.jupiter.engine.extension.TempDirectory$FailureTracker +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$532/0x00007f42040fd8b8 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$533/0x00007f42040fdae0 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$534/0x00007f42040fdd08 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$535/0x00007f42040fdf40 +sun.reflect.generics.repository.AbstractRepository +sun.reflect.generics.repository.GenericDeclRepository +sun.reflect.generics.repository.ClassRepository +java.lang.reflect.TypeVariable +sun.reflect.generics.tree.FormalTypeParameter +sun.reflect.generics.tree.Signature +sun.reflect.generics.tree.ClassSignature +org.junitpioneer.jupiter.ClearEnvironmentVariable +org.junitpioneer.jupiter.RestoreEnvironmentVariables +java.lang.reflect.ParameterizedType +sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl +sun.reflect.generics.reflectiveObjects.LazyReflectiveObjectGenerator +sun.reflect.generics.reflectiveObjects.TypeVariableImpl +org.junitpioneer.internal.PioneerAnnotationUtils +java.lang.invoke.LambdaForm$DMH/0x00007f42040f8400 +org.junitpioneer.internal.PioneerAnnotationUtils$$Lambda$536/0x00007f42040fe798 +java.lang.invoke.LambdaForm$DMH/0x00007f42040f8800 +java.util.stream.Collectors$$Lambda$537/0x00007f42040f97c0 +java.util.stream.Collectors$$Lambda$538/0x00007f42040f99e0 +java.util.stream.Collectors$$Lambda$539/0x00007f42040f9c10 +java.util.stream.Collectors$$Lambda$540/0x00007f42040f8c00 +java.util.ImmutableCollections$Access +jdk.internal.access.JavaUtilCollectionAccess +java.util.ImmutableCollections$Access$1 +sun.reflect.annotation.AnnotationSupport +org.junitpioneer.internal.PioneerAnnotationUtils$$Lambda$541/0x00007f42040febd8 +org.junitpioneer.internal.PioneerAnnotationUtils$$Lambda$542/0x00007f42040fee20 +java.util.AbstractList$RandomAccessSpliterator +java.lang.invoke.MethodHandleImpl$BindCaller +java.lang.invoke.MethodHandleImpl$BindCaller$1 +java.lang.invoke.LambdaForm$MH/0x00007f4204140000 +java.lang.invoke.LambdaForm$MH/0x00007f4204140400 +java.lang.invoke.MethodHandleImpl$CasesHolder +java.lang.invoke.MethodHandleImpl$LoopClauses +java.lang.invoke.MethodHandleImpl$ArrayAccess +java.lang.invoke.MethodHandleImpl$2 +java.lang.invoke.MethodHandleImpl$ArrayAccessor +java.lang.invoke.MethodHandleImpl$ArrayAccessor$1 +java.lang.invoke.LambdaForm$DMH/0x00007f4204140800 +java.lang.invoke.LambdaForm$DMH/0x00007f4204140c00 +java.lang.invoke.LambdaForm$DMH/0x00007f4204141000 +java.lang.invoke.LambdaForm$MH/0x00007f4204141400 +java.lang.invoke.LambdaForm$MH/0x00007f4204141800 +org.junitpioneer.internal.PioneerAnnotationUtils$$InjectedInvoker/0x00007f4204141c00 +java.util.Collections$CopiesList +java.lang.invoke.LambdaForm$MH/0x00007f4204142000 +java.lang.invoke.BoundMethodHandle$Species_LLL +java.lang.invoke.LambdaForm$MH/0x00007f4204142400 +java.lang.invoke.LambdaForm$MH/0x00007f4204142800 +java.lang.invoke.LambdaForm$MH/0x00007f4204142c00 +java.lang.invoke.BoundMethodHandle$Species_LLLL +java.lang.invoke.LambdaForm$MH/0x00007f4204143000 +java.lang.invoke.MethodHandleImpl$WrappedMember +org.junitpioneer.internal.PioneerAnnotationUtils$$Lambda$543/0x00007f42040ff060 +org.junitpioneer.internal.PioneerUtils +org.junitpioneer.internal.PioneerUtils$$Lambda$544/0x00007f42040ff4a8 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$545/0x00007f42040ff6e8 +java.lang.invoke.LambdaForm$DMH/0x00007f4204143400 +java.lang.invoke.LambdaForm$DMH/0x00007f4204143800 +java.lang.invoke.LambdaForm$MH/0x00007f4204143c00 +java.lang.invoke.LambdaForm$DMH/0x00007f4204144000 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$546/0x00007f42040ff920 +java.lang.invoke.LambdaForm$DMH/0x00007f4204144400 +java.lang.invoke.LambdaForm$DMH/0x00007f4204144800 +java.lang.invoke.LambdaForm$MH/0x00007f4204144c00 +org.junitpioneer.jupiter.ClearEnvironmentVariable$ClearEnvironmentVariables +org.junitpioneer.jupiter.EnvironmentVariableExtension$$Lambda$547/0x00007f42040ffd58 +org.junitpioneer.internal.PioneerUtils$$Lambda$548/0x00007f4204146000 +org.junitpioneer.internal.PioneerUtils$$Lambda$549/0x00007f4204146220 +org.junitpioneer.internal.PioneerUtils$$Lambda$550/0x00007f4204146450 +org.junitpioneer.jupiter.EnvironmentVariableExtension$$Lambda$551/0x00007f4204146698 +org.junitpioneer.jupiter.EnvironmentVariableExtension$$Lambda$552/0x00007f42041468d8 +java.util.stream.Collectors$$Lambda$553/0x00007f4204100e30 +java.util.stream.Collectors$$Lambda$554/0x00007f4204101050 +java.util.stream.Collectors$$Lambda$555/0x00007f4204101288 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$556/0x00007f4204146b18 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$557/0x00007f4204146d70 +java.time.temporal.TemporalAccessor +java.time.temporal.Temporal +java.time.temporal.TemporalAdjuster +java.time.chrono.ChronoLocalDateTime +java.time.LocalDateTime +java.time.chrono.ChronoLocalDate +java.time.LocalDate +java.time.temporal.TemporalField +java.time.temporal.ChronoField +java.time.temporal.ValueRange +java.time.LocalTime +java.time.InstantSource +java.time.Clock +java.time.Clock$SystemClock +java.time.ZoneId +java.time.ZoneOffset +java.util.TimeZone +sun.util.calendar.ZoneInfo +sun.util.calendar.ZoneInfoFile +sun.util.calendar.ZoneInfoFile$1 +java.io.DataInputStream +sun.util.calendar.ZoneInfoFile$ZoneOffsetTransitionRule +sun.util.calendar.ZoneInfoFile$Checksum +java.time.ZoneRegion +java.time.zone.ZoneRulesProvider +java.time.zone.ZoneRulesProvider$1 +java.time.zone.TzdbZoneRulesProvider +java.time.zone.Ser +java.time.zone.ZoneRules +java.time.zone.ZoneOffsetTransitionRule +java.time.zone.ZoneOffsetTransition +java.time.Instant +org.junit.platform.engine.reporting.ReportEntry$$Lambda$558/0x00007f4204146fb0 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$559/0x00007f42041471e8 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$560/0x00007f4204147420 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$561/0x00007f4204147648 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$562/0x00007f4204147880 +org.apache.maven.surefire.api.report.TestOutputReportEntry +java.lang.invoke.LambdaForm$MH/0x00007f4204145000 +java.lang.invoke.LambdaForm$MH/0x00007f4204145400 +java.lang.invoke.LambdaForm$MH/0x00007f4204145800 +java.lang.invoke.LambdaForm$MH/0x00007f4204145c00 +java.lang.invoke.LambdaForm$MH/0x00007f4204148000 +java.lang.invoke.BoundMethodHandle$Species_LLLLL +java.lang.invoke.LambdaForm$MH/0x00007f4204148400 +java.lang.invoke.BoundMethodHandle$Species_LLLLLL +java.lang.invoke.LambdaForm$MH/0x00007f4204148800 +java.lang.invoke.BoundMethodHandle$Species_LLLLLLL +java.lang.invoke.LambdaForm$MH/0x00007f4204148c00 +java.lang.invoke.MethodHandles$1 +java.lang.invoke.BoundMethodHandle$Species_LJ +java.lang.invoke.LambdaForm$MH/0x00007f4204149000 +java.lang.invoke.BoundMethodHandle$Species_LLLLLLLL +java.lang.invoke.LambdaForm$MH/0x00007f4204149400 +java.lang.invoke.BoundMethodHandle$Species_LLLLLLLLL +java.lang.invoke.LambdaFormEditor$1 +java.util.TreeMap$EntrySet +java.util.TreeMap$EntryIterator +java.lang.invoke.LambdaForm$MH/0x00007f4204149800 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup$$Lambda$563/0x00007f420414c000 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$564/0x00007f420414c238 +java.lang.invoke.LambdaForm$DMH/0x00007f4204149c00 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$565/0x00007f420414c470 +org.junitpioneer.jupiter.EnvironmentVariableUtils +java.lang.reflect.InaccessibleObjectException +org.junitpioneer.jupiter.EnvironmentVariableUtils$$Lambda$566/0x00007f420414c8b0 +jdk.internal.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl +jdk.internal.reflect.UnsafeQualifiedFieldAccessorImpl +jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$567/0x00007f420414cae8 +org.junit.jupiter.api.extension.BeforeTestExecutionCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$568/0x00007f420414cf10 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$569/0x00007f420414d130 +org.junit.jupiter.engine.descriptor.MethodExtensionContext$$Lambda$570/0x00007f420414d358 +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$571/0x00007f420414d598 +org.junit.jupiter.engine.execution.MethodInvocation +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$$Lambda$572/0x00007f420414da58 +org.junit.jupiter.engine.extension.TimeoutExtension$TimeoutProvider +org.junit.jupiter.engine.extension.TimeoutConfiguration +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$573/0x00007f420414e0d0 +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$574/0x00007f420414e328 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$575/0x00007f420414e550 +org.junit.jupiter.engine.extension.TimeoutDurationParser +java.time.DateTimeException +java.time.format.DateTimeParseException +java.util.regex.Pattern$$Lambda$576/0x00007f4204104db8 +java.lang.CharacterData00 +java.util.regex.Pattern$$Lambda$577/0x00007f4204105408 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$578/0x00007f420414e9a8 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$579/0x00007f420414ebd0 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$580/0x00007f420414ee18 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$581/0x00007f420414f060 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$582/0x00007f420414f288 +software.amazon.lambda.powertools.kafka.internal.DeserializationUtils +org.slf4j.LoggerFactory +org.slf4j.spi.SLF4JServiceProvider +org.slf4j.event.LoggingEvent +java.lang.InstantiationException +java.util.ServiceConfigurationError +org.slf4j.helpers.SubstituteServiceProvider +org.slf4j.IMarkerFactory +org.slf4j.spi.MDCAdapter +org.slf4j.ILoggerFactory +org.slf4j.helpers.SubstituteLoggerFactory +org.slf4j.Logger +java.util.concurrent.LinkedBlockingQueue +java.util.concurrent.LinkedBlockingQueue$Node +org.slf4j.helpers.BasicMarkerFactory +org.slf4j.Marker +org.slf4j.helpers.BasicMDCAdapter +java.lang.InheritableThreadLocal +org.slf4j.helpers.BasicMDCAdapter$1 +org.slf4j.helpers.ThreadLocalMapOfStacks +org.slf4j.helpers.NOP_FallbackServiceProvider +org.slf4j.helpers.NOPLoggerFactory +org.slf4j.helpers.NOPMDCAdapter +org.slf4j.helpers.Util +org.slf4j.simple.SimpleServiceProvider +org.slf4j.MDC +org.slf4j.simple.SimpleLoggerFactory +org.slf4j.helpers.AbstractLogger +org.slf4j.helpers.LegacyAbstractLogger +org.slf4j.simple.SimpleLogger +org.slf4j.spi.LoggingEventBuilder +org.slf4j.simple.SimpleLoggerConfiguration +java.text.Format +java.text.DateFormat +java.text.SimpleDateFormat +org.slf4j.simple.SimpleLoggerConfiguration$$Lambda$583/0x00007f4204151ec0 +org.slf4j.simple.OutputChoice +org.slf4j.simple.OutputChoice$OutputChoiceType +java.text.AttributedCharacterIterator$Attribute +java.text.Format$Field +java.text.DateFormat$Field +java.util.Calendar +java.util.spi.LocaleServiceProvider +sun.util.spi.CalendarProvider +sun.util.locale.provider.LocaleProviderAdapter +sun.util.locale.provider.LocaleProviderAdapter$Type +sun.util.locale.provider.LocaleProviderAdapter$1 +sun.util.locale.provider.ResourceBundleBasedAdapter +sun.util.locale.provider.JRELocaleProviderAdapter +sun.util.cldr.CLDRLocaleProviderAdapter +sun.util.locale.provider.LocaleDataMetaInfo +sun.util.cldr.CLDRBaseLocaleDataMetaInfo +sun.util.locale.LanguageTag +sun.util.locale.ParseStatus +sun.util.locale.StringTokenIterator +sun.util.locale.InternalLocaleBuilder +sun.util.locale.InternalLocaleBuilder$CaseInsensitiveChar +sun.util.locale.BaseLocale$Key +sun.util.locale.LocaleObjectCache +sun.util.locale.BaseLocale$Cache +sun.util.locale.LocaleObjectCache$CacheEntry +java.util.Locale$Cache +sun.util.cldr.CLDRLocaleProviderAdapter$$Lambda$57/0x80000005e +jdk.internal.module.ModulePatcher$PatchedModuleReader +sun.net.www.protocol.jrt.Handler +sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$59/0x800000060 +sun.util.locale.provider.AvailableLanguageTags +sun.util.locale.provider.CalendarProviderImpl +java.util.Calendar$Builder +java.util.GregorianCalendar +sun.util.calendar.CalendarSystem +sun.util.calendar.CalendarSystem$GregorianHolder +sun.util.calendar.AbstractCalendar +sun.util.calendar.BaseCalendar +sun.util.calendar.Gregorian +sun.util.locale.provider.CalendarDataUtility +java.util.Locale$Builder +java.util.spi.CalendarDataProvider +sun.util.locale.provider.LocaleServiceProviderPool +java.text.spi.BreakIteratorProvider +java.text.spi.CollatorProvider +java.text.spi.DateFormatProvider +java.text.spi.DateFormatSymbolsProvider +java.text.spi.DecimalFormatSymbolsProvider +java.text.spi.NumberFormatProvider +java.util.spi.CurrencyNameProvider +java.util.spi.LocaleNameProvider +java.util.spi.TimeZoneNameProvider +sun.util.locale.provider.LocaleServiceProviderPool$LocalizedObjectGetter +sun.util.locale.provider.CalendarDataUtility$CalendarWeekParameterGetter +java.util.ResourceBundle$Control +java.util.ResourceBundle +java.util.ResourceBundle$Control$CandidateListCache +java.util.ResourceBundle$SingleFormatControl +java.util.ResourceBundle$NoFallbackControl +sun.util.cldr.CLDRLocaleProviderAdapter$$Lambda$58/0x80000005f +sun.util.locale.provider.CalendarDataProviderImpl +sun.util.cldr.CLDRCalendarDataProviderImpl +sun.util.locale.provider.LocaleResources +sun.util.resources.LocaleData +sun.util.resources.LocaleData$1 +sun.util.resources.Bundles$Strategy +sun.util.resources.LocaleData$LocaleDataStrategy +sun.util.resources.Bundles +sun.util.resources.Bundles$1 +jdk.internal.access.JavaUtilResourceBundleAccess +java.util.ResourceBundle$1 +java.util.ResourceBundle$2 +sun.util.resources.Bundles$CacheKey +java.util.ListResourceBundle +sun.util.resources.cldr.CalendarData +java.util.ResourceBundle$ResourceBundleProviderHelper +java.util.ResourceBundle$ResourceBundleProviderHelper$$Lambda$11/0x80000000f +sun.util.resources.Bundles$CacheKeyReference +sun.util.resources.Bundles$BundleReference +sun.util.locale.provider.LocaleResources$ResourceReference +sun.util.calendar.CalendarDate +sun.util.calendar.BaseCalendar$Date +sun.util.calendar.Gregorian$Date +sun.util.calendar.CalendarUtils +java.text.DateFormatSymbols +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$61/0x800000062 +sun.util.locale.provider.DateFormatSymbolsProviderImpl +sun.text.resources.cldr.FormatData +sun.text.resources.cldr.FormatData_en +java.text.NumberFormat +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$63/0x800000064 +sun.util.locale.provider.NumberFormatProviderImpl +java.text.DecimalFormatSymbols +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$62/0x800000063 +sun.util.locale.provider.DecimalFormatSymbolsProviderImpl +java.lang.StringLatin1$CharsSpliterator +java.util.stream.IntStream +java.util.stream.IntPipeline +java.util.stream.IntPipeline$Head +java.util.function.IntPredicate +java.text.DecimalFormatSymbols$$Lambda$7/0x80000000b +java.util.stream.IntPipeline$StatelessOp +java.util.stream.IntPipeline$10 +java.util.function.IntConsumer +java.util.stream.Sink$OfInt +java.util.stream.FindOps$FindSink$OfInt +java.util.OptionalInt +java.util.stream.FindOps$FindSink$OfInt$$Lambda$36/0x800000047 +java.util.stream.FindOps$FindSink$OfInt$$Lambda$34/0x800000045 +java.util.stream.FindOps$FindSink$OfInt$$Lambda$35/0x800000046 +java.util.stream.FindOps$FindSink$OfInt$$Lambda$33/0x800000044 +java.util.stream.Sink$ChainedInt +java.util.stream.IntPipeline$10$1 +java.lang.StringUTF16$CharsSpliterator +java.text.DecimalFormat +java.text.FieldPosition +java.text.DigitList +java.math.RoundingMode +java.util.Date +org.slf4j.helpers.Reporter +org.slf4j.helpers.Reporter$TargetChoice +org.slf4j.helpers.Reporter$Level +org.slf4j.simple.SimpleLoggerFactory$$Lambda$596/0x00007f42041531c0 +software.amazon.lambda.powertools.kafka.internal.DeserializationUtils$HandlerInfo +com.amazonaws.services.lambda.runtime.RequestHandler +software.amazon.lambda.powertools.kafka.testutils.AvroHandler +org.apache.kafka.clients.consumer.ConsumerRecords +com.amazonaws.services.lambda.runtime.Context +software.amazon.lambda.powertools.kafka.Deserialization +software.amazon.lambda.powertools.kafka.DeserializationType +jdk.proxy2.$Proxy20 +org.slf4j.event.Level +java.text.DontCareFieldPosition +java.text.Format$FieldDelegate +java.text.DontCareFieldPosition$1 +java.text.NumberFormat$Field +org.slf4j.helpers.MessageFormatter +org.slf4j.helpers.FormattingTuple +org.assertj.core.api.InstanceOfAssertFactories +org.assertj.core.api.Assertions +org.assertj.core.api.NumberAssert +org.assertj.core.api.ComparableAssert +org.assertj.core.api.Descriptable +org.assertj.core.api.ExtensionPoints +org.assertj.core.api.Assert +org.assertj.core.api.AbstractAssert +org.assertj.core.api.AbstractObjectAssert +org.assertj.core.api.AbstractComparableAssert +org.assertj.core.api.AbstractBigIntegerAssert +org.assertj.core.api.BigIntegerAssert +org.assertj.core.data.TemporalOffset +org.assertj.core.data.TemporalUnitOffset +org.assertj.core.data.TemporalUnitWithinOffset +org.assertj.core.data.TemporalUnitLessThanOffset +org.assertj.core.configuration.ConfigurationProvider +org.assertj.core.api.AssertionsForClassTypes +org.assertj.core.api.AssertionsForInterfaceTypes +org.assertj.core.api.EnumerableAssert +org.assertj.core.api.AbstractCharSequenceAssert +org.assertj.core.api.CharSequenceAssert +org.assertj.core.api.AbstractStringAssert +org.assertj.core.api.StringAssert +org.assertj.core.api.AbstractDateAssert +org.assertj.core.api.DateAssert +org.assertj.core.api.AbstractTemporalAssert +org.assertj.core.api.AbstractZonedDateTimeAssert +org.assertj.core.api.ZonedDateTimeAssert +org.assertj.core.api.AbstractShortAssert +org.assertj.core.api.ShortAssert +org.assertj.core.api.ArraySortedAssert +org.assertj.core.api.AbstractEnumerableAssert +org.assertj.core.api.AbstractArrayAssert +org.assertj.core.api.AbstractShortArrayAssert +org.assertj.core.api.ShortArrayAssert +org.assertj.core.api.AbstractYearMonthAssert +org.assertj.core.api.YearMonthAssert +org.assertj.core.api.AbstractInstantAssert +org.assertj.core.api.InstantAssert +org.assertj.core.api.AbstractDurationAssert +org.assertj.core.api.DurationAssert +org.assertj.core.api.AbstractPeriodAssert +org.assertj.core.api.PeriodAssert +org.assertj.core.api.AbstractThrowableAssert +org.assertj.core.api.ThrowableAssert +org.assertj.core.api.AbstractLocalDateTimeAssert +org.assertj.core.api.LocalDateTimeAssert +org.assertj.core.api.AbstractOffsetDateTimeAssert +org.assertj.core.api.OffsetDateTimeAssert +org.assertj.core.api.AbstractOffsetTimeAssert +org.assertj.core.api.OffsetTimeAssert +org.assertj.core.api.AbstractLocalTimeAssert +org.assertj.core.api.LocalTimeAssert +org.assertj.core.api.AbstractLocalDateAssert +org.assertj.core.api.LocalDateAssert +org.assertj.core.api.AbstractByteArrayAssert +org.assertj.core.api.ByteArrayAssert +org.assertj.core.api.AbstractByteAssert +org.assertj.core.api.ByteAssert +org.assertj.core.api.AbstractCharacterAssert +org.assertj.core.api.CharacterAssert +org.assertj.core.api.AbstractCharArrayAssert +org.assertj.core.api.CharArrayAssert +org.assertj.core.api.AbstractBooleanAssert +org.assertj.core.api.BooleanAssert +org.assertj.core.api.AbstractBigDecimalAssert +org.assertj.core.api.BigDecimalAssert +org.assertj.core.api.AbstractUriAssert +org.assertj.core.api.UriAssert +org.assertj.core.api.AbstractUrlAssert +org.assertj.core.api.UrlAssert +org.assertj.core.api.AbstractBooleanArrayAssert +org.assertj.core.api.BooleanArrayAssert +org.assertj.core.api.AbstractIntArrayAssert +org.assertj.core.api.IntArrayAssert +org.assertj.core.api.AbstractIntegerAssert +org.assertj.core.api.IntegerAssert +org.assertj.core.api.AbstractFloatArrayAssert +org.assertj.core.api.FloatArrayAssert +org.assertj.core.api.FloatingPointNumberAssert +org.assertj.core.api.AbstractFloatAssert +org.assertj.core.api.FloatAssert +org.assertj.core.api.AbstractLongArrayAssert +org.assertj.core.api.LongArrayAssert +org.assertj.core.api.AbstractLongAssert +org.assertj.core.api.LongAssert +org.assertj.core.api.AbstractDoubleArrayAssert +org.assertj.core.api.DoubleArrayAssert +org.assertj.core.api.AbstractDoubleAssert +org.assertj.core.api.DoubleAssert +org.assertj.core.api.AbstractFileAssert +org.assertj.core.api.FileAssert +org.assertj.core.api.AbstractInputStreamAssert +org.assertj.core.api.InputStreamAssert +org.assertj.core.api.GenericComparableAssert +org.assertj.core.api.AbstractUniversalComparableAssert +org.assertj.core.api.UniversalComparableAssert +org.assertj.core.description.Description +org.assertj.core.description.LazyTextDescription +org.assertj.core.description.TextDescription +org.assertj.core.api.AssertionInfo +org.assertj.core.internal.ComparisonStrategy +org.assertj.core.api.ObjectEnumerableAssert +org.assertj.core.api.IndexedObjectEnumerableAssert +org.assertj.core.api.AbstractIterableAssert +org.assertj.core.api.AbstractCollectionAssert +org.assertj.core.api.AbstractListAssert +org.assertj.core.api.FactoryBasedNavigableListAssert +org.assertj.core.api.ListAssert +org.assertj.core.api.ObjectAssert +org.assertj.core.internal.Objects +org.assertj.core.error.ErrorMessageFactory +org.assertj.core.util.introspection.IntrospectionError +org.assertj.core.internal.AbstractComparisonStrategy +org.assertj.core.internal.StandardComparisonStrategy +org.assertj.core.util.introspection.PropertySupport +org.assertj.core.internal.Failures +org.assertj.core.error.AssertionErrorCreator +org.assertj.core.util.Arrays +org.assertj.core.error.ConstructorInvoker +org.assertj.core.util.introspection.FieldSupport +org.assertj.core.error.GroupTypeDescription +org.assertj.core.internal.Conditions +org.assertj.core.api.WritableAssertionInfo +org.assertj.core.presentation.Representation +org.assertj.core.configuration.Configuration +org.assertj.core.configuration.PreferredAssumptionException +org.assertj.core.configuration.PreferredAssumptionException$1 +org.assertj.core.configuration.Services +org.assertj.core.util.Lists +org.assertj.core.util.Streams +org.assertj.core.util.Lists$$Lambda$597/0x00007f42041958b0 +org.assertj.core.presentation.CompositeRepresentation +java.lang.invoke.LambdaForm$DMH/0x00007f4204154800 +org.assertj.core.presentation.CompositeRepresentation$$Lambda$598/0x00007f4204195d28 +java.util.stream.SortedOps$SizedRefSortingSink +org.assertj.core.presentation.StandardRepresentation +java.time.chrono.ChronoZonedDateTime +java.time.ZonedDateTime +java.time.OffsetDateTime +java.nio.file.DirectoryStream +org.assertj.core.internal.Comparables +org.junit.jupiter.api.extension.AfterTestExecutionCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$599/0x00007f42041967b8 +org.junit.jupiter.engine.descriptor.CallbackSupport$$Lambda$600/0x00007f42041969d8 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$601/0x00007f4204196c10 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$602/0x00007f4204196e38 +org.junit.jupiter.engine.descriptor.CallbackSupport$$Lambda$603/0x00007f4204197058 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$604/0x00007f4204197280 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup$$Lambda$605/0x00007f42041974b8 +org.junitpioneer.jupiter.EnvironmentVariableUtils$$Lambda$606/0x00007f42041976f0 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup$$Lambda$607/0x00007f4204197928 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$608/0x00007f4204197b60 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$609/0x00007f4204197d88 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$610/0x00007f4204154c00 +org.junit.jupiter.engine.descriptor.MethodExtensionContext$$Lambda$611/0x00007f4204198000 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$612/0x00007f4204198240 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$613/0x00007f4204198460 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$614/0x00007f42041986b0 +org.junit.jupiter.api.extension.TestInstancePreDestroyCallback$$Lambda$615/0x00007f42041988e8 +org.junit.jupiter.api.extension.TestInstancePreDestroyCallback$$Lambda$616/0x00007f4204198b28 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$617/0x00007f4204198d60 +org.junit.jupiter.api.AutoClose +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$618/0x00007f42041991b0 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$619/0x00007f42041993e8 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$620/0x00007f4204199610 +java.util.concurrent.ConcurrentHashMap$EntrySpliterator +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$621/0x00007f4204199a70 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$622/0x00007f4204199cb0 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue$$Lambda$623/0x00007f4204199f00 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$624/0x00007f420419a140 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$625/0x00007f420419a378 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$626/0x00007f420419a5a0 +org.junit.platform.engine.TestExecutionResult +org.junit.platform.engine.TestExecutionResult$Status +org.junit.jupiter.api.extension.TestWatcher +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$627/0x00007f420419b048 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$628/0x00007f420419b280 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$629/0x00007f420419b4b8 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$630/0x00007f420419b6f8 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$631/0x00007f420419b948 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$632/0x00007f420419bb88 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$633/0x00007f420419bdc8 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$634/0x00007f420419c018 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$635/0x00007f420419c250 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$636/0x00007f420419c478 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$637/0x00007f420419c6b0 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$638/0x00007f420419c8d8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$639/0x00007f420419cb10 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$640/0x00007f420419cd38 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$1 +software.amazon.lambda.powertools.kafka.testutils.ProtobufHandler +software.amazon.lambda.powertools.kafka.testutils.JsonHandler +java.lang.Throwable$PrintStreamOrWriter +java.lang.Throwable$WrappedPrintStream +java.lang.StackTraceElement$HashedModules +java.lang.invoke.LambdaForm$DMH/0x00007f42041a0000 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$641/0x00007f420419d5f8 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$642/0x00007f420419d830 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$643/0x00007f420419da50 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$644/0x00007f420419dca0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$645/0x00007f420419def0 +org.apache.maven.surefire.api.util.internal.ObjectUtils +org.apache.maven.surefire.api.util.internal.ImmutableMap$Node +org.mockito.session.MockitoSessionLogger +org.mockito.quality.Strictness +org.mockito.junit.jupiter.resolver.CompositeParameterResolver +org.mockito.junit.jupiter.resolver.MockParameterResolver +org.mockito.junit.jupiter.resolver.CaptorParameterResolver +com.fasterxml.jackson.core.Versioned +com.fasterxml.jackson.core.TreeCodec +com.fasterxml.jackson.core.ObjectCodec +com.fasterxml.jackson.databind.ObjectMapper +org.junit.jupiter.api.extension.RegisterExtension +org.mockito.Mock +org.mockito.stubbing.Answer +org.mockito.Answers +org.mockito.Mock$Strictness +org.mockito.invocation.InvocationOnMock +org.mockito.internal.stubbing.defaultanswers.GloballyConfiguredAnswer +org.mockito.internal.stubbing.defaultanswers.ReturnsSmartNulls +org.mockito.internal.stubbing.defaultanswers.RetrieveGenericsForDefaultAnswers$AnswerCallback +org.mockito.internal.stubbing.defaultanswers.ReturnsMoreEmptyValues +org.mockito.internal.stubbing.defaultanswers.ReturnsEmptyValues +org.mockito.internal.stubbing.defaultanswers.ReturnsMocks +org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs +org.mockito.invocation.DescribedInvocation +org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs$ReturnsDeepStubsSerializationFallback +org.mockito.stubbing.ValidableAnswer +org.mockito.internal.stubbing.answers.CallsRealMethods +org.mockito.exceptions.base.MockitoException +org.mockito.internal.stubbing.defaultanswers.TriesToReturnSelf +jdk.proxy2.$Proxy21 +com.fasterxml.jackson.core.TokenStreamFactory +com.fasterxml.jackson.core.JsonFactory +com.fasterxml.jackson.databind.MappingJsonFactory +com.fasterxml.jackson.databind.jsontype.SubtypeResolver +com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver +com.fasterxml.jackson.databind.DatabindContext +com.fasterxml.jackson.databind.SerializerProvider +com.fasterxml.jackson.databind.ser.DefaultSerializerProvider +com.fasterxml.jackson.databind.ser.DefaultSerializerProvider$Impl +com.fasterxml.jackson.databind.deser.DeserializerFactory +com.fasterxml.jackson.databind.deser.BasicDeserializerFactory +com.fasterxml.jackson.databind.deser.BeanDeserializerFactory +com.fasterxml.jackson.databind.DeserializationContext +com.fasterxml.jackson.databind.deser.DefaultDeserializationContext +com.fasterxml.jackson.databind.deser.DefaultDeserializationContext$Impl +com.fasterxml.jackson.databind.ser.SerializerFactory +com.fasterxml.jackson.databind.ser.BasicSerializerFactory +com.fasterxml.jackson.databind.ser.BeanSerializerFactory +com.fasterxml.jackson.databind.AnnotationIntrospector +com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector +com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy$Provider +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$Provider +com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator +com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator$Base +com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator +com.fasterxml.jackson.databind.util.StdDateFormat +com.fasterxml.jackson.databind.DatabindException +com.fasterxml.jackson.databind.JsonMappingException +com.fasterxml.jackson.core.util.BufferRecycler$Gettable +com.fasterxml.jackson.core.io.SegmentedStringWriter +com.fasterxml.jackson.core.util.ByteArrayBuilder +com.fasterxml.jackson.core.TreeNode +com.fasterxml.jackson.databind.JsonSerializable +com.fasterxml.jackson.databind.JsonSerializable$Base +com.fasterxml.jackson.databind.JsonNode +com.fasterxml.jackson.databind.node.BaseJsonNode +com.fasterxml.jackson.databind.node.ValueNode +com.fasterxml.jackson.databind.node.NullNode +com.fasterxml.jackson.databind.introspect.ClassIntrospector +com.fasterxml.jackson.databind.introspect.BasicClassIntrospector +com.fasterxml.jackson.databind.Module$SetupContext +com.fasterxml.jackson.databind.introspect.VisibilityChecker +com.fasterxml.jackson.core.JsonParser +com.fasterxml.jackson.core.base.ParserMinimalBase +com.fasterxml.jackson.databind.node.TreeTraversingParser +com.fasterxml.jackson.core.JsonGenerator +com.fasterxml.jackson.databind.util.TokenBuffer +com.fasterxml.jackson.core.type.ResolvedType +com.fasterxml.jackson.databind.JavaType +com.fasterxml.jackson.databind.type.TypeBase +com.fasterxml.jackson.databind.type.ArrayType +com.fasterxml.jackson.databind.type.CollectionLikeType +com.fasterxml.jackson.databind.type.CollectionType +com.fasterxml.jackson.databind.type.MapLikeType +com.fasterxml.jackson.databind.type.MapType +com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder +com.fasterxml.jackson.databind.exc.MismatchedInputException +com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair +com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector +com.fasterxml.jackson.databind.introspect.Annotated +com.fasterxml.jackson.databind.introspect.TypeResolutionContext +com.fasterxml.jackson.databind.introspect.AnnotatedClass +com.fasterxml.jackson.databind.introspect.AnnotatedMember +com.fasterxml.jackson.databind.introspect.VirtualAnnotatedMember +com.fasterxml.jackson.databind.util.Named +com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition +com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition +com.fasterxml.jackson.databind.BeanProperty +com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase +com.fasterxml.jackson.databind.ser.PropertyWriter +com.fasterxml.jackson.databind.ser.BeanPropertyWriter +com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter +com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter +com.fasterxml.jackson.databind.introspect.AnnotatedWithParams +com.fasterxml.jackson.databind.introspect.AnnotatedMethod +com.fasterxml.jackson.databind.annotation.JsonSerialize +com.fasterxml.jackson.annotation.JsonView +com.fasterxml.jackson.annotation.JsonFormat +com.fasterxml.jackson.annotation.JsonTypeInfo +com.fasterxml.jackson.annotation.JsonRawValue +com.fasterxml.jackson.annotation.JsonUnwrapped +com.fasterxml.jackson.annotation.JsonBackReference +com.fasterxml.jackson.annotation.JsonManagedReference +com.fasterxml.jackson.databind.annotation.JsonDeserialize +com.fasterxml.jackson.annotation.JsonMerge +com.fasterxml.jackson.databind.ext.Java7Support +java.lang.IllegalAccessError +com.fasterxml.jackson.databind.ext.Java7SupportImpl +com.fasterxml.jackson.databind.util.ClassUtil +com.fasterxml.jackson.databind.util.ClassUtil$Ctor +java.lang.reflect.AnnotatedType +java.beans.Transient +java.beans.ConstructorProperties +com.fasterxml.jackson.databind.util.LookupCache +com.fasterxml.jackson.databind.util.LRUMap +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Builder +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap +java.io.InvalidObjectException +com.fasterxml.jackson.databind.util.internal.Linked +com.fasterxml.jackson.databind.util.internal.LinkedDeque +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$1 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$2 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$3 +java.util.concurrent.atomic.AtomicLongArray +java.lang.invoke.VarHandleLongs$Array +java.util.concurrent.atomic.AtomicReferenceArray +java.lang.invoke.VarHandleReferences$Array +com.fasterxml.jackson.databind.cfg.BaseSettings +com.fasterxml.jackson.databind.type.TypeFactory +com.fasterxml.jackson.databind.type.SimpleType +com.fasterxml.jackson.databind.type.IdentityEqualityType +com.fasterxml.jackson.databind.type.PlaceholderForType +com.fasterxml.jackson.databind.type.ReferenceType +com.fasterxml.jackson.databind.type.IterationType +com.fasterxml.jackson.databind.type.ResolvedRecursiveType +com.fasterxml.jackson.databind.type.TypeParser +com.fasterxml.jackson.databind.type.TypeBindings +java.text.ParseException +com.fasterxml.jackson.core.Base64Variants +com.fasterxml.jackson.core.Base64Variant +com.fasterxml.jackson.core.Base64Variant$PaddingReadBehaviour +com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$RecordNaming +com.fasterxml.jackson.databind.cfg.CacheProvider +com.fasterxml.jackson.databind.cfg.DefaultCacheProvider +com.fasterxml.jackson.core.io.DataOutputAsStream +com.fasterxml.jackson.core.SerializableString +com.fasterxml.jackson.core.TSFBuilder +com.fasterxml.jackson.core.JsonFactoryBuilder +java.io.CharArrayReader +com.fasterxml.jackson.core.async.NonBlockingInputFeeder +com.fasterxml.jackson.core.async.ByteBufferFeeder +com.fasterxml.jackson.core.base.ParserBase +com.fasterxml.jackson.core.json.JsonParserBase +com.fasterxml.jackson.core.json.async.NonBlockingJsonParserBase +com.fasterxml.jackson.core.json.async.NonBlockingUtf8JsonParserBase +com.fasterxml.jackson.core.json.async.NonBlockingByteBufferJsonParser +com.fasterxml.jackson.core.json.ReaderBasedJsonParser +com.fasterxml.jackson.core.json.UTF8DataInputJsonParser +com.fasterxml.jackson.core.base.GeneratorBase +com.fasterxml.jackson.core.json.JsonGeneratorImpl +com.fasterxml.jackson.core.json.WriterBasedJsonGenerator +com.fasterxml.jackson.core.json.UTF8JsonGenerator +com.fasterxml.jackson.core.io.UTF8Writer +com.fasterxml.jackson.core.async.ByteArrayFeeder +com.fasterxml.jackson.core.json.async.NonBlockingJsonParser +com.fasterxml.jackson.core.util.JacksonFeature +com.fasterxml.jackson.core.JsonFactory$Feature +com.fasterxml.jackson.core.JsonParser$Feature +com.fasterxml.jackson.core.JsonGenerator$Feature +com.fasterxml.jackson.core.io.SerializedString +com.fasterxml.jackson.core.io.JsonStringEncoder +com.fasterxml.jackson.core.io.CharTypes +com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer +com.fasterxml.jackson.core.exc.StreamConstraintsException +com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer$TableInfo +com.fasterxml.jackson.core.util.JsonRecyclerPools +com.fasterxml.jackson.core.util.RecyclerPool +com.fasterxml.jackson.core.util.RecyclerPool$ThreadLocalPoolBase +com.fasterxml.jackson.core.util.JsonRecyclerPools$ThreadLocalPool +com.fasterxml.jackson.core.util.RecyclerPool$WithPool +com.fasterxml.jackson.core.StreamReadConstraints +com.fasterxml.jackson.core.StreamWriteConstraints +com.fasterxml.jackson.core.ErrorReportConfiguration +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$TableInfo +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$Bucket +com.fasterxml.jackson.databind.util.RootNameLookup +com.fasterxml.jackson.databind.introspect.ClassIntrospector$MixInResolver +com.fasterxml.jackson.databind.introspect.SimpleMixInResolver +com.fasterxml.jackson.databind.BeanDescription +com.fasterxml.jackson.databind.introspect.BasicBeanDescription +com.fasterxml.jackson.databind.cfg.MapperConfig +com.fasterxml.jackson.databind.cfg.MapperConfigBase +com.fasterxml.jackson.databind.SerializationConfig +com.fasterxml.jackson.databind.DeserializationConfig +com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver +com.fasterxml.jackson.databind.introspect.AnnotationCollector +com.fasterxml.jackson.databind.util.Annotations +com.fasterxml.jackson.databind.introspect.AnnotationCollector$EmptyCollector +com.fasterxml.jackson.databind.introspect.AnnotationCollector$NoAnnotations +com.fasterxml.jackson.databind.introspect.AnnotatedClass$Creators +com.fasterxml.jackson.databind.introspect.AnnotatedConstructor +com.fasterxml.jackson.databind.cfg.ConfigOverrides +com.fasterxml.jackson.annotation.JacksonAnnotationValue +com.fasterxml.jackson.annotation.JsonInclude$Value +com.fasterxml.jackson.annotation.JsonInclude$Include +com.fasterxml.jackson.annotation.JsonSetter$Value +com.fasterxml.jackson.annotation.Nulls +com.fasterxml.jackson.databind.introspect.VisibilityChecker$Std +com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility +com.fasterxml.jackson.databind.cfg.CoercionConfigs +com.fasterxml.jackson.databind.type.LogicalType +com.fasterxml.jackson.databind.cfg.CoercionAction +com.fasterxml.jackson.databind.cfg.CoercionConfig +com.fasterxml.jackson.databind.cfg.MutableCoercionConfig +com.fasterxml.jackson.databind.cfg.CoercionInputShape +com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator +com.fasterxml.jackson.core.PrettyPrinter +com.fasterxml.jackson.annotation.JsonFormat$Value +com.fasterxml.jackson.annotation.JsonFormat$Shape +com.fasterxml.jackson.annotation.JsonFormat$Features +com.fasterxml.jackson.databind.cfg.ConfigOverride +com.fasterxml.jackson.databind.cfg.ConfigOverride$Empty +com.fasterxml.jackson.databind.cfg.ConfigFeature +com.fasterxml.jackson.databind.MapperFeature +com.fasterxml.jackson.core.util.Instantiatable +com.fasterxml.jackson.core.util.DefaultPrettyPrinter +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$Indenter +com.fasterxml.jackson.core.util.Separators +com.fasterxml.jackson.core.util.Separators$Spacing +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$NopIndenter +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$FixedSpaceIndenter +com.fasterxml.jackson.core.util.DefaultIndenter +com.fasterxml.jackson.databind.SerializationFeature +com.fasterxml.jackson.databind.cfg.DatatypeFeatures +com.fasterxml.jackson.databind.cfg.DatatypeFeatures$DefaultHolder +com.fasterxml.jackson.databind.cfg.DatatypeFeature +com.fasterxml.jackson.databind.cfg.EnumFeature +com.fasterxml.jackson.databind.cfg.JsonNodeFeature +com.fasterxml.jackson.databind.cfg.ContextAttributes +com.fasterxml.jackson.databind.cfg.ContextAttributes$Impl +com.fasterxml.jackson.databind.DeserializationFeature +com.fasterxml.jackson.databind.node.JsonNodeCreator +com.fasterxml.jackson.databind.node.JsonNodeFactory +com.fasterxml.jackson.databind.node.MissingNode +com.fasterxml.jackson.databind.node.BooleanNode +com.fasterxml.jackson.databind.node.NumericNode +com.fasterxml.jackson.databind.node.DoubleNode +com.fasterxml.jackson.databind.node.DecimalNode +com.fasterxml.jackson.databind.node.IntNode +com.fasterxml.jackson.databind.node.ShortNode +com.fasterxml.jackson.databind.node.BigIntegerNode +com.fasterxml.jackson.databind.node.LongNode +com.fasterxml.jackson.databind.node.FloatNode +com.fasterxml.jackson.databind.node.TextNode +com.fasterxml.jackson.databind.node.BinaryNode +com.fasterxml.jackson.databind.node.POJONode +com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable +com.fasterxml.jackson.databind.JsonSerializer +com.fasterxml.jackson.databind.jsonschema.SchemaAware +com.fasterxml.jackson.databind.ser.std.StdSerializer +com.fasterxml.jackson.databind.ser.std.NullSerializer +com.fasterxml.jackson.databind.ser.impl.FailingSerializer +com.fasterxml.jackson.databind.ser.std.ToEmptyObjectSerializer +com.fasterxml.jackson.databind.ser.impl.UnknownSerializer +com.fasterxml.jackson.databind.ser.ContextualSerializer +com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer +com.fasterxml.jackson.databind.exc.InvalidDefinitionException +com.fasterxml.jackson.databind.exc.InvalidTypeIdException +com.fasterxml.jackson.databind.node.ContainerNode +com.fasterxml.jackson.databind.node.ObjectNode +com.fasterxml.jackson.databind.ser.ResolvableSerializer +com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer +com.fasterxml.jackson.databind.ser.SerializerCache +com.fasterxml.jackson.databind.deser.NullValueProvider +com.fasterxml.jackson.databind.JsonDeserializer +com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer +com.fasterxml.jackson.databind.exc.PropertyBindingException +com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException +com.fasterxml.jackson.databind.exc.InvalidFormatException +com.fasterxml.jackson.databind.exc.ValueInstantiationException +com.fasterxml.jackson.databind.exc.MissingInjectableValueExcepion +com.fasterxml.jackson.databind.deser.UnresolvedForwardReference +com.fasterxml.jackson.databind.deser.ContextualDeserializer +com.fasterxml.jackson.databind.deser.ResolvableDeserializer +com.fasterxml.jackson.databind.deser.ValueInstantiator$Gettable +com.fasterxml.jackson.databind.deser.std.StdDeserializer +com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase +com.fasterxml.jackson.databind.deser.std.EnumMapDeserializer +com.fasterxml.jackson.databind.deser.std.MapDeserializer +com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer +com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer +com.fasterxml.jackson.databind.deser.std.StringDeserializer +com.fasterxml.jackson.databind.deser.std.MapEntryDeserializer +com.fasterxml.jackson.databind.deser.std.TokenBufferDeserializer +com.fasterxml.jackson.databind.introspect.AnnotatedParameter +com.fasterxml.jackson.databind.deser.SettableBeanProperty +com.fasterxml.jackson.databind.deser.CreatorProperty +com.fasterxml.jackson.databind.deser.AbstractDeserializer +com.fasterxml.jackson.databind.deser.std.EnumDeserializer +com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicReferenceDeserializer +com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer +com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer +com.fasterxml.jackson.databind.deser.std.EnumSetDeserializer +com.fasterxml.jackson.databind.deser.std.CollectionDeserializer +com.fasterxml.jackson.databind.deser.std.ArrayBlockingQueueDeserializer +com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer +com.fasterxml.jackson.databind.deser.impl.MethodProperty +com.fasterxml.jackson.databind.deser.impl.FieldProperty +com.fasterxml.jackson.databind.deser.impl.SetterlessProperty +com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer +com.fasterxml.jackson.databind.deser.impl.ErrorThrowingDeserializer +com.fasterxml.jackson.annotation.ObjectIdGenerator +com.fasterxml.jackson.annotation.ObjectIdGenerators$Base +com.fasterxml.jackson.annotation.ObjectIdGenerators$PropertyGenerator +com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator +com.fasterxml.jackson.databind.deser.BeanDeserializerBase +com.fasterxml.jackson.databind.deser.BeanDeserializer +com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer +com.fasterxml.jackson.databind.deser.Deserializers +com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig +com.fasterxml.jackson.databind.deser.BeanDeserializerModifier +com.fasterxml.jackson.databind.AbstractTypeResolver +com.fasterxml.jackson.databind.deser.ValueInstantiators +com.fasterxml.jackson.databind.deser.KeyDeserializers +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers +com.fasterxml.jackson.databind.KeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$DelegatingKD +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$EnumKD +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringCtorKeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringFactoryKeyDeserializer +com.fasterxml.jackson.databind.deser.DeserializerCache +com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer +com.fasterxml.jackson.databind.ser.std.JsonValueSerializer +com.fasterxml.jackson.databind.ser.std.SerializableSerializer +com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +com.fasterxml.jackson.databind.ser.std.DateTimeSerializerBase +com.fasterxml.jackson.databind.ser.std.CalendarSerializer +com.fasterxml.jackson.databind.ser.std.DateSerializer +com.fasterxml.jackson.databind.ser.std.ByteBufferSerializer +com.fasterxml.jackson.databind.ser.std.InetAddressSerializer +com.fasterxml.jackson.databind.ser.std.InetSocketAddressSerializer +com.fasterxml.jackson.databind.ser.std.TimeZoneSerializer +com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase +com.fasterxml.jackson.databind.ser.std.ToStringSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializer +com.fasterxml.jackson.databind.ser.ContainerSerializer +com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase +com.fasterxml.jackson.databind.ser.std.CollectionSerializer +com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase +com.fasterxml.jackson.databind.ser.impl.IndexedStringListSerializer +com.fasterxml.jackson.databind.ser.impl.StringCollectionSerializer +com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer +com.fasterxml.jackson.databind.ser.std.EnumSetSerializer +com.fasterxml.jackson.databind.ser.std.MapSerializer +com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer +com.fasterxml.jackson.databind.ser.std.ArraySerializerBase +com.fasterxml.jackson.databind.ser.impl.StringArraySerializer +com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer +com.fasterxml.jackson.databind.ser.std.ReferenceTypeSerializer +com.fasterxml.jackson.databind.ser.impl.IteratorSerializer +com.fasterxml.jackson.databind.ser.std.IterableSerializer +com.fasterxml.jackson.databind.ser.std.EnumSerializer +com.fasterxml.jackson.databind.ser.impl.MapEntryAsPOJOSerializer +com.fasterxml.jackson.databind.ser.std.BeanSerializerBase +com.fasterxml.jackson.databind.ser.BeanSerializer +com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator +com.fasterxml.jackson.databind.introspect.AnnotatedField +com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer +com.fasterxml.jackson.databind.ser.std.StringSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers +com.fasterxml.jackson.databind.ser.std.NumberSerializers$Base +com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntegerSerializer +com.fasterxml.jackson.core.JsonParser$NumberType +com.fasterxml.jackson.databind.ser.std.NumberSerializers$LongSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntLikeSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$ShortSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$DoubleSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$FloatSerializer +com.fasterxml.jackson.databind.ser.std.BooleanSerializer +com.fasterxml.jackson.databind.ser.std.BooleanSerializer$AsNumber +com.fasterxml.jackson.databind.ser.std.NumberSerializer$BigDecimalAsStringSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers +java.util.Currency +java.util.UUID +com.fasterxml.jackson.databind.ser.std.UUIDSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicBooleanSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicIntegerSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicLongSerializer +com.fasterxml.jackson.databind.ser.std.FileSerializer +com.fasterxml.jackson.databind.ser.std.ClassSerializer +com.fasterxml.jackson.databind.ser.std.TokenBufferSerializer +com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig +com.fasterxml.jackson.databind.ser.Serializers +com.fasterxml.jackson.databind.ser.BeanSerializerModifier +org.mockito.junit.jupiter.MockitoSettings +org.mockito.junit.jupiter.MockitoExtension$$Lambda$646/0x00007f42041f8000 +org.mockito.ArgumentMatchers +org.mockito.Mockito +org.mockito.ArgumentMatcher +org.mockito.verification.VerificationMode +org.mockito.verification.VerificationAfterDelay +org.mockito.verification.VerificationWithTimeout +org.mockito.session.MockitoSessionBuilder +org.mockito.MockitoFramework +org.mockito.internal.MockitoCore +org.mockito.stubbing.BaseStubber +org.mockito.stubbing.LenientStubber +org.mockito.MockingDetails +org.mockito.ScopedMock +org.mockito.MockedStatic +org.mockito.MockedConstruction +org.mockito.exceptions.misusing.NotAMockException +org.mockito.internal.verification.api.VerificationData +org.mockito.stubbing.Stubber +org.mockito.InOrder +org.mockito.exceptions.misusing.DoNotMockException +org.mockito.internal.verification.api.VerificationDataInOrder +org.mockito.internal.configuration.plugins.Plugins +org.mockito.plugins.MockitoPlugins +org.mockito.internal.configuration.plugins.PluginRegistry +org.mockito.plugins.PluginSwitch +org.mockito.internal.configuration.plugins.PluginLoader +org.mockito.internal.configuration.plugins.DefaultPluginSwitch +org.mockito.internal.configuration.plugins.DefaultMockitoPlugins +org.mockito.plugins.MockMaker +org.mockito.plugins.StackTraceCleanerProvider +org.mockito.plugins.InstantiatorProvider2 +org.mockito.plugins.AnnotationEngine +org.mockito.plugins.MockitoLogger +org.mockito.plugins.MemberAccessor +org.mockito.plugins.DoNotMockEnforcerWithType +org.mockito.internal.configuration.plugins.PluginInitializer +org.mockito.internal.configuration.plugins.PluginFinder +org.mockito.internal.util.collections.Iterables +org.mockito.internal.creation.bytebuddy.ClassCreatingMockMaker +org.mockito.plugins.InlineMockMaker +org.mockito.creation.instance.Instantiator +org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker +org.mockito.exceptions.base.MockitoInitializationException +org.mockito.internal.creation.bytebuddy.BytecodeGenerator +org.mockito.creation.instance.InstantiationException +org.mockito.exceptions.misusing.MockitoConfigurationException +org.mockito.plugins.MockMaker$TypeMockability +org.mockito.plugins.MockMaker$StaticMockControl +org.mockito.plugins.MockMaker$ConstructionMockControl +org.mockito.internal.PremainAttachAccess +org.mockito.internal.PremainAttach +java.lang.instrument.Instrumentation +net.bytebuddy.agent.Installer +java.io.Console +net.bytebuddy.ClassFileVersion +net.bytebuddy.ClassFileVersion$VersionLocator$Resolver +net.bytebuddy.ClassFileVersion$VersionLocator +net.bytebuddy.ClassFileVersion$VersionLocator$Resolved +java.lang.Process +net.bytebuddy.agent.ByteBuddyAgent +net.bytebuddy.agent.ByteBuddyAgent$AgentProvider +net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider +net.bytebuddy.agent.ByteBuddyAgent$AttachmentTypeEvaluator$InstallationAction +net.bytebuddy.agent.ByteBuddyAgent$AttachmentTypeEvaluator +java.security.PrivilegedActionException +com.sun.proxy.jdk.proxy1.$Proxy22 +java.security.DomainCombiner +net.bytebuddy.agent.ByteBuddyAgent$AttachmentTypeEvaluator$ForJava9CapableVm +java.lang.ProcessHandle +java.lang.ProcessHandle$Info +java.util.concurrent.CompletionStage +java.util.concurrent.CompletableFuture +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Compound +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForModularizedVm +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForJ9Vm +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForStandardToolsJarVm +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForUserDefinedToolsJar +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForEmulatedAttachment +net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm +net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm$ForJava9CapableVm +java.lang.ProcessHandleImpl +java.lang.ProcessHandleImpl$$Lambda$647/0x00007f420410ced8 +java.util.concurrent.ThreadLocalRandom +jdk.internal.util.random.RandomSupport +java.lang.invoke.LambdaForm$DMH/0x00007f4204204000 +java.lang.invoke.LambdaForm$DMH/0x00007f4204204400 +java.lang.ProcessHandleImpl$$Lambda$648/0x00007f420410d0f8 +java.lang.invoke.LambdaForm$DMH/0x00007f4204204800 +java.lang.invoke.LambdaForm$MH/0x00007f4204204c00 +java.util.concurrent.SynchronousQueue +java.util.concurrent.SynchronousQueue$Transferer +java.util.concurrent.SynchronousQueue$TransferStack +java.util.concurrent.SynchronousQueue$TransferStack$SNode +net.bytebuddy.agent.ByteBuddyAgent$AgentProvider$ForByteBuddyAgent +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor$Simple +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor$Simple$WithExternalAttachment +com.sun.tools.attach.VirtualMachine +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor$ExternalAttachment +java.util.zip.ZipInputStream +java.util.jar.JarInputStream +java.io.PushbackInputStream +sun.security.util.ManifestEntryVerifier +net.bytebuddy.agent.Attacher +java.lang.ProcessBuilder +java.lang.ProcessImpl +java.lang.ProcessImpl$Platform +java.lang.ProcessImpl$LaunchMechanism +java.lang.ProcessImpl$Platform$$Lambda$649/0x00007f420410f8c8 +java.lang.ProcessImpl$$Lambda$650/0x00007f420410faf0 +java.lang.ProcessImpl$1 +java.lang.ProcessImpl$ProcessPipeOutputStream +java.lang.ProcessImpl$ProcessPipeInputStream +java.lang.Process$PipeInputStream +java.lang.ProcessHandleImpl$ExitCompletion +java.util.concurrent.CompletableFuture$AltResult +java.util.concurrent.ForkJoinPool +java.lang.invoke.VarHandleLongs$FieldInstanceReadOnly +java.lang.invoke.VarHandleLongs$FieldInstanceReadWrite +java.lang.invoke.VarHandleInts$FieldStaticReadOnly +java.lang.invoke.VarHandleInts$FieldStaticReadWrite +java.util.concurrent.ForkJoinPool$ForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$1 +java.util.concurrent.ForkJoinPool$DefaultCommonPoolForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$WorkQueue +java.util.concurrent.CompletableFuture$AsynchronousCompletionTask +java.util.concurrent.ForkJoinTask +java.util.concurrent.CompletableFuture$Completion +java.lang.ProcessHandleImpl$1 +java.lang.ProcessImpl$$Lambda$651/0x00007f4204111920 +java.util.concurrent.CompletableFuture$UniCompletion +java.util.concurrent.CompletableFuture$UniHandle +java.util.concurrent.ForkJoinTask$Aux +jdk.internal.event.Event +jdk.internal.event.ProcessStartEvent +sun.instrument.InstrumentationImpl +sun.instrument.TransformerManager +sun.instrument.TransformerManager$TransformerInfo +java.lang.ProcessBuilder$NullInputStream +java.io.FileOutputStream$1 +java.lang.ProcessBuilder$NullOutputStream +java.io.File$TempDirectory +java.security.SecureRandom +sun.security.jca.Providers +sun.security.jca.ProviderList +sun.security.jca.ProviderConfig +java.security.Provider +sun.security.jca.ProviderList$3 +sun.security.jca.ProviderList$1 +java.security.Provider$ServiceKey +java.security.Provider$EngineDescription +java.security.SecureRandomParameters +java.security.cert.CertStoreParameters +java.security.Policy$Parameters +javax.security.auth.login.Configuration$Parameters +sun.security.jca.ProviderList$2 +sun.security.provider.Sun +sun.security.util.SecurityConstants +java.net.NetPermission +java.security.SecurityPermission +java.net.SocketPermission +sun.security.provider.SunEntries +sun.security.provider.SunEntries$1 +java.security.SecureRandomSpi +sun.security.provider.NativePRNG +sun.security.provider.NativePRNG$Variant +sun.security.provider.NativePRNG$1 +sun.security.provider.NativePRNG$2 +sun.security.provider.NativePRNG$RandomIO +sun.security.provider.FileInputStreamPool +sun.security.provider.FileInputStreamPool$UnclosableInputStream +sun.security.provider.FileInputStreamPool$StreamRef +java.security.Provider$Service +java.security.Provider$UString +sun.security.provider.NativePRNG$Blocking +sun.security.provider.NativePRNG$NonBlocking +sun.security.util.SecurityProviderConstants +sun.security.util.KnownOIDs +sun.security.util.KnownOIDs$1 +sun.security.util.KnownOIDs$2 +sun.security.util.KnownOIDs$3 +sun.security.util.KnownOIDs$4 +sun.security.util.KnownOIDs$5 +sun.security.util.KnownOIDs$6 +sun.security.util.KnownOIDs$7 +sun.security.util.KnownOIDs$8 +sun.security.util.KnownOIDs$9 +sun.security.util.KnownOIDs$10 +jdk.internal.event.SecurityProviderServiceEvent +sun.security.provider.SecureRandom +java.security.MessageDigestSpi +java.security.MessageDigest +sun.security.jca.GetInstance +sun.security.provider.DigestBase +sun.security.provider.SHA +sun.security.jca.GetInstance$Instance +sun.security.util.MessageDigestSpi2 +java.security.MessageDigest$Delegate +java.security.MessageDigest$Delegate$CloneableDelegate +sun.security.provider.ByteArrayAccess +sun.security.provider.ByteArrayAccess$BE +java.lang.invoke.VarHandleByteArrayAsInts$ByteArrayViewVarHandle +java.lang.invoke.VarHandleByteArrayAsInts$ArrayHandle +java.lang.ArrayIndexOutOfBoundsException +java.lang.invoke.VarHandleByteArrayBase +java.lang.invoke.VarHandleByteArrayAsInts +java.lang.invoke.VarHandleByteArrayAsInts$ArrayHandle$$Lambda$652/0x00007f420411ea70 +java.lang.invoke.VarHandleByteArrayAsLongs$ByteArrayViewVarHandle +java.lang.invoke.VarHandleByteArrayAsLongs$ArrayHandle +java.lang.invoke.VarHandleByteArrayAsLongs +java.lang.invoke.VarHandleByteArrayAsLongs$ArrayHandle$$Lambda$653/0x00007f420411f3d8 +java.lang.invoke.VarHandle$TypesAndInvokers +java.lang.invoke.VarHandle$2 +java.lang.invoke.VarHandle$VarHandleDesc$Kind +java.lang.constant.ConstantDescs +java.lang.constant.ClassDesc +java.lang.constant.ConstantUtils +java.lang.constant.ReferenceClassDescImpl +java.lang.constant.DirectMethodHandleDesc$Kind +java.lang.constant.MethodTypeDesc +java.lang.constant.MethodTypeDescImpl +java.lang.constant.MethodHandleDesc +java.lang.constant.MethodHandleDesc$1 +java.lang.constant.DirectMethodHandleDesc +java.lang.constant.DirectMethodHandleDescImpl +java.lang.constant.DirectMethodHandleDescImpl$1 +java.lang.constant.DirectMethodHandleDesc$1 +java.lang.constant.DynamicConstantDesc +java.lang.constant.PrimitiveClassDescImpl +java.lang.constant.DynamicConstantDesc$AnonymousDynamicConstantDesc +java.io.DeleteOnExitHook +java.io.DeleteOnExitHook$1 +java.util.zip.DeflaterOutputStream +java.util.zip.ZipOutputStream +java.util.jar.JarOutputStream +java.util.zip.Deflater +java.util.zip.Deflater$DeflaterZStreamRef +java.util.zip.ZipOutputStream$XEntry +java.util.Vector$Itr +opened:/tmp/mockitoboot10528763096709983050.jar +org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher +org.mockito.internal.util.concurrent.WeakConcurrentMap +org.mockito.internal.util.concurrent.DetachedThreadLocal +org.mockito.internal.util.concurrent.DetachedThreadLocal$1 +org.mockito.internal.util.concurrent.WeakConcurrentMap$WithInlinedExpunction +org.mockito.internal.util.concurrent.DetachedThreadLocal$2 +org.mockito.internal.util.concurrent.DetachedThreadLocal$Cleaner +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$654/0x00007f42042076e0 +java.lang.ThreadLocal$SuppliedThreadLocal +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$655/0x00007f4204207900 +org.mockito.internal.creation.bytebuddy.StackWalkerChecker +java.lang.StackWalker$Option +java.lang.invoke.LambdaForm$DMH/0x00007f4204205000 +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$656/0x00007f4204207d88 +org.mockito.internal.creation.bytebuddy.ConstructionCallback +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$657/0x00007f4204205a00 +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator +net.bytebuddy.matcher.ElementMatcher +net.bytebuddy.TypeCache +net.bytebuddy.TypeCache$WithInlineExpunction +java.lang.instrument.ClassFileTransformer +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator +net.bytebuddy.implementation.Implementation$Context$Factory +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler +net.bytebuddy.dynamic.scaffold.InstrumentedType$Prepareable +net.bytebuddy.implementation.Implementation +net.bytebuddy.asm.AsmVisitorWrapper +org.mockito.internal.creation.bytebuddy.MockMethodAdvice +java.lang.instrument.UnmodifiableClassException +net.bytebuddy.ByteBuddy +net.bytebuddy.NamingStrategy +net.bytebuddy.implementation.auxiliary.AuxiliaryType$NamingStrategy +net.bytebuddy.matcher.LatentMatcher +net.bytebuddy.utility.AsmClassWriter$Factory +net.bytebuddy.utility.AsmClassReader$Factory +net.bytebuddy.dynamic.VisibilityBridgeStrategy +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Factory +net.bytebuddy.NamingStrategy$Suffixing$BaseNameResolver +net.bytebuddy.dynamic.DynamicType$Builder +net.bytebuddy.description.NamedElement +net.bytebuddy.description.ModifierReviewable +net.bytebuddy.description.ModifierReviewable$OfByteCodeElement +net.bytebuddy.description.ModifierReviewable$OfAbstraction +net.bytebuddy.description.ModifierReviewable$OfEnumeration +net.bytebuddy.description.ModifierReviewable$ForTypeDefinition +net.bytebuddy.description.type.TypeDefinition +net.bytebuddy.matcher.FilterableList +net.bytebuddy.description.type.TypeList$Generic +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy +net.bytebuddy.description.NamedElement$WithRuntimeName +net.bytebuddy.description.annotation.AnnotationSource +net.bytebuddy.description.type.PackageDescription +net.bytebuddy.description.NamedElement$WithDescriptor +net.bytebuddy.description.DeclaredByType +net.bytebuddy.description.ByteCodeElement +net.bytebuddy.description.TypeVariableSource +net.bytebuddy.description.type.TypeDescription +net.bytebuddy.utility.privilege.GetSystemPropertyAction +net.bytebuddy.dynamic.scaffold.TypeValidation +net.bytebuddy.utility.GraalImageCode +net.bytebuddy.NamingStrategy$AbstractBase +net.bytebuddy.NamingStrategy$Suffixing +net.bytebuddy.NamingStrategy$SuffixingRandom +net.bytebuddy.NamingStrategy$Suffixing$BaseNameResolver$ForUnnamedType +net.bytebuddy.utility.RandomString +net.bytebuddy.implementation.auxiliary.AuxiliaryType$NamingStrategy$SuffixingRandom +net.bytebuddy.implementation.attribute.AnnotationValueFilter +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Default +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Default$1 +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Default$2 +net.bytebuddy.implementation.attribute.AnnotationRetention +net.bytebuddy.implementation.Implementation$Context$Default$Factory +net.bytebuddy.implementation.MethodAccessorFactory +net.bytebuddy.implementation.Implementation$Context +net.bytebuddy.implementation.Implementation$Context$ExtractableView +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$AbstractBase +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default +net.bytebuddy.dynamic.scaffold.MethodGraph +net.bytebuddy.dynamic.scaffold.MethodGraph$Linked +net.bytebuddy.description.type.TypeDescription$Generic$Visitor +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Merger +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Harmonizer +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Harmonizer$ForJavaMethod +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Merger$Directional +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying$1 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying$2 +net.bytebuddy.description.type.TypeDescription$Generic +net.bytebuddy.matcher.ElementMatchers +net.bytebuddy.matcher.ElementMatcher$Junction +net.bytebuddy.description.ModifierReviewable$ForMethodDescription +net.bytebuddy.description.DeclaredByType$WithMandatoryDeclaration +net.bytebuddy.description.NamedElement$WithGenericName +net.bytebuddy.description.ByteCodeElement$Member +net.bytebuddy.description.ByteCodeElement$TypeDependant +net.bytebuddy.description.method.MethodDescription +net.bytebuddy.description.method.MethodDescription$InDefinedShape +net.bytebuddy.description.ModifierReviewable$ForFieldDescription +net.bytebuddy.description.field.FieldDescription +net.bytebuddy.description.field.FieldDescription$InDefinedShape +net.bytebuddy.matcher.ElementMatcher$Junction$AbstractBase +net.bytebuddy.matcher.BooleanMatcher +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default$1 +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default$2 +net.bytebuddy.implementation.LoadedTypeInitializer +net.bytebuddy.implementation.bytecode.ByteCodeAppender +net.bytebuddy.dynamic.scaffold.TypeInitializer +net.bytebuddy.dynamic.scaffold.InstrumentedType +net.bytebuddy.dynamic.scaffold.InstrumentedType$WithFlexibleName +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default$1 +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default$2 +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default$3 +net.bytebuddy.utility.AsmClassReader$Factory$Default +net.bytebuddy.utility.AsmClassReader$Factory$Default$1 +net.bytebuddy.utility.AsmClassReader$Factory$Default$2 +net.bytebuddy.utility.AsmClassReader$Factory$Default$3 +net.bytebuddy.utility.AsmClassReader$Factory$Default$4 +net.bytebuddy.utility.AsmClassReader$Factory$Default$5 +net.bytebuddy.utility.AsmClassReader +net.bytebuddy.utility.AsmClassWriter$Factory$Default +net.bytebuddy.utility.AsmClassWriter$Factory$Default$1 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$2 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$3 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$4 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$5 +net.bytebuddy.pool.TypePool +net.bytebuddy.jar.asm.ClassVisitor +net.bytebuddy.jar.asm.ClassWriter +net.bytebuddy.utility.AsmClassWriter$FrameComputingClassWriter +net.bytebuddy.utility.AsmClassWriter +net.bytebuddy.matcher.LatentMatcher$Resolved +net.bytebuddy.matcher.ModifierMatcher$Mode +net.bytebuddy.matcher.ElementMatcher$Junction$ForNonNullValues +net.bytebuddy.matcher.ModifierMatcher +net.bytebuddy.matcher.NameMatcher +net.bytebuddy.matcher.StringMatcher +net.bytebuddy.matcher.StringMatcher$Mode +net.bytebuddy.matcher.StringMatcher$Mode$1 +net.bytebuddy.matcher.StringMatcher$Mode$2 +net.bytebuddy.matcher.StringMatcher$Mode$3 +net.bytebuddy.matcher.StringMatcher$Mode$4 +net.bytebuddy.matcher.StringMatcher$Mode$5 +net.bytebuddy.matcher.StringMatcher$Mode$6 +net.bytebuddy.matcher.StringMatcher$Mode$7 +net.bytebuddy.matcher.StringMatcher$Mode$8 +net.bytebuddy.matcher.StringMatcher$Mode$9 +net.bytebuddy.matcher.MethodParametersMatcher +net.bytebuddy.matcher.CollectionSizeMatcher +net.bytebuddy.matcher.ElementMatcher$Junction$Conjunction +net.bytebuddy.description.ModifierReviewable$ForParameterDescription +net.bytebuddy.description.ModifierReviewable$AbstractBase +net.bytebuddy.description.TypeVariableSource$AbstractBase +net.bytebuddy.description.type.TypeDescription$AbstractBase +net.bytebuddy.description.type.TypeDescription$ForLoadedType +java.lang.ClassFormatError +java.lang.reflect.GenericSignatureFormatError +net.bytebuddy.description.annotation.AnnotationList +net.bytebuddy.description.field.FieldList +net.bytebuddy.description.type.RecordComponentList +net.bytebuddy.matcher.FilterableList$Empty +net.bytebuddy.description.type.RecordComponentList$Empty +net.bytebuddy.matcher.FilterableList$AbstractBase +net.bytebuddy.description.type.RecordComponentList$AbstractBase +net.bytebuddy.description.type.RecordComponentList$ForLoadedRecordComponents +net.bytebuddy.description.method.MethodList +net.bytebuddy.description.type.TypeList +net.bytebuddy.description.type.TypeList$Empty +net.bytebuddy.description.type.TypeList$AbstractBase +net.bytebuddy.description.type.TypeList$ForLoadedTypes +net.bytebuddy.description.type.TypeDescription$ForLoadedType$Dispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader$Resolver$CreationAction +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader$Resolver +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader$Resolver$ForModuleSystem +net.bytebuddy.utility.dispatcher.JavaDispatcher$InvokerCreationAction +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader +net.bytebuddy.utility.Invoker +net.bytebuddy.jar.asm.ClassTooLargeException +net.bytebuddy.jar.asm.FieldVisitor +net.bytebuddy.jar.asm.FieldWriter +net.bytebuddy.jar.asm.AnnotationVisitor +net.bytebuddy.jar.asm.AnnotationWriter +net.bytebuddy.jar.asm.MethodVisitor +net.bytebuddy.jar.asm.MethodWriter +net.bytebuddy.jar.asm.ModuleVisitor +net.bytebuddy.jar.asm.ModuleWriter +net.bytebuddy.jar.asm.RecordComponentVisitor +net.bytebuddy.jar.asm.RecordComponentWriter +java.lang.TypeNotPresentException +net.bytebuddy.jar.asm.SymbolTable +net.bytebuddy.jar.asm.Symbol +net.bytebuddy.jar.asm.SymbolTable$Entry +net.bytebuddy.jar.asm.ByteVector +net.bytebuddy.jar.asm.Type +net.bytebuddy.utility.MethodComparator +net.bytebuddy.jar.asm.Frame +net.bytebuddy.jar.asm.CurrentFrame +net.bytebuddy.jar.asm.MethodTooLargeException +net.bytebuddy.jar.asm.Handler +net.bytebuddy.jar.asm.Attribute +net.bytebuddy.utility.Invoker$Dispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher$Proxied +net.bytebuddy.utility.dispatcher.JavaDispatcher$Defaults +jdk.proxy2.$Proxy23 +jdk.proxy2.$Proxy24 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Instance +net.bytebuddy.utility.dispatcher.JavaDispatcher$Container +net.bytebuddy.utility.dispatcher.JavaDispatcher$IsStatic +net.bytebuddy.utility.dispatcher.JavaDispatcher$IsConstructor +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod +net.bytebuddy.utility.nullability.MaybeNull +jdk.proxy2.$Proxy25 +net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler +net.bytebuddy.description.type.$Proxy26 +net.bytebuddy.dynamic.TargetType +net.bytebuddy.matcher.EqualityMatcher +net.bytebuddy.matcher.ErasureMatcher +net.bytebuddy.matcher.MethodReturnTypeMatcher +net.bytebuddy.matcher.DeclaringTypeMatcher +net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction +net.bytebuddy.implementation.Implementation$Context$Disabled$Factory +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$ForDeclaredMethods +net.bytebuddy.matcher.MethodSortMatcher$Sort +net.bytebuddy.matcher.MethodSortMatcher$Sort$1 +net.bytebuddy.matcher.MethodSortMatcher$Sort$2 +net.bytebuddy.matcher.MethodSortMatcher$Sort$3 +net.bytebuddy.matcher.MethodSortMatcher$Sort$4 +net.bytebuddy.matcher.MethodSortMatcher$Sort$5 +net.bytebuddy.matcher.MethodSortMatcher +net.bytebuddy.matcher.NegatingMatcher +org.mockito.internal.util.concurrent.WeakConcurrentSet +org.mockito.internal.util.concurrent.WeakConcurrentSet$Cleaner +org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator +net.bytebuddy.implementation.attribute.MethodAttributeAppender$Factory +org.mockito.internal.creation.bytebuddy.ModuleHandler +org.mockito.internal.creation.bytebuddy.ModuleHandler$ModuleSystemFound +org.mockito.internal.creation.bytebuddy.ModuleHandler$1 +org.mockito.internal.creation.bytebuddy.ModuleHandler$NoModuleSystemFound +org.mockito.internal.creation.bytebuddy.ModuleHandler$2 +org.mockito.internal.creation.bytebuddy.ModuleHandler$3 +java.lang.instrument.ClassDefinition +org.mockito.internal.creation.bytebuddy.ModuleHandler$MockitoMockClassLoader +jdk.internal.vm.annotation.ForceInline +com.sun.proxy.jdk.proxy1.$Proxy27 +net.bytebuddy.implementation.Implementation$Composable +net.bytebuddy.implementation.MethodDelegation +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler +net.bytebuddy.implementation.bind.MethodDelegationBinder$Record +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver +net.bytebuddy.implementation.MethodDelegation$WithCustomProperties +net.bytebuddy.implementation.bind.MethodDelegationBinder$BindingResolver +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate +net.bytebuddy.dynamic.scaffold.FieldLocator$Factory +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver$Compound +net.bytebuddy.implementation.bind.annotation.BindingPriority$Resolver +net.bytebuddy.implementation.bind.annotation.BindingPriority +net.bytebuddy.description.method.MethodList$AbstractBase +net.bytebuddy.description.method.MethodList$ForLoadedMethods +net.bytebuddy.description.method.MethodDescription$AbstractBase +net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase +net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$ParameterAnnotationSource +net.bytebuddy.description.method.MethodDescription$ForLoadedConstructor +net.bytebuddy.description.method.MethodDescription$ForLoadedMethod +net.bytebuddy.utility.ConstructorComparator +net.bytebuddy.description.ByteCodeElement$Token +net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$Executable +net.bytebuddy.description.method.$Proxy28 +net.bytebuddy.implementation.bind.DeclaringTypeResolver +net.bytebuddy.implementation.bind.ArgumentTypeResolver +net.bytebuddy.implementation.bind.MethodNameEqualityResolver +net.bytebuddy.implementation.bind.ParameterLengthResolver +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver$NoOp +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder +net.bytebuddy.implementation.bind.annotation.Argument$Binder +net.bytebuddy.implementation.bytecode.StackManipulation +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding +net.bytebuddy.implementation.bind.annotation.Argument +net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic +net.bytebuddy.description.method.MethodList$Explicit +net.bytebuddy.implementation.bind.annotation.AllArguments$Binder +net.bytebuddy.implementation.bind.annotation.AllArguments +net.bytebuddy.implementation.bind.annotation.AllArguments$Assignment +net.bytebuddy.implementation.bind.annotation.Origin$Binder +net.bytebuddy.implementation.bind.annotation.Origin +net.bytebuddy.implementation.bind.annotation.This$Binder +net.bytebuddy.implementation.bind.annotation.This +net.bytebuddy.implementation.bind.annotation.Super$Binder +net.bytebuddy.implementation.bind.annotation.Super +net.bytebuddy.implementation.bind.annotation.Super$Instantiation +net.bytebuddy.implementation.bind.annotation.Default$Binder +net.bytebuddy.implementation.bind.annotation.Default +net.bytebuddy.implementation.bind.annotation.SuperCall$Binder +net.bytebuddy.implementation.bind.annotation.SuperCall +net.bytebuddy.implementation.bind.annotation.SuperCallHandle$Binder +net.bytebuddy.implementation.bind.annotation.SuperCallHandle +net.bytebuddy.implementation.bind.annotation.DefaultCall$Binder +net.bytebuddy.implementation.bind.annotation.DefaultCall$Binder$DefaultMethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultCall +net.bytebuddy.implementation.bind.annotation.DefaultCallHandle$Binder +net.bytebuddy.implementation.bind.annotation.DefaultCallHandle$Binder$DefaultMethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultCallHandle +net.bytebuddy.implementation.bind.annotation.SuperMethod$Binder +net.bytebuddy.implementation.bind.annotation.SuperMethod +net.bytebuddy.implementation.bind.annotation.SuperMethodHandle$Binder +net.bytebuddy.implementation.bind.annotation.SuperMethodHandle +net.bytebuddy.implementation.bind.annotation.Handle$Binder +net.bytebuddy.utility.ConstantValue +net.bytebuddy.utility.JavaConstant +net.bytebuddy.implementation.bind.annotation.Handle +net.bytebuddy.utility.JavaConstant$MethodHandle$HandleType +net.bytebuddy.implementation.bind.annotation.DynamicConstant$Binder +net.bytebuddy.implementation.bind.annotation.DynamicConstant +net.bytebuddy.implementation.bind.annotation.DefaultMethod$Binder +net.bytebuddy.implementation.bind.annotation.DefaultMethod$Binder$MethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultMethod +net.bytebuddy.implementation.bind.annotation.DefaultMethodHandle$Binder +net.bytebuddy.implementation.bind.annotation.DefaultMethodHandle$Binder$MethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultMethodHandle +net.bytebuddy.implementation.bind.annotation.FieldValue$Binder +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder$ForFieldBinding +net.bytebuddy.implementation.bind.annotation.FieldValue$Binder$Delegate +net.bytebuddy.dynamic.scaffold.FieldLocator +net.bytebuddy.dynamic.scaffold.FieldLocator$AbstractBase +net.bytebuddy.dynamic.scaffold.FieldLocator$ForClassHierarchy +net.bytebuddy.dynamic.scaffold.FieldLocator$ForExactType +net.bytebuddy.implementation.bind.annotation.FieldValue +net.bytebuddy.implementation.bind.annotation.FieldGetterHandle$Binder +net.bytebuddy.implementation.bind.annotation.FieldGetterHandle$Binder$Delegate +net.bytebuddy.implementation.bind.annotation.FieldGetterHandle +net.bytebuddy.implementation.bind.annotation.FieldSetterHandle$Binder +net.bytebuddy.implementation.bind.annotation.FieldSetterHandle$Binder$Delegate +net.bytebuddy.implementation.bind.annotation.FieldSetterHandle +net.bytebuddy.implementation.bind.annotation.StubValue$Binder +net.bytebuddy.implementation.bind.annotation.Empty$Binder +net.bytebuddy.implementation.bind.MethodDelegationBinder$BindingResolver$Default +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$Identifier +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder$ForFixedValue +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder$ForFixedValue$OfConstant +net.bytebuddy.utility.CompoundList +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForReadObject +org.mockito.internal.creation.bytebuddy.access.MockAccess +net.bytebuddy.implementation.bind.MethodDelegationBinder +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor$Handler +net.bytebuddy.implementation.bind.annotation.StubValue +net.bytebuddy.implementation.bind.annotation.Empty +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate$ForStaticMethod +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate$Compiled +net.bytebuddy.implementation.bind.annotation.IgnoreForBinding$Verifier +net.bytebuddy.description.annotation.AnnotationList$AbstractBase +net.bytebuddy.description.annotation.AnnotationList$ForLoadedAnnotations +net.bytebuddy.description.annotation.AnnotationDescription +net.bytebuddy.implementation.bind.annotation.IgnoreForBinding +net.bytebuddy.description.method.ParameterList +net.bytebuddy.description.method.ParameterList$AbstractBase +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfMethod +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfLegacyVmMethod +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfConstructor +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfLegacyVmConstructor +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$Executable +jdk.proxy2.$Proxy29 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForInstanceCheck +jdk.internal.reflect.GeneratedConstructorAccessor10 +net.bytebuddy.description.method.$Proxy30 +net.bytebuddy.description.NamedElement$WithOptionalName +net.bytebuddy.description.method.ParameterDescription +net.bytebuddy.description.method.ParameterDescription$InDefinedShape +net.bytebuddy.description.method.ParameterDescription$AbstractBase +net.bytebuddy.description.method.ParameterDescription$InDefinedShape$AbstractBase +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$OfMethod +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$Parameter +net.bytebuddy.description.method.$Proxy31 +net.bytebuddy.implementation.bind.annotation.RuntimeType$Verifier +org.mockito.internal.creation.bytebuddy.$Proxy32 +jdk.proxy2.$Proxy33 +net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 +net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 +jdk.proxy2.$Proxy34 +net.bytebuddy.implementation.bind.annotation.RuntimeType +net.bytebuddy.description.annotation.AnnotationDescription$Loadable +net.bytebuddy.description.annotation.AnnotationDescription$AbstractBase +net.bytebuddy.description.annotation.AnnotationDescription$ForLoadedAnnotation +net.bytebuddy.description.annotation.AnnotationValue +net.bytebuddy.description.enumeration.EnumerationDescription +net.bytebuddy.description.type.TypeDefinition$Sort +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader +net.bytebuddy.description.type.TypeDefinition$Sort$AnnotatedType +net.bytebuddy.description.type.$Proxy35 +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$NoOp +net.bytebuddy.description.type.TypeDescription$Generic$AbstractBase +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType$ForLoadedType +net.bytebuddy.implementation.bytecode.assign.Assigner$Typing +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor$Handler$Unbound +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor$Handler$Bound +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$Record +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler$Default +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler$Default$1 +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler$Default$2 +net.bytebuddy.implementation.bytecode.assign.Assigner +net.bytebuddy.implementation.bytecode.assign.primitive.VoidAwareAssigner +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveTypeAwareAssigner +net.bytebuddy.implementation.bytecode.assign.reference.ReferenceTypeAwareAssigner +net.bytebuddy.implementation.bytecode.StackManipulation$Trivial +net.bytebuddy.implementation.bytecode.StackManipulation$Illegal +net.bytebuddy.implementation.bytecode.assign.reference.GenericTypeAwareAssigner +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$DispatcherDefaultingToRealMethod +org.mockito.internal.invocation.RealMethod +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor +jdk.proxy2.$Proxy36 +jdk.proxy2.$Proxy37 +jdk.proxy2.$Proxy38 +jdk.proxy2.$Proxy39 +jdk.proxy2.$Proxy40 +jdk.proxy2.$Proxy41 +jdk.proxy2.$Proxy42 +jdk.internal.reflect.GeneratedMethodAccessor1 +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$ForHashCode +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$ForEquals +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$ForWriteReplace +net.bytebuddy.TypeCache$Sort +net.bytebuddy.TypeCache$Sort$1 +net.bytebuddy.TypeCache$Sort$2 +net.bytebuddy.TypeCache$Sort$3 +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$TypeCachingLock +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$DispatchingVisitor +net.bytebuddy.matcher.CollectionOneToOneMatcher +net.bytebuddy.matcher.CollectionErasureMatcher +net.bytebuddy.matcher.MethodParameterTypesMatcher +net.bytebuddy.matcher.AnnotationTypeMatcher +net.bytebuddy.matcher.DeclaringAnnotationMatcher +net.bytebuddy.matcher.CollectionItemMatcher +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$MethodVisitorWrapper +net.bytebuddy.asm.Advice +net.bytebuddy.asm.Advice$ExceptionHandler +net.bytebuddy.asm.Advice$Dispatcher +net.bytebuddy.asm.Advice$Dispatcher$Unresolved +net.bytebuddy.dynamic.ClassFileLocator +net.bytebuddy.asm.Advice$Delegator$Factory +net.bytebuddy.asm.Advice$PostProcessor$Factory +net.bytebuddy.utility.visitor.ExceptionTableSensitiveMethodVisitor +net.bytebuddy.utility.visitor.LineNumberPrependingMethodVisitor +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$Relocation +net.bytebuddy.asm.Advice$AdviceVisitor +net.bytebuddy.asm.Advice$AdviceVisitor$WithoutExitAdvice +net.bytebuddy.asm.Advice$AdviceVisitor$WithExitAdvice +net.bytebuddy.asm.Advice$AdviceVisitor$WithExitAdvice$WithoutExceptionHandling +net.bytebuddy.asm.Advice$AdviceVisitor$WithExitAdvice$WithExceptionHandling +net.bytebuddy.asm.Advice$OnMethodEnter +net.bytebuddy.asm.Advice$OnMethodExit +net.bytebuddy.asm.Advice$WithCustomMapping +net.bytebuddy.asm.Advice$OffsetMapping$Factory +net.bytebuddy.asm.Advice$BootstrapArgumentResolver$Factory +net.bytebuddy.asm.Advice$PostProcessor +net.bytebuddy.asm.Advice$PostProcessor$NoOp +net.bytebuddy.asm.Advice$Delegator$ForRegularInvocation$Factory +net.bytebuddy.asm.Advice$Delegator +net.bytebuddy.asm.Advice$OffsetMapping$ForStackManipulation$Factory +net.bytebuddy.asm.Advice$OffsetMapping +net.bytebuddy.utility.ConstantValue$Simple +net.bytebuddy.utility.JavaConstant$Simple +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher +jdk.proxy2.$Proxy43 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForContainerCreation +net.bytebuddy.utility.$Proxy44 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfClassDesc +jdk.proxy2.$Proxy45 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForStaticMethod +jdk.proxy2.$Proxy46 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfMethodTypeDesc +jdk.proxy2.$Proxy47 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfMethodHandleDesc +jdk.proxy2.$Proxy48 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDirectMethodHandleDesc +jdk.proxy2.$Proxy49 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDirectMethodHandleDesc$ForKind +jdk.proxy2.$Proxy50 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDynamicConstantDesc +jdk.proxy2.$Proxy51 +net.bytebuddy.utility.JavaConstant$Simple$OfTrivialValue +net.bytebuddy.utility.JavaConstant$Simple$OfTrivialValue$ForString +net.bytebuddy.implementation.bytecode.StackManipulation$AbstractBase +net.bytebuddy.implementation.bytecode.constant.TextConstant +net.bytebuddy.dynamic.ClassFileLocator$ForClassLoader +net.bytebuddy.dynamic.ClassFileLocator$Resolution +net.bytebuddy.dynamic.ClassFileLocator$ForClassLoader$BootLoaderProxyCreationAction +net.bytebuddy.dynamic.loading.ClassLoadingStrategy +net.bytebuddy.asm.Advice$Dispatcher$Resolved +net.bytebuddy.asm.Advice$Dispatcher$Resolved$ForMethodEnter +net.bytebuddy.asm.Advice$Dispatcher$Resolved$ForMethodExit +net.bytebuddy.asm.Advice$Dispatcher$Bound +net.bytebuddy.asm.Advice$Dispatcher$Inactive +net.bytebuddy.asm.Advice$NoExceptionHandler +jdk.proxy2.$Proxy52 +net.bytebuddy.description.annotation.AnnotationValue$AbstractBase +net.bytebuddy.description.annotation.AnnotationValue$ForConstant +net.bytebuddy.description.annotation.AnnotationValue$Loaded +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$1 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$2 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$3 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$4 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$5 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$6 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$7 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$8 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$9 +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithEagerNavigation +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithEagerNavigation$OfAnnotatedElement +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$ForLoadedReturnType +net.bytebuddy.asm.Advice$Dispatcher$Inlining +net.bytebuddy.asm.Advice$Return +jdk.proxy2.$Proxy53 +net.bytebuddy.asm.Advice$Enter +jdk.proxy2.$Proxy54 +net.bytebuddy.asm.Advice$Local +net.bytebuddy.asm.Advice$OnNonDefaultValue +jdk.proxy2.$Proxy55 +net.bytebuddy.asm.Advice$This +jdk.proxy2.$Proxy56 +net.bytebuddy.asm.Advice$Origin +jdk.proxy2.$Proxy57 +net.bytebuddy.asm.Advice$AllArguments +jdk.proxy2.$Proxy58 +net.bytebuddy.dynamic.ClassFileLocator$Resolution$Explicit +net.bytebuddy.utility.StreamDrainer +net.bytebuddy.utility.OpenedClassReader +net.bytebuddy.utility.AsmClassReader$ForAsm +net.bytebuddy.jar.asm.ClassReader +net.bytebuddy.asm.Advice$Dispatcher$Resolved$AbstractBase +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter$WithRetainedEnterType +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter$WithDiscardedEnterType +net.bytebuddy.asm.Advice$ArgumentHandler +net.bytebuddy.asm.Advice$Dispatcher$Inlining$CodeTranslationVisitor +net.bytebuddy.asm.Advice$OffsetMapping$ForArgument$Unresolved$Factory +net.bytebuddy.asm.Advice$Argument +net.bytebuddy.asm.Advice$OffsetMapping$ForAllArguments$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForThisReference$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForField +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved$WithImplicitType +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved$WithExplicitType +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$ReaderFactory +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$WithImplicitType +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$WithExplicitType +net.bytebuddy.asm.Advice$FieldGetterHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$WriterFactory +net.bytebuddy.asm.Advice$FieldSetterHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForOrigin$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForSelfCallHandle$Factory +net.bytebuddy.asm.Advice$SelfCallHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForHandle$Factory +net.bytebuddy.asm.Advice$Handle +net.bytebuddy.asm.Advice$OffsetMapping$ForDynamicConstant$Factory +net.bytebuddy.asm.Advice$DynamicConstant +net.bytebuddy.asm.Advice$OffsetMapping$ForUnusedValue$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForStubValue +net.bytebuddy.asm.Advice$OffsetMapping$Target +net.bytebuddy.asm.Advice$OffsetMapping$ForThrowable$Factory +net.bytebuddy.asm.Advice$Thrown +net.bytebuddy.asm.Advice$OffsetMapping$ForExitValue$Factory +net.bytebuddy.asm.Advice$Exit +net.bytebuddy.asm.Advice$OffsetMapping$Factory$Illegal +net.bytebuddy.asm.Advice$OffsetMapping$ForLocalValue$Factory +net.bytebuddy.description.annotation.AnnotationValue$ForTypeDescription +net.bytebuddy.description.annotation.AnnotationValue$ForMismatchedType +net.bytebuddy.asm.Advice$OffsetMapping$Factory$AdviceType +net.bytebuddy.asm.Advice$FieldValue +net.bytebuddy.asm.Advice$Unused +net.bytebuddy.asm.Advice$StubValue +net.bytebuddy.asm.Advice$OffsetMapping$ForStackManipulation +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$OfMethodParameter +net.bytebuddy.description.type.TypeList$Generic$AbstractBase +net.bytebuddy.description.type.TypeList$Generic$Explicit +net.bytebuddy.description.type.TypeList$Explicit +net.bytebuddy.implementation.bytecode.StackSize +net.bytebuddy.asm.Advice$OffsetMapping$ForThisReference +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForDefaultValue +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForDefaultValue$ReadOnly +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForDefaultValue$ReadWrite +net.bytebuddy.description.enumeration.EnumerationDescription$AbstractBase +net.bytebuddy.description.enumeration.EnumerationDescription$ForLoadedEnumeration +net.bytebuddy.description.annotation.AnnotationValue$ForEnumerationDescription +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$1 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$2 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$3 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$4 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$5 +sun.reflect.generics.scope.MethodScope +sun.reflect.generics.repository.ConstructorRepository +sun.reflect.generics.repository.MethodRepository +sun.reflect.generics.tree.ArrayTypeSignature +sun.reflect.generics.tree.BottomSignature +sun.reflect.generics.tree.Wildcard +sun.reflect.generics.tree.MethodTypeSignature +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableParameterType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableParameterType$Dispatcher +jdk.internal.reflect.GeneratedMethodAccessor2 +net.bytebuddy.description.type.$Proxy59 +net.bytebuddy.asm.Advice$OffsetMapping$ForAllArguments +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$Chained +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForComponentType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForComponentType$AnnotatedParameterizedType +java.lang.reflect.AnnotatedArrayType +net.bytebuddy.description.type.$Proxy60 +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler$Suppressing +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler$Bound +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler$NoOp +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForType +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$Bound +net.bytebuddy.asm.Advice$OnDefaultValue +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$1 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$2 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$3 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$4 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$5 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$6 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$7 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$8 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$9 +java.lang.reflect.WildcardType +sun.reflect.generics.reflectiveObjects.WildcardTypeImpl +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedMethodReturnType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedMethodReturnType$Dispatcher +net.bytebuddy.description.type.$Proxy61 +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$ForLoadedType +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$OfNonDefault +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit$WithoutExceptionHandler +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit$WithExceptionHandler +net.bytebuddy.asm.Advice$OffsetMapping$ForEnterValue$Factory +sun.reflect.generics.tree.VoidDescriptor +net.bytebuddy.asm.Advice$OffsetMapping$ForReturnValue$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForReturnValue +net.bytebuddy.asm.Advice$OffsetMapping$ForEnterValue +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$Disabled +net.bytebuddy.asm.Advice$ExceptionHandler$Default +net.bytebuddy.asm.Advice$ExceptionHandler$Default$1 +net.bytebuddy.asm.Advice$ExceptionHandler$Default$2 +net.bytebuddy.asm.Advice$ExceptionHandler$Default$3 +net.bytebuddy.implementation.SuperMethodCall +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$Entry +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForStatic +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedType +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ConstructorShortcut +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ConstructorShortcut$1 +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForHashCode +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForEquals +jdk.proxy2.$Proxy62 +net.bytebuddy.asm.Advice$OffsetMapping$ForArgument +net.bytebuddy.asm.Advice$OffsetMapping$ForArgument$Unresolved +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$SelfCallInfo +org.mockito.internal.util.reflection.ModuleMemberAccessor +org.mockito.internal.util.reflection.InstrumentationMemberAccessor +net.bytebuddy.dynamic.loading.InjectionClassLoader +net.bytebuddy.dynamic.loading.ByteArrayClassLoader +net.bytebuddy.implementation.MethodCall +net.bytebuddy.implementation.MethodCall$WithoutSpecifiedTarget +net.bytebuddy.dynamic.loading.ClassFilePostProcessor +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PackageLookupStrategy$CreationAction +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PackageLookupStrategy +net.bytebuddy.utility.JavaModule +net.bytebuddy.utility.JavaModule$Resolver +net.bytebuddy.utility.$Proxy63 +net.bytebuddy.utility.JavaModule$Module +net.bytebuddy.utility.$Proxy64 +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PackageLookupStrategy$ForJava9CapableVm +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy$CreationAction +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy$Initializable +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy$ForJava8CapableVm +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PersistenceHandler +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PersistenceHandler$1 +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PersistenceHandler$2 +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy$Trivial +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy$Definition +net.bytebuddy.dynamic.loading.ClassFilePostProcessor$NoOp +org.mockito.internal.util.reflection.InstrumentationMemberAccessor$Dispatcher +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$1 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$2 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$3 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$4 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$5 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter +net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder +net.bytebuddy.dynamic.TypeResolutionStrategy +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ImplementationDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$TypeVariableDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ExceptionDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Valuable +net.bytebuddy.implementation.attribute.TypeAttributeAppender +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator +net.bytebuddy.dynamic.DynamicType$Builder$InnerTypeDefinition +net.bytebuddy.dynamic.DynamicType$Builder$InnerTypeDefinition$ForType +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$InnerTypeDefinitionForTypeAdapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$InnerTypeDefinitionForMethodAdapter +net.bytebuddy.dynamic.DynamicType$Builder$RecordComponentDefinition +net.bytebuddy.dynamic.DynamicType$Builder$RecordComponentDefinition$Optional +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ImplementationDefinition$Optional +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Initial +net.bytebuddy.dynamic.DynamicType$Builder$TypeVariableDefinition +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$Valuable +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry +net.bytebuddy.dynamic.scaffold.MethodRegistry +net.bytebuddy.dynamic.scaffold.FieldRegistry +net.bytebuddy.implementation.Implementation$Target$Factory +net.bytebuddy.dynamic.scaffold.TypeWriter$RecordComponentPool +net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool +net.bytebuddy.description.modifier.ModifierContributor +net.bytebuddy.description.modifier.ModifierContributor$ForType +net.bytebuddy.description.modifier.ModifierContributor$ForMethod +net.bytebuddy.description.modifier.ModifierContributor$ForField +net.bytebuddy.description.modifier.Visibility +net.bytebuddy.description.modifier.TypeManifestation +net.bytebuddy.description.modifier.ModifierContributor$Resolver +net.bytebuddy.description.type.TypeDescription$AbstractBase$OfSimpleType +net.bytebuddy.dynamic.scaffold.InstrumentedType$Default +net.bytebuddy.dynamic.scaffold.TypeInitializer$None +net.bytebuddy.implementation.LoadedTypeInitializer$NoOp +net.bytebuddy.description.type.TypeDescription$LazyProxy +net.bytebuddy.description.modifier.Ownership +net.bytebuddy.description.modifier.ModifierContributor$ForParameter +net.bytebuddy.description.modifier.SyntheticState +net.bytebuddy.description.modifier.EnumerationState +net.bytebuddy.description.TypeVariableSource$Visitor +java.util.LinkedList$ListItr +jdk.proxy2.$Proxy65 +net.bytebuddy.description.type.TypeList$Generic$ForLoadedTypes +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$ForDetachment +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default +net.bytebuddy.dynamic.scaffold.FieldRegistry$Compiled +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default +net.bytebuddy.dynamic.scaffold.MethodRegistry$Prepared +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry$Default +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry$Compiled +net.bytebuddy.implementation.attribute.TypeAttributeAppender$ForInstrumentedType +net.bytebuddy.implementation.attribute.AnnotationAppender$Target +net.bytebuddy.implementation.attribute.AnnotationAppender +net.bytebuddy.asm.AsmVisitorWrapper$NoOp +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ImplementationDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodMatchAdapter +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ReceiverTypeDefinition +net.bytebuddy.implementation.MethodCall$MethodLocator$Factory +net.bytebuddy.implementation.MethodCall$TerminationHandler$Factory +net.bytebuddy.implementation.MethodCall$MethodInvoker$Factory +net.bytebuddy.implementation.MethodCall$TargetHandler$Factory +net.bytebuddy.implementation.MethodCall$ArgumentLoader$Factory +net.bytebuddy.implementation.MethodCall$MethodLocator +net.bytebuddy.implementation.MethodCall$MethodLocator$ForExplicitMethod +net.bytebuddy.implementation.MethodCall$TargetHandler$ForField$Location +net.bytebuddy.implementation.MethodCall$TargetHandler$ForSelfOrStaticInvocation$Factory +net.bytebuddy.implementation.MethodCall$TargetHandler +net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation$Factory +net.bytebuddy.implementation.MethodCall$MethodInvoker +net.bytebuddy.implementation.MethodCall$TerminationHandler +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple$1 +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple$2 +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple$3 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler$ForImplementation +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler$Compiled +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ReceiverTypeDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$AbstractBase$Adapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodMatchAdapter$AnnotationAdapter +net.bytebuddy.dynamic.Transformer +net.bytebuddy.implementation.attribute.MethodAttributeAppender +net.bytebuddy.implementation.attribute.MethodAttributeAppender$NoOp +net.bytebuddy.dynamic.Transformer$NoOp +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Entry +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodCall$Factory +net.bytebuddy.implementation.MethodCall$MethodInvoker$ForVirtualInvocation$WithImplicitType +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodParameter +net.bytebuddy.implementation.MethodCall$TargetHandler$Resolved +net.bytebuddy.implementation.MethodCall$ArgumentLoader$ArgumentProvider +net.bytebuddy.implementation.MethodCall$ArgumentLoader$ForMethodParameter$Factory +net.bytebuddy.dynamic.TypeResolutionStrategy$Resolved +net.bytebuddy.dynamic.TypeResolutionStrategy$Passive +net.bytebuddy.pool.TypePool$AbstractBase +net.bytebuddy.pool.TypePool$AbstractBase$Hierarchical +net.bytebuddy.pool.TypePool$ClassLoading +net.bytebuddy.pool.TypePool$Resolution +net.bytebuddy.pool.TypePool$CacheProvider +net.bytebuddy.pool.TypePool$Empty +net.bytebuddy.pool.TypePool$CacheProvider$Simple +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithResolvedErasure +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$ForAttachment +net.bytebuddy.description.method.MethodList$TypeSubstituting +net.bytebuddy.description.method.MethodDescription$InGenericShape +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$ForRawType +net.bytebuddy.matcher.VisibilityMatcher +net.bytebuddy.description.method.MethodDescription$TypeSubstituting +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$ForGenerifiedErasure +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType$ForErasure +net.bytebuddy.description.type.TypeList$Generic$ForLoadedTypes$OfTypeVariables +net.bytebuddy.description.method.MethodDescription$Token +net.bytebuddy.matcher.TypeSortMatcher +net.bytebuddy.description.ByteCodeElement$Token$TokenList +net.bytebuddy.description.method.ParameterList$TypeSubstituting +net.bytebuddy.description.method.ParameterDescription$InGenericShape +net.bytebuddy.description.type.TypeList$Generic$ForDetachedTypes +net.bytebuddy.description.type.TypeList$Generic$OfConstructorExceptionTypes +jdk.internal.vm.annotation.IntrinsicCandidate +com.sun.proxy.jdk.proxy1.$Proxy66 +net.bytebuddy.description.annotation.AnnotationList$Explicit +net.bytebuddy.description.type.TypeDescription$Generic$LazyProxy +jdk.proxy2.$Proxy67 +net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder$InstrumentableMatcher +net.bytebuddy.description.method.MethodList$ForTokens +net.bytebuddy.description.method.MethodDescription$Latent +net.bytebuddy.description.method.ParameterList$ForTokens +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithLazyNavigation +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithLazyNavigation$OfAnnotatedElement +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$ForLoadedSuperClass +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry +net.bytebuddy.description.type.TypeList$Generic$ForDetachedTypes$WithResolvedErasure +net.bytebuddy.description.type.TypeList$Generic$OfLoadedInterfaceTypes +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Harmonized +net.bytebuddy.description.method.MethodDescription$TypeToken +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Harmonizer$ForJavaMethod$Token +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry$Initial +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry$Resolved +net.bytebuddy.dynamic.scaffold.MethodGraph$Node +java.util.AbstractMap$SimpleImmutableEntry +net.bytebuddy.description.method.ParameterDescription$TypeSubstituting +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry$Resolved$Node +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Detached +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Graph +net.bytebuddy.dynamic.scaffold.MethodGraph$Linked$Delegation +net.bytebuddy.matcher.MethodParameterTypeMatcher +net.bytebuddy.matcher.FailSafeMatcher +net.bytebuddy.dynamic.scaffold.MethodGraph$NodeList +net.bytebuddy.dynamic.scaffold.MethodGraph$Node$Sort +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Prepared$Entry +net.bytebuddy.description.method.MethodDescription$Latent$TypeInitializer +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Prepared +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool +net.bytebuddy.dynamic.scaffold.MethodRegistry$Compiled +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$1 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$2 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$3 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$ForTypeAnnotations +net.bytebuddy.description.annotation.AnnotationList$Empty +net.bytebuddy.description.type.TypeList$Generic$ForDetachedTypes$OfTypeVariables +net.bytebuddy.description.type.PackageDescription$AbstractBase +net.bytebuddy.description.type.PackageDescription$Simple +net.bytebuddy.description.field.FieldList$AbstractBase +net.bytebuddy.description.field.FieldList$ForTokens +net.bytebuddy.description.method.MethodDescription$SignatureToken +net.bytebuddy.description.annotation.AnnotationValue$ForDescriptionArray +net.bytebuddy.description.annotation.AnnotationValue$Sort +net.bytebuddy.description.annotation.AnnotationValue$State +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$Factory +net.bytebuddy.implementation.Implementation$Target +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$OriginTypeResolver +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$OriginTypeResolver$1 +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$OriginTypeResolver$2 +net.bytebuddy.implementation.Implementation$Target$AbstractBase +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation +net.bytebuddy.implementation.Implementation$Target$AbstractBase$DefaultMethodInvocation +net.bytebuddy.implementation.Implementation$Target$AbstractBase$DefaultMethodInvocation$1 +net.bytebuddy.implementation.Implementation$Target$AbstractBase$DefaultMethodInvocation$2 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler$ForImplementation$Compiled +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record +net.bytebuddy.implementation.MethodCall$Appender +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodCall +net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation +net.bytebuddy.implementation.MethodCall$TargetHandler$ForSelfOrStaticInvocation +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Compiled$Entry +net.bytebuddy.implementation.SuperMethodCall$Appender +net.bytebuddy.implementation.SuperMethodCall$Appender$TerminationHandler +net.bytebuddy.implementation.SuperMethodCall$Appender$TerminationHandler$1 +net.bytebuddy.implementation.SuperMethodCall$Appender$TerminationHandler$2 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Compiled +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default$Compiled +net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool$Record +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry$Default$Compiled +net.bytebuddy.dynamic.scaffold.TypeWriter$RecordComponentPool$Record +net.bytebuddy.pool.TypePool$Explicit +net.bytebuddy.pool.TypePool$CacheProvider$NoOp +net.bytebuddy.dynamic.scaffold.TypeWriter +net.bytebuddy.dynamic.scaffold.TypeWriter$Default +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ClassDumpAction$Dispatcher +net.bytebuddy.dynamic.scaffold.inline.MethodRebaseResolver +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation +net.bytebuddy.utility.visitor.MetadataAwareClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation$CreationClassVisitor +net.bytebuddy.utility.visitor.ContextClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation$ImplementationContextClassVisitor +net.bytebuddy.dynamic.scaffold.TypeInitializer$Drain +net.bytebuddy.description.type.RecordComponentList$ForTokens +net.bytebuddy.description.type.RecordComponentDescription +net.bytebuddy.description.type.RecordComponentDescription$InDefinedShape +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ClassDumpAction$Dispatcher$Disabled +net.bytebuddy.utility.AsmClassWriter$Factory$Default$EmptyAsmClassReader +net.bytebuddy.utility.AsmClassWriter$ForAsm +net.bytebuddy.implementation.Implementation$Context$FrameGeneration +net.bytebuddy.implementation.Implementation$Context$FrameGeneration$1 +net.bytebuddy.implementation.Implementation$Context$FrameGeneration$2 +net.bytebuddy.implementation.Implementation$Context$FrameGeneration$3 +net.bytebuddy.implementation.Implementation$Context$ExtractableView$AbstractBase +net.bytebuddy.implementation.Implementation$Context$Default +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod +net.bytebuddy.implementation.Implementation$Context$Default$DelegationRecord +net.bytebuddy.implementation.Implementation$Context$Default$AccessorMethodDelegation +net.bytebuddy.implementation.Implementation$Context$Default$FieldGetterDelegation +net.bytebuddy.implementation.Implementation$Context$Default$FieldSetterDelegation +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$ValidatingFieldVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$ValidatingMethodVisitor +net.bytebuddy.jar.asm.signature.SignatureVisitor +net.bytebuddy.jar.asm.signature.SignatureWriter +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$ForSignatureVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint$ForClassFileVersion +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint$ForClass +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint$Compound +net.bytebuddy.implementation.attribute.AnnotationAppender$Default +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnType +net.bytebuddy.implementation.attribute.AnnotationAppender$ForTypeAnnotations +java.util.AbstractList$SubList +net.bytebuddy.jar.asm.TypeReference +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$AccessBridgeWrapper +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$Sort +net.bytebuddy.description.modifier.Visibility$1 +net.bytebuddy.description.type.TypeList$Generic$OfMethodExceptionTypes +net.bytebuddy.implementation.MethodCall$TargetHandler$ForSelfOrStaticInvocation$Resolved +net.bytebuddy.implementation.bytecode.Duplication +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodCall$Resolved +net.bytebuddy.implementation.bytecode.ByteCodeAppender$Size +net.bytebuddy.implementation.bytecode.StackManipulation$Compound +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$MethodLoading$TypeCastingHandler +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$OffsetLoading +net.bytebuddy.implementation.bytecode.member.MethodInvocation +net.bytebuddy.implementation.bytecode.member.MethodInvocation$WithImplicitInvocationTargetType +net.bytebuddy.implementation.bytecode.member.MethodInvocation$Invocation +net.bytebuddy.implementation.bytecode.member.MethodReturn +net.bytebuddy.implementation.bytecode.StackManipulation$Size +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodParameter$Resolved +net.bytebuddy.implementation.MethodCall$ArgumentLoader +net.bytebuddy.implementation.MethodCall$ArgumentLoader$ForMethodParameter +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveWideningDelegate +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveWideningDelegate$WideningStackManipulation +net.bytebuddy.description.type.TypeList$Generic$OfMethodExceptionTypes$TypeProjection +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableExceptionType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableExceptionType$Dispatcher +net.bytebuddy.description.type.$Proxy68 +net.bytebuddy.matcher.SignatureTokenMatcher +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation$AbstractBase +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation$Simple +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$MethodLoading +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$MethodLoading$TypeCastingHandler$NoOp +net.bytebuddy.dynamic.scaffold.TypeInitializer$Drain$Default +net.bytebuddy.description.method.ParameterList$Empty +net.bytebuddy.description.type.TypeList$Generic$Empty +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForNonImplementedMethod +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$UnresolvedType +net.bytebuddy.dynamic.DynamicType +net.bytebuddy.dynamic.DynamicType$Unloaded +net.bytebuddy.dynamic.DynamicType$AbstractBase +net.bytebuddy.dynamic.DynamicType$Default +net.bytebuddy.dynamic.DynamicType$Default$Unloaded +net.bytebuddy.dynamic.DynamicType$Loaded +net.bytebuddy.dynamic.loading.InjectionClassLoader$Strategy +net.bytebuddy.dynamic.DynamicType$Default$Loaded +java.lang.invoke.LambdaForm$MH/0x00007f42042d0000 +java.lang.invoke.LambdaForm$MH/0x00007f42042d0400 +java.lang.invoke.LambdaForm$MH/0x00007f42042d0800 +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$ClassDefinitionAction +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy$Definition$Trivial +org.mockito.internal.util.reflection.InstrumentationMemberAccessor$Dispatcher$ByteBuddy$41mTxNg6 +java.lang.invoke.LambdaForm$DMH/0x00007f42042d1000 +org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleanerProvider +org.mockito.internal.configuration.InjectingAnnotationEngine +org.mockito.internal.configuration.IndependentAnnotationEngine +org.mockito.internal.configuration.FieldAnnotationProcessor +org.mockito.internal.configuration.MockAnnotationProcessor +org.mockito.Captor +org.mockito.internal.configuration.CaptorAnnotationProcessor +org.mockito.internal.configuration.SpyAnnotationEngine +org.mockito.internal.util.ConsoleMockitoLogger +org.mockito.plugins.MockResolver +org.mockito.plugins.DoNotMockEnforcer +org.mockito.internal.configuration.DefaultDoNotMockEnforcer +org.mockito.internal.creation.instance.DefaultInstantiatorProvider +org.mockito.internal.creation.instance.ObjenesisInstantiator +org.objenesis.Objenesis +org.objenesis.ObjenesisBase +org.objenesis.ObjenesisStd +org.objenesis.strategy.InstantiatorStrategy +org.mockito.configuration.IMockitoConfiguration +org.mockito.internal.configuration.GlobalConfiguration +org.mockito.configuration.DefaultMockitoConfiguration +org.mockito.internal.configuration.ClassPathLoader +org.objenesis.strategy.BaseInstantiatorStrategy +org.objenesis.strategy.StdInstantiatorStrategy +org.objenesis.instantiator.ObjectInstantiator +org.mockito.internal.session.DefaultMockitoSessionBuilder +org.mockito.MockitoSession +org.mockito.internal.session.MockitoSessionLoggerAdapter +org.mockito.internal.session.MockitoLoggerAdapter +org.mockito.internal.framework.DefaultMockitoSession +org.mockito.exceptions.misusing.RedundantListenerException +org.mockito.listeners.MockitoListener +org.mockito.internal.junit.TestFinishedEvent +org.mockito.listeners.MockCreationListener +org.mockito.internal.junit.MockitoTestListener +org.mockito.internal.listeners.AutoCleanableListener +org.mockito.internal.junit.UniversalTestListener +org.mockito.listeners.StubbingLookupListener +org.mockito.internal.junit.DefaultStubbingLookupListener +org.mockito.internal.framework.DefaultMockitoFramework +org.mockito.invocation.InvocationFactory +org.mockito.internal.util.Checks +org.mockito.internal.progress.ThreadSafeMockingProgress +org.mockito.internal.progress.ThreadSafeMockingProgress$1 +org.mockito.internal.progress.MockingProgress +org.mockito.internal.progress.MockingProgressImpl +org.mockito.internal.progress.ArgumentMatcherStorage +org.mockito.verification.VerificationStrategy +org.mockito.internal.progress.ArgumentMatcherStorageImpl +java.util.Stack +org.mockito.internal.progress.MockingProgressImpl$1 +org.mockito.MockitoAnnotations +org.mockito.internal.util.Supplier +org.mockito.internal.configuration.MockAnnotationProcessor$$Lambda$658/0x00007f42042d86c0 +org.mockito.MockSettings +org.mockito.mock.MockCreationSettings +org.mockito.internal.creation.settings.CreationSettings +org.mockito.internal.creation.MockSettingsImpl +org.mockito.mock.MockName +org.mockito.mock.SerializableMode +org.mockito.internal.util.MockCreationValidator +org.mockito.internal.util.MockUtil +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$1 +org.mockito.mock.MockType +org.mockito.internal.util.MockNameImpl +org.mockito.plugins.DoNotMockEnforcer$Cache +org.mockito.internal.handler.MockHandlerFactory +org.mockito.invocation.MockHandler +org.mockito.internal.handler.MockHandlerImpl +org.mockito.invocation.MatchableInvocation +org.mockito.stubbing.OngoingStubbing +org.mockito.stubbing.Stubbing +org.mockito.invocation.InvocationContainer +org.mockito.internal.invocation.MatchersBinder +org.mockito.internal.stubbing.InvocationContainerImpl +org.mockito.invocation.StubInfo +org.mockito.internal.verification.RegisteredInvocations +org.mockito.internal.verification.DefaultRegisteredInvocations +org.mockito.internal.stubbing.DoAnswerStyleStubbing +org.mockito.internal.handler.NullResultGuardian +org.mockito.internal.handler.InvocationNotifierHandler +org.mockito.listeners.MethodInvocationReport +org.mockito.internal.creation.bytebuddy.MockFeatures +net.bytebuddy.TypeCache$SimpleKey +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$MockitoMockKey +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$$Lambda$659/0x00007f42042dd328 +net.bytebuddy.TypeCache$LookupKey +org.mockito.internal.creation.bytebuddy.TypeSupport +java.lang.invoke.LambdaForm$DMH/0x00007f42042e0000 +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$$Lambda$660/0x00007f42042dd978 +org.mockito.internal.util.concurrent.WeakConcurrentMap$WeakKey +org.mockito.internal.util.concurrent.WeakConcurrentMap$LatentKey +net.bytebuddy.dynamic.ClassFileLocator$Simple +net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder +net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder +net.bytebuddy.description.field.FieldList$ForLoadedFields +net.bytebuddy.utility.FieldComparator +sun.reflect.annotation.TypeAnnotation$TypeAnnotationTarget +sun.reflect.annotation.TypeAnnotationParser +sun.reflect.annotation.TypeAnnotation +sun.reflect.annotation.TypeAnnotation$LocationInfo +sun.reflect.annotation.TypeAnnotation$LocationInfo$Location +sun.reflect.annotation.AnnotatedTypeFactory +sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$Simple +sun.reflect.generics.tree.TypeVariableSignature +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedTypeVariable +net.bytebuddy.description.type.TypeDescription$Generic$OfTypeVariable +net.bytebuddy.description.type.TypeDescription$Generic$OfTypeVariable$ForLoadedType +net.bytebuddy.description.type.TypeVariableToken +net.bytebuddy.description.type.TypeDescription$Generic$OfTypeVariable$ForLoadedType$TypeVariableBoundList +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForTypeVariableBoundType$OfFormalTypeVariable +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForTypeVariableBoundType$OfFormalTypeVariable$FormalTypeVariable +net.bytebuddy.description.type.$Proxy69 +sun.reflect.misc.ReflectUtil +net.bytebuddy.description.type.TypeDescription$Generic$OfTypeVariable$Symbolic +net.bytebuddy.description.method.ParameterDescription$Token +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType$Latent +java.lang.Class$EnclosingMethodInfo +net.bytebuddy.description.type.RecordComponentDescription$Token +net.bytebuddy.implementation.attribute.TypeAttributeAppender$ForInstrumentedType$Differentiating +net.bytebuddy.asm.AsmVisitorWrapper$AbstractBase +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$ParameterWritingVisitorWrapper +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$ParameterWritingVisitorWrapper$ParameterAddingClassVisitor +net.bytebuddy.asm.AsmVisitorWrapper$Compound +net.bytebuddy.pool.TypePool$Default +net.bytebuddy.pool.TypePool$Default$TypeExtractor +net.bytebuddy.pool.TypePool$Default$ReaderMode +net.bytebuddy.dynamic.scaffold.inline.InliningImplementationMatcher +net.bytebuddy.description.type.TypeDescription$Generic$OfTypeVariable$WithAnnotationOverlay +net.bytebuddy.description.type.TypeList$Generic$ForDetachedTypes$OfTypeVariables$AttachedTypeVariable +net.bytebuddy.description.method.ParameterDescription$Latent +net.bytebuddy.dynamic.scaffold.MethodGraph$Node$Simple +net.bytebuddy.dynamic.scaffold.MethodGraph$Simple +net.bytebuddy.dynamic.scaffold.MethodGraph$Empty +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RegistryContextClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor +net.bytebuddy.jar.asm.commons.Remapper +net.bytebuddy.jar.asm.commons.SimpleRemapper +net.bytebuddy.jar.asm.commons.ClassRemapper +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$OpenedClassRemapper +net.bytebuddy.dynamic.scaffold.inline.MethodRebaseResolver$Disabled +net.bytebuddy.dynamic.scaffold.inline.MethodRebaseResolver$Resolution +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$ContextRegistry +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$InitializationHandler +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$AttributeObtainingRecordComponentVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$AttributeObtainingFieldVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$AttributeObtainingMethodVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$CodePreservingMethodVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$DeduplicatingClassVisitor +net.bytebuddy.jar.asm.Context +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$InitializationHandler$Creating +net.bytebuddy.implementation.Implementation$Context$Disabled +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$ParameterWritingVisitorWrapper$MethodParameterStrippingMethodVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$SignatureKey +net.bytebuddy.matcher.DescriptorMatcher +jdk.internal.reflect.GeneratedMethodAccessor3 +software.amazon.lambda.powertools.kafka.serializers.PowertoolsDeserializer +net.bytebuddy.dynamic.loading.MultipleParentClassLoader$Builder +net.bytebuddy.dynamic.loading.MultipleParentClassLoader +net.bytebuddy.matcher.LatentMatcher$Disjunction +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$OptionalMethodMatchAdapter +net.bytebuddy.description.modifier.SynchronizationState +net.bytebuddy.dynamic.Transformer$ForMethod +net.bytebuddy.dynamic.Transformer$ForMethod$MethodModifierTransformer +net.bytebuddy.dynamic.Transformer$Compound +net.bytebuddy.implementation.attribute.MethodAttributeAppender$ForInstrumentedMethod +net.bytebuddy.implementation.attribute.MethodAttributeAppender$ForInstrumentedMethod$1 +net.bytebuddy.implementation.attribute.MethodAttributeAppender$ForInstrumentedMethod$2 +net.bytebuddy.implementation.attribute.MethodAttributeAppender$Factory$Compound +net.bytebuddy.description.modifier.FieldManifestation +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$Valuable$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$Valuable$AbstractBase$Adapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$FieldDefinitionAdapter +net.bytebuddy.implementation.attribute.FieldAttributeAppender$Factory +net.bytebuddy.description.field.FieldDescription$Token +net.bytebuddy.implementation.attribute.FieldAttributeAppender +net.bytebuddy.implementation.attribute.FieldAttributeAppender$ForInstrumentedField +net.bytebuddy.matcher.LatentMatcher$ForFieldToken +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default$Entry +net.bytebuddy.implementation.FieldAccessor +net.bytebuddy.implementation.FieldAccessor$FieldLocation +net.bytebuddy.implementation.FieldAccessor$PropertyConfigurable +net.bytebuddy.implementation.FieldAccessor$AssignerConfigurable +net.bytebuddy.implementation.FieldAccessor$OwnerTypeLocatable +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor$ForBeanProperty +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor$ForBeanProperty$1 +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor$ForBeanProperty$2 +net.bytebuddy.implementation.FieldAccessor$ForImplicitProperty +net.bytebuddy.implementation.FieldAccessor$FieldLocation$Relative +net.bytebuddy.implementation.FieldAccessor$FieldLocation$Prepared +net.bytebuddy.dynamic.scaffold.FieldLocator$ForClassHierarchy$Factory +net.bytebuddy.matcher.SuperTypeMatcher +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$TypeVariableDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ExceptionDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Initial$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodDefinitionAdapter +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$TypeVariableDefinition$Annotatable +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Annotatable +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$Annotatable +net.bytebuddy.description.method.ParameterDescription$Token$TypeList +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$Annotatable$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$Annotatable$AbstractBase$Adapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodDefinitionAdapter$SimpleParameterAnnotationAdapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodDefinitionAdapter$AnnotationAdapter +jdk.internal.reflect.GeneratedMethodAccessor4 +net.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup +net.bytebuddy.dynamic.loading.ClassInjector +net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase +net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup +net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles +net.bytebuddy.dynamic.loading.$Proxy70 +net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup +jdk.proxy2.$Proxy71 +net.bytebuddy.utility.JavaType +net.bytebuddy.description.type.TypeDescription$Latent +net.bytebuddy.utility.JavaType$LatentTypeWithSimpleName +net.bytebuddy.matcher.LatentMatcher$ForMethodToken +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForTypeVariableBoundType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForTypeVariableBoundType$AnnotatedTypeVariable +java.lang.reflect.AnnotatedTypeVariable +net.bytebuddy.description.type.$Proxy72 +net.bytebuddy.matcher.LatentMatcher$ForMethodToken$ResolvedMatcher +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reducing +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod +jdk.internal.reflect.GeneratedMethodAccessor5 +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate$Compiled$ForStaticCall +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodInvoker +net.bytebuddy.implementation.MethodDelegation$Appender +net.bytebuddy.implementation.bind.MethodDelegationBinder$Processor +net.bytebuddy.implementation.attribute.MethodAttributeAppender$Compound +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$WithoutTypeSubstitution +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod$AttachmentVisitor +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod$TransformedParameterList +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod$TransformedParameter +net.bytebuddy.implementation.FieldAccessor$ForImplicitProperty$Appender +net.bytebuddy.implementation.FieldAccessor$FieldLocation$Relative$Prepared +net.bytebuddy.dynamic.scaffold.FieldLocator$Resolution +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default$Compiled$Entry +net.bytebuddy.matcher.LatentMatcher$ForFieldToken$ResolvedMatcher +net.bytebuddy.description.field.FieldDescription$SignatureToken +net.bytebuddy.description.field.FieldDescription$AbstractBase +net.bytebuddy.description.field.FieldDescription$InDefinedShape$AbstractBase +net.bytebuddy.description.field.FieldDescription$Latent +net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool$Record$ForExplicitField +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnField +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodInvoker$Simple +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Builder +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding$Anonymous +net.bytebuddy.description.annotation.AnnotationValue$Loaded$AbstractBase +net.bytebuddy.description.annotation.AnnotationValue$ForEnumerationDescription$Loaded +net.bytebuddy.implementation.bind.ArgumentTypeResolver$ParameterIndexToken +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding$Unique +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Builder$Build +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnMethod +net.bytebuddy.implementation.bytecode.assign.TypeCasting +net.bytebuddy.description.type.TypeDefinition$SuperClassIterator +net.bytebuddy.description.field.FieldList$Explicit +net.bytebuddy.dynamic.scaffold.FieldLocator$Resolution$Simple +net.bytebuddy.implementation.bytecode.member.FieldAccess +net.bytebuddy.implementation.bytecode.member.FieldAccess$Defined +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher$AbstractFieldInstruction +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher$FieldGetInstruction +net.bytebuddy.implementation.bytecode.constant.MethodConstant +net.bytebuddy.implementation.bytecode.constant.MethodConstant$CanCache +net.bytebuddy.implementation.bytecode.constant.MethodConstant$ForMethod +net.bytebuddy.implementation.bytecode.constant.MethodConstant$CachedMethod +net.bytebuddy.implementation.bytecode.collection.CollectionFactory +net.bytebuddy.implementation.bytecode.collection.ArrayFactory +net.bytebuddy.implementation.bytecode.collection.ArrayFactory$ArrayCreator +net.bytebuddy.implementation.bytecode.collection.ArrayFactory$ArrayCreator$ForReferenceType +net.bytebuddy.implementation.bytecode.collection.ArrayFactory$ArrayStackManipulation +net.bytebuddy.implementation.auxiliary.MethodCallProxy$AssignableSignatureCall +net.bytebuddy.implementation.auxiliary.AuxiliaryType +net.bytebuddy.implementation.bytecode.constant.DefaultValue +net.bytebuddy.implementation.bytecode.constant.IntegerConstant +net.bytebuddy.implementation.bytecode.constant.LongConstant +net.bytebuddy.implementation.bytecode.constant.FloatConstant +net.bytebuddy.implementation.bytecode.constant.DoubleConstant +net.bytebuddy.implementation.bytecode.constant.NullConstant +net.bytebuddy.implementation.bind.MethodDelegationBinder$1 +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver$Resolution +net.bytebuddy.implementation.Implementation$Context$Default$FieldCacheEntry +net.bytebuddy.implementation.Implementation$Context$Default$CacheValueField +net.bytebuddy.implementation.auxiliary.MethodCallProxy +net.bytebuddy.implementation.MethodAccessorFactory$AccessType +net.bytebuddy.implementation.Implementation$Context$Default$AbstractPropertyAccessorMethod +net.bytebuddy.implementation.Implementation$Context$Default$AccessorMethod +net.bytebuddy.description.method.ParameterList$Explicit$ForTypes +net.bytebuddy.implementation.auxiliary.MethodCallProxy$PrecomputedMethodGraph +net.bytebuddy.implementation.auxiliary.MethodCallProxy$MethodCall +net.bytebuddy.implementation.auxiliary.MethodCallProxy$ConstructorCall +net.bytebuddy.implementation.auxiliary.MethodCallProxy$MethodCall$Appender +net.bytebuddy.implementation.auxiliary.MethodCallProxy$ConstructorCall$Appender +net.bytebuddy.implementation.bytecode.Removal +net.bytebuddy.implementation.bytecode.Removal$1 +net.bytebuddy.implementation.bytecode.Removal$2 +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnMethodParameter +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher$FieldPutInstruction +net.bytebuddy.implementation.bytecode.TypeCreation +net.bytebuddy.implementation.bytecode.Duplication$1 +net.bytebuddy.implementation.bytecode.Duplication$2 +net.bytebuddy.implementation.bytecode.Duplication$3 +sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeVariableImpl +net.bytebuddy.dynamic.scaffold.MethodGraph$Node$Unresolved +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation$Illegal +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding$Illegal +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Illegal +jdk.internal.reflect.GeneratedMethodAccessor6 +jdk.internal.reflect.GeneratedMethodAccessor7 +net.bytebuddy.dynamic.scaffold.FieldLocator$Resolution$Illegal +net.bytebuddy.implementation.bytecode.ByteCodeAppender$Simple +net.bytebuddy.dynamic.scaffold.TypeInitializer$Simple +net.bytebuddy.implementation.bytecode.ByteCodeAppender$Compound +net.bytebuddy.implementation.bytecode.constant.ClassConstant +net.bytebuddy.implementation.bytecode.constant.ClassConstant$ForReferenceType +net.bytebuddy.description.type.PackageDescription$ForLoadedPackage +software.amazon.lambda.powertools.kafka.serializers.PowertoolsDeserializer$MockitoMock$84ecoPEA +software.amazon.lambda.powertools.kafka.serializers.PowertoolsDeserializer$MockitoMock$84ecoPEA$auxiliary$0oONJkzb +software.amazon.lambda.powertools.kafka.serializers.PowertoolsDeserializer$MockitoMock$84ecoPEA$auxiliary$buK1SnlY +net.bytebuddy.TypeCache$StorageKey +org.mockito.plugins.MemberAccessor$OnConstruction +org.mockito.plugins.MemberAccessor$ConstructionDispatcher +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$661/0x00007f4204315f70 +java.lang.invoke.LambdaForm$MH/0x00007f4204310800 +java.lang.invoke.LambdaForm$MH/0x00007f4204310c00 +java.lang.invoke.LambdaForm$MH/0x00007f4204311000 +java.lang.invoke.LambdaForm$MH/0x00007f4204311400 +sun.invoke.util.ValueConversions$WrapperCache +java.lang.invoke.LambdaForm$MH/0x00007f4204311800 +java.lang.invoke.LambdaForm$MH/0x00007f4204311c00 +java.lang.invoke.LambdaForm$MH/0x00007f4204312000 +java.lang.invoke.LambdaForm$MH/0x00007f4204312400 +java.lang.invoke.LambdaForm$MH/0x00007f4204312800 +org.mockito.internal.util.reflection.InstrumentationMemberAccessor$$Lambda$662/0x00007f4204316198 +org.mockito.invocation.Invocation +org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport +org.mockito.exceptions.base.MockitoSerializationIssue +java.lang.invoke.LambdaForm$MH/0x00007f4204312c00 +java.lang.invoke.LambdaForm$MH/0x00007f4204313000 +java.lang.invoke.LambdaForm$MH/0x00007f4204313400 +java.lang.invoke.LambdaForm$DMH/0x00007f4204313800 +org.mockito.internal.configuration.IndependentAnnotationEngine$$Lambda$663/0x00007f4204316a40 +org.mockito.Spy +org.mockito.plugins.AnnotationEngine$NoAction +org.mockito.internal.util.collections.Sets +org.mockito.internal.util.collections.HashCodeAndEqualsSafeSet +org.mockito.internal.configuration.injection.scanner.InjectMocksScanner +org.mockito.InjectMocks +org.mockito.internal.configuration.injection.scanner.MockScanner +org.mockito.internal.util.reflection.FieldReader +org.mockito.internal.util.collections.HashCodeAndEqualsMockWrapper +java.lang.invoke.DirectMethodHandle$StaticAccessor +java.lang.invoke.LambdaForm$MH/0x00007f4204318000 +org.mockito.internal.util.collections.HashCodeAndEqualsSafeSet$1 +org.mockito.internal.configuration.DefaultInjectionEngine +org.mockito.internal.configuration.injection.MockInjection +org.mockito.internal.configuration.injection.MockInjection$OngoingMockInjection +org.mockito.internal.configuration.injection.MockInjectionStrategy +org.mockito.internal.configuration.injection.ConstructorInjection +org.mockito.internal.configuration.injection.PropertyAndSetterInjection +org.mockito.internal.configuration.injection.SpyOnInjectedFieldsHandler +org.mockito.internal.configuration.injection.MockInjectionStrategy$1 +org.mockito.internal.util.reflection.FieldInitializer$ConstructorArgumentResolver +org.mockito.internal.configuration.injection.filter.MockCandidateFilter +org.mockito.internal.configuration.injection.filter.TypeBasedCandidateFilter +org.mockito.internal.configuration.injection.filter.NameBasedCandidateFilter +org.mockito.internal.configuration.injection.filter.TerminalMockCandidateFilter +org.mockito.internal.configuration.InjectingAnnotationEngine$$Lambda$664/0x00007f420431de08 +org.mockito.internal.configuration.InjectingAnnotationEngine$$Lambda$665/0x00007f420431e030 +com.amazonaws.services.lambda.runtime.CustomPojoSerializer +org.crac.Resource +software.amazon.lambda.powertools.kafka.PowertoolsSerializer +software.amazon.lambda.powertools.kafka.serializers.KafkaJsonDeserializer +org.apache.kafka.common.header.Headers +software.amazon.lambda.powertools.kafka.serializers.KafkaAvroDeserializer +org.apache.avro.io.DatumReader +software.amazon.lambda.powertools.kafka.serializers.KafkaProtobufDeserializer +software.amazon.lambda.powertools.kafka.testutils.InputStreamHandler +org.crac.Core +org.crac.Context +org.crac.GlobalContextWrapper +org.crac.Core$Compat +org.crac.CheckpointException +org.crac.RestoreException +org.assertj.core.internal.Strings +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$666/0x00007f420431a8a0 +org.mockito.junit.jupiter.MockitoExtension$$Lambda$667/0x00007f420431aac8 +org.mockito.junit.jupiter.MockitoExtension$$Lambda$668/0x00007f420431acf8 +org.mockito.junit.jupiter.MockitoExtension$$Lambda$669/0x00007f420431af28 +org.mockito.internal.framework.DefaultMockitoSession$1 +org.mockito.internal.junit.UniversalTestListener$1 +org.mockito.internal.junit.UnusedStubbingsFinder +org.mockito.internal.junit.UnusedStubbings +org.mockito.internal.invocation.finder.AllInvocationsFinder +org.mockito.internal.stubbing.StubbingComparator +org.mockito.internal.invocation.InvocationComparator +java.util.IdentityHashMap$IdentityHashMapIterator +java.util.IdentityHashMap$KeyIterator +org.mockito.internal.util.DefaultMockingDetails +java.util.TreeMap$TreeMapSpliterator +java.util.TreeMap$KeySpliterator +org.mockito.internal.stubbing.UnusedStubbingReporting +org.mockito.internal.junit.UnusedStubbingsFinder$$Lambda$670/0x00007f4204319750 +org.assertj.core.api.ThrowableAssert$ThrowingCallable +software.amazon.lambda.powertools.kafka.PowertoolsSerializerTest$$Lambda$671/0x00007f4204319ba0 +org.assertj.core.internal.Throwables +org.assertj.core.internal.CommonValidations +org.junit.jupiter.api.extension.TemplateInvocationValidationException +org.junit.jupiter.params.ParameterizedDeclarationContext +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext +org.junit.jupiter.engine.descriptor.TemplateExecutor +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$TestTemplateExecutor +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$672/0x00007f42043209d0 +org.junit.jupiter.api.RepeatedTest +org.junit.jupiter.params.ParameterizedTestContext +org.junit.jupiter.params.ResolverFacade +org.junit.jupiter.params.support.ParameterDeclaration +org.junit.jupiter.params.support.ParameterDeclarations +org.junit.jupiter.params.ResolverFacade$ResolvableParameterDeclaration +org.junit.jupiter.params.support.FieldContext +org.junit.jupiter.params.ResolverFacade$FieldParameterDeclaration +org.junit.jupiter.params.ResolverFacade$Resolver +org.junit.jupiter.params.ResolverFacade$ExecutableParameterDeclaration +org.junit.jupiter.params.aggregator.ArgumentsAccessor +org.junit.jupiter.params.aggregator.AggregateWith +java.util.TreeMap$Values +java.util.TreeMap$ValueIterator +org.junit.jupiter.params.ResolverFacade$DefaultParameterDeclarations +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$673/0x00007f4204322990 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$674/0x00007f4204322bb8 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PartialFormatter +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$675/0x00007f42043231f8 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$676/0x00007f4204323420 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$CachingByArgumentsLengthPartialFormatter +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$677/0x00007f4204323890 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$ArgumentSetNameFormatter +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PartialFormatters +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$ArgumentsContext +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PartialFormatter$$Lambda$678/0x00007f4204324140 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$679/0x00007f4204324360 +java.lang.invoke.LambdaForm$DMH/0x00007f4204318400 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$680/0x00007f4204324588 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$681/0x00007f42043247c8 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PlaceholderPosition +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$682/0x00007f4204324c00 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$683/0x00007f4204325020 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$684/0x00007f4204325260 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$685/0x00007f42043254a8 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$686/0x00007f42043256f0 +org.junit.jupiter.params.provider.Arguments +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$687/0x00007f4204325b38 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$688/0x00007f4204325d80 +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$689/0x00007f4204325fa8 +org.junit.jupiter.params.ParameterizedTestSpiInstantiator +org.junit.jupiter.params.ParameterizedTestSpiInstantiator$$Lambda$690/0x00007f42043263e8 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$691/0x00007f4204326610 +org.junit.jupiter.params.support.AnnotationConsumerInitializer +org.junit.jupiter.params.support.AnnotationConsumerInitializer$AnnotationConsumingMethodSignature +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$692/0x00007f4204326e80 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$693/0x00007f42043270c0 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$694/0x00007f4204327310 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$695/0x00007f4204327558 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$696/0x00007f42043277b0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$697/0x00007f4204327a00 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$698/0x00007f4204327c20 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$699/0x00007f4204328000 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$700/0x00007f4204328220 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$701/0x00007f4204328470 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$702/0x00007f42043286c8 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$703/0x00007f4204328908 +org.junit.jupiter.params.provider.AnnotationBasedArgumentsProvider$$Lambda$704/0x00007f4204328b40 +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext$$Lambda$705/0x00007f4204328d88 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$706/0x00007f4204328fc8 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$707/0x00007f4204329210 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$708/0x00007f4204329458 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$709/0x00007f42043296a0 +org.junit.jupiter.params.provider.ArgumentsUtils +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$710/0x00007f4204329ae8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$711/0x00007f4204329d28 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$712/0x00007f4204329f50 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$713/0x00007f420432a198 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$714/0x00007f420432a3f0 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$715/0x00007f420432a618 +org.junit.jupiter.params.provider.Arguments$$Lambda$716/0x00007f420432aa38 +org.junit.jupiter.params.ParameterizedInvocationContext +org.junit.jupiter.params.ParameterizedTestInvocationContext +java.util.function.IntUnaryOperator +java.lang.invoke.LambdaForm$DMH/0x00007f420432c000 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$717/0x00007f420432b0d0 +org.junit.jupiter.params.EvaluatedArgumentSet +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$718/0x00007f420432b560 +java.lang.invoke.LambdaForm$DMH/0x00007f420432c400 +org.junit.jupiter.params.provider.Arguments$ArgumentSet +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor$$Lambda$719/0x00007f420432e000 +org.junit.jupiter.api.Named +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$720/0x00007f420432e420 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$721/0x00007f420432e660 +java.util.stream.ReferencePipeline$$Lambda$722/0x00007f420412bc70 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$MessageFormatPartialFormatter +java.util.stream.Streams$RangeIntSpliterator +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$723/0x00007f420432ead8 +java.util.stream.IntPipeline$1 +java.util.stream.IntPipeline$1$1 +org.junit.jupiter.params.ResolverFacade$$Lambda$724/0x00007f420432ed00 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$725/0x00007f420432ef40 +java.text.MessageFormat +java.text.MessageFormat$Field +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$726/0x00007f420432f180 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$727/0x00007f420432f3b8 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$728/0x00007f420432f5f0 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$729/0x00007f420432f818 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$730/0x00007f420432fa50 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor$$Lambda$731/0x00007f420432fc78 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DynamicTaskState +org.junit.platform.engine.support.hierarchical.NodeTestTask$DynamicTaskState$$Lambda$732/0x00007f420432d200 +org.junit.jupiter.params.ParameterizedInvocationParameterResolver +org.junit.jupiter.params.ParameterizedTestMethodParameterResolver +org.junit.jupiter.params.ResolutionCache +org.junit.jupiter.params.ResolutionCache$$Lambda$733/0x00007f420432dac0 +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor$$Lambda$734/0x00007f420432dce0 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$735/0x00007f420432c800 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$736/0x00007f420432ca40 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$737/0x00007f420432cc98 +org.junit.jupiter.params.ParameterizedInvocationContext$CloseableArgument +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$738/0x00007f4204330248 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$739/0x00007f4204330488 +org.junit.jupiter.params.ArgumentCountValidator +org.junit.jupiter.params.ArgumentCountValidator$$Lambda$740/0x00007f42043308d8 +org.junit.jupiter.params.ArgumentCountValidator$1 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$741/0x00007f4204330d28 +org.junit.jupiter.params.aggregator.DefaultArgumentsAccessor +org.junit.jupiter.params.aggregator.ArgumentAccessException +org.junit.jupiter.params.aggregator.DefaultArgumentsAccessor$$Lambda$742/0x00007f42043314d8 +org.junit.jupiter.params.aggregator.DefaultArgumentsAccessor$$Lambda$743/0x00007f4204331710 +org.junit.jupiter.params.support.ParameterInfo +org.junit.jupiter.params.DefaultParameterInfo +org.junit.jupiter.engine.execution.DefaultParameterContext +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$744/0x00007f4204332048 +org.mockito.junit.jupiter.resolver.CompositeParameterResolver$$Lambda$745/0x00007f42043322a0 +org.junit.jupiter.params.ResolverFacade$$Lambda$746/0x00007f42043324f8 +org.junit.jupiter.params.ResolverFacade$$Lambda$747/0x00007f4204332718 +java.lang.invoke.LambdaForm$DMH/0x00007f4204334000 +org.junit.jupiter.params.ResolverFacade$$Lambda$748/0x00007f4204332938 +java.lang.invoke.LambdaForm$DMH/0x00007f4204334400 +java.lang.invoke.LambdaForm$DMH/0x00007f4204334800 +java.lang.invoke.LambdaForm$MH/0x00007f4204334c00 +org.junit.jupiter.params.ResolverFacade$$Lambda$749/0x00007f4204332b60 +org.junit.jupiter.params.converter.ConvertWith +org.junit.jupiter.params.ResolverFacade$$Lambda$750/0x00007f4204332fa8 +org.junit.jupiter.params.converter.ArgumentConverter +org.junit.jupiter.params.ResolverFacade$$Lambda$751/0x00007f42043333e8 +org.junit.jupiter.params.ResolverFacade$$Lambda$752/0x00007f4204333630 +org.junit.jupiter.params.ResolverFacade$Converter +org.junit.jupiter.params.ResolverFacade$$Lambda$753/0x00007f4204333ab8 +org.junit.jupiter.params.ResolverFacade$$Lambda$754/0x00007f4204333cf8 +org.junit.jupiter.params.converter.DefaultArgumentConverter +org.junit.platform.commons.support.conversion.ConversionException +org.junit.jupiter.params.converter.ArgumentConversionException +org.junit.jupiter.params.converter.DefaultArgumentConverter$LocaleConversionFormat +org.junit.jupiter.params.converter.DefaultArgumentConverter$$Lambda$755/0x00007f4204336940 +org.junit.jupiter.params.ResolverFacade$ExecutableParameterDeclaration$$Lambda$756/0x00007f4204336b80 +org.junit.jupiter.params.ResolverFacade$ExecutableParameterDeclaration$$Lambda$757/0x00007f4204336dd8 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$758/0x00007f4204337000 +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$759/0x00007f4204337240 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$760/0x00007f4204337468 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$761/0x00007f42043376c0 +org.apache.avro.Schema$Parser +org.apache.avro.SchemaParseException +com.fasterxml.jackson.core.exc.StreamReadException +com.fasterxml.jackson.core.JsonParseException +org.apache.avro.NameValidator +org.apache.avro.NameValidator$Result +org.apache.avro.NameValidator$1 +org.apache.avro.NameValidator$2 +org.apache.avro.NameValidator$3 +org.apache.avro.ParseContext +org.apache.avro.Schema$Type +org.apache.avro.AvroTypeException +org.apache.avro.util.SchemaVisitor +org.apache.avro.SchemaParser$ParseResult +org.apache.avro.util.internal.Accessor$JsonPropertiesAccessor +org.apache.avro.JsonProperties$1 +org.apache.avro.Schema$StringSchema +org.apache.avro.Schema$BytesSchema +org.apache.avro.Schema$IntSchema +org.apache.avro.Schema$LongSchema +org.apache.avro.Schema$FloatSchema +org.apache.avro.Schema$DoubleSchema +org.apache.avro.Schema$BooleanSchema +org.apache.avro.Schema$NullSchema +org.apache.avro.Schema$MapSchema +org.apache.avro.Schema$UnionSchema +org.apache.avro.Schema$Field +org.apache.avro.Schema$ArraySchema +org.apache.avro.Schema$NamedSchema +org.apache.avro.Schema$FixedSchema +org.apache.avro.Schema$RecordSchema +org.apache.avro.Schema$EnumSchema +org.apache.avro.util.internal.Accessor +org.apache.avro.JsonProperties$Null +org.apache.avro.Schema$$Lambda$762/0x00007f420433cd10 +org.apache.avro.util.internal.ThreadLocalWithInitial +org.apache.avro.Schema$$Lambda$763/0x00007f420433d138 +org.apache.avro.Schema$$Lambda$764/0x00007f420433d358 +org.apache.avro.Schema$$Lambda$765/0x00007f420433d578 +com.fasterxml.jackson.core.io.ContentReference +com.fasterxml.jackson.core.util.BufferRecyclers +com.fasterxml.jackson.core.util.BufferRecycler +com.fasterxml.jackson.core.io.IOContext +com.fasterxml.jackson.core.util.TextBuffer +com.fasterxml.jackson.core.util.ReadConstrainedTextBuffer +com.fasterxml.jackson.core.exc.InputCoercionException +com.fasterxml.jackson.core.io.JsonEOFException +com.fasterxml.jackson.core.JsonStreamContext +com.fasterxml.jackson.core.json.JsonReadContext +com.fasterxml.jackson.core.StreamReadCapability +com.fasterxml.jackson.core.util.JacksonFeatureSet +com.fasterxml.jackson.core.JsonToken +com.fasterxml.jackson.databind.util.ArrayIterator +com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer +com.fasterxml.jackson.databind.node.ArrayNode +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer$ObjectDeserializer +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer$ArrayDeserializer +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WeightedValue +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Node +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$AddTask +com.fasterxml.jackson.databind.util.LinkedNode +com.fasterxml.jackson.databind.annotation.JsonTypeResolver +com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer$ContainerStack +com.fasterxml.jackson.core.util.InternCache +com.fasterxml.jackson.databind.node.JsonNodeType +org.apache.avro.Schema$Name +java.lang.invoke.LambdaForm$MH/0x00007f4204348000 +java.lang.invoke.LambdaForm$MH/0x00007f4204348400 +java.lang.invoke.LambdaForm$MH/0x00007f4204348800 +java.lang.invoke.LambdaForm$MH/0x00007f4204348c00 +java.lang.invoke.LambdaForm$MH/0x00007f4204349000 +java.lang.invoke.LambdaForm$MH/0x00007f4204349400 +java.lang.invoke.LambdaForm$MH/0x00007f4204349800 +org.apache.avro.JsonProperties$2 +org.apache.avro.Schema$Field$Order +org.apache.avro.util.internal.Accessor$FieldAccessor +org.apache.avro.Schema$Field$1 +org.apache.avro.Schema$$Lambda$766/0x00007f4204345390 +org.apache.avro.util.MapEntry +org.apache.avro.LogicalTypes +org.apache.avro.LogicalType +org.apache.avro.LogicalTypes$Uuid +org.apache.avro.LogicalTypes$Duration +org.apache.avro.LogicalTypes$TimestampMillis +org.apache.avro.LogicalTypes$Decimal +org.apache.avro.LogicalTypes$BigDecimal +org.apache.avro.LogicalTypes$Date +org.apache.avro.LogicalTypes$TimestampMicros +org.apache.avro.LogicalTypes$TimestampNanos +org.apache.avro.LogicalTypes$TimeMillis +org.apache.avro.LogicalTypes$TimeMicros +org.apache.avro.LogicalTypes$LocalTimestampMicros +org.apache.avro.LogicalTypes$LocalTimestampMillis +org.apache.avro.LogicalTypes$LocalTimestampNanos +org.apache.avro.LogicalTypes$LogicalTypeFactory +org.apache.avro.Schema$LockableArrayList +org.apache.avro.util.SchemaResolver$ResolvingVisitor +org.apache.avro.ParseContext$$Lambda$767/0x00007f420434c240 +org.apache.avro.ParseContext$$Lambda$768/0x00007f420434c488 +org.apache.avro.util.Schemas +org.apache.avro.util.Schemas$1 +org.apache.avro.util.SchemaVisitor$SchemaVisitorAction +org.apache.avro.util.Schemas$$Lambda$769/0x00007f420434cf10 +org.apache.avro.util.SchemaResolver +org.apache.avro.util.Schemas$$Lambda$770/0x00007f420434d360 +org.apache.avro.util.Schemas$$Lambda$771/0x00007f420434d588 +org.apache.avro.util.Schemas$$Lambda$772/0x00007f420434d7c0 +org.apache.avro.util.Schemas$$Lambda$773/0x00007f420434da00 +java.util.ArrayDeque$DescendingIterator +org.apache.avro.util.SchemaResolver$1 +org.apache.avro.JsonProperties$2$1 +org.apache.avro.JsonProperties$2$1$1 +org.apache.avro.util.SchemaResolver$ResolvingVisitor$$Lambda$774/0x00007f420434e4a0 +org.apache.avro.util.SchemaResolver$ResolvingVisitor$$Lambda$775/0x00007f420434e6d8 +org.apache.avro.util.SchemaResolver$ResolvingVisitor$$Lambda$776/0x00007f420434e910 +org.apache.avro.util.SchemaResolver$ResolvingVisitor$$Lambda$777/0x00007f420434eb48 +org.apache.avro.ParseContext$$Lambda$778/0x00007f420434ed70 +org.apache.avro.UnresolvedUnionException +org.apache.avro.AvroMissingFieldException +org.apache.avro.specific.ExternalizableInput +org.apache.avro.specific.ExternalizableOutput +org.apache.avro.util.springframework.ConcurrentReferenceHashMap +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$Task +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$2 +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$3 +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$1 +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$4 +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$5 +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$ReferenceType +org.apache.avro.util.springframework.Assert +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$Segment +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$ReferenceManager +org.apache.avro.util.springframework.ConcurrentReferenceHashMap$Reference +org.apache.avro.util.Utf8 +org.apache.avro.util.internal.ClassValueCache +org.apache.avro.util.internal.ClassValueCache$1 +org.apache.avro.specific.SpecificData$$Lambda$779/0x00007f42043504a8 +org.apache.avro.specific.SpecificData$$Lambda$780/0x00007f42043506f0 +org.apache.avro.specific.SpecificData$$Lambda$781/0x00007f4204350930 +org.apache.avro.specific.SpecificData$1 +org.apache.avro.message.RawMessageEncoder +org.apache.avro.message.BinaryMessageEncoder$V1MessageEncoder +org.apache.avro.message.RawMessageEncoder$BufferOutputStream +java.security.GeneralSecurityException +java.security.NoSuchAlgorithmException +org.apache.avro.message.RawMessageEncoder$$Lambda$782/0x00007f42043514c8 +org.apache.avro.generic.GenericDatumWriter +org.apache.avro.specific.SpecificDatumWriter +org.apache.avro.path.PathTracingException +org.apache.avro.path.TracingNullPointException +org.apache.avro.path.TracingClassCastException +org.apache.avro.path.TracingAvroTypeException +org.apache.avro.path.PathElement +java.util.ConcurrentModificationException +org.apache.avro.SchemaNormalization +org.apache.avro.SchemaNormalization$1 +java.lang.invoke.LambdaForm$MH/0x00007f4204354000 +java.lang.invoke.LambdaForm$MH/0x00007f4204354400 +java.lang.invoke.LambdaForm$MH/0x00007f4204354800 +java.lang.invoke.LambdaForm$MH/0x00007f4204354c00 +java.lang.invoke.LambdaForm$MH/0x00007f4204355000 +java.lang.invoke.LambdaForm$MH/0x00007f4204355400 +org.apache.avro.SchemaNormalization$FP64 +org.apache.avro.util.ReusableByteArrayInputStream +org.apache.avro.util.ReusableByteBufferInputStream +org.apache.avro.message.BadHeaderException +org.apache.avro.message.MissingSchemaException +org.apache.avro.message.MessageDecoder$BaseDecoder$$Lambda$783/0x00007f42043538f8 +org.apache.avro.message.MessageDecoder$BaseDecoder$$Lambda$784/0x00007f4204353b18 +org.apache.avro.message.BinaryMessageDecoder$$Lambda$785/0x00007f4204353d38 +org.apache.avro.message.BinaryMessageDecoder$$Lambda$786/0x00007f4204356000 +org.apache.avro.message.RawMessageDecoder +org.apache.avro.generic.GenericDatumReader +org.apache.avro.specific.SpecificDatumReader +org.apache.avro.util.WeakIdentityHashMap +org.apache.avro.generic.GenericDatumReader$$Lambda$787/0x00007f4204356ee8 +org.apache.avro.generic.GenericDatumReader$ReaderCache +org.apache.avro.generic.GenericDatumReader$$Lambda$788/0x00007f4204357328 +java.util.Base64 +java.util.Base64$Encoder +org.apache.avro.io.EncoderFactory +org.apache.avro.io.EncoderFactory$DefaultEncoderFactory +org.apache.avro.io.DirectBinaryEncoder +org.apache.avro.io.BufferedBinaryEncoder +org.apache.avro.io.BlockingDirectBinaryEncoder +org.apache.avro.io.BlockingBinaryEncoder +org.apache.avro.io.BufferedBinaryEncoder$ByteSink +org.apache.avro.io.BufferedBinaryEncoder$OutputStreamSink +java.nio.channels.Channels +java.nio.channels.InterruptibleChannel +java.nio.channels.spi.AbstractInterruptibleChannel +java.nio.channels.Channels$WritableByteChannelImpl +org.apache.avro.generic.GenericDatumWriter$1 +org.apache.avro.io.BinaryData +org.apache.avro.io.BinaryData$Decoders +org.apache.avro.io.BinaryData$$Lambda$789/0x00007f4204358b68 +org.apache.avro.io.BinaryData$HashData +org.apache.avro.io.BinaryData$$Lambda$790/0x00007f4204358fa0 +software.amazon.lambda.powertools.kafka.testutils.TestUtils$1 +com.amazonaws.services.lambda.runtime.events.KafkaEvent +com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper +com.fasterxml.jackson.core.json.UTF8StreamJsonParser +com.fasterxml.jackson.core.io.MergedStream +com.fasterxml.jackson.core.io.UTF32Reader +java.io.CharConversionException +com.fasterxml.jackson.core.JsonEncoding +com.fasterxml.jackson.databind.type.ClassStack +com.fasterxml.jackson.databind.introspect.AnnotationCollector$OneCollector +com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector +com.fasterxml.jackson.annotation.JsonAutoDetect +com.fasterxml.jackson.annotation.JsonIdentityInfo +com.fasterxml.jackson.databind.ext.OptionalHandlerFactory +org.w3c.dom.Node +org.w3c.dom.Document +com.fasterxml.jackson.databind.ext.Java7Handlers +com.fasterxml.jackson.databind.ext.Java7HandlersImpl +com.fasterxml.jackson.databind.ext.NioPathSerializer +com.fasterxml.jackson.databind.ext.NioPathDeserializer +com.fasterxml.jackson.databind.deser.std.JdkDeserializers +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer +com.fasterxml.jackson.databind.deser.std.UUIDDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicBooleanDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicIntegerDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicLongDeserializer +com.fasterxml.jackson.databind.deser.std.ByteBufferDeserializer +com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer +com.fasterxml.jackson.databind.deser.std.StdNodeBasedDeserializer +com.fasterxml.jackson.databind.deser.std.ThreadGroupDeserializer +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBuilderDeserializer +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBufferDeserializer +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$Std +java.net.InetAddress +com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator +com.fasterxml.jackson.databind.util.BeanUtil +com.fasterxml.jackson.databind.annotation.JsonValueInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators +com.fasterxml.jackson.databind.deser.ValueInstantiator +com.fasterxml.jackson.databind.deser.ValueInstantiator$Base +com.fasterxml.jackson.databind.deser.std.JsonLocationInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$JDKValueInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ArrayListInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$HashSetInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$LinkedListInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$TreeSetInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$LinkedHashSetInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ConstantValueInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$LinkedHashMapInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$HashMapInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ConcurrentHashMapInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$TreeMapInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$PropertiesInstantiator +com.fasterxml.jackson.core.JsonLocation +com.fasterxml.jackson.databind.introspect.PotentialCreators +com.fasterxml.jackson.databind.introspect.CollectorBase +com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector +com.fasterxml.jackson.databind.introspect.AnnotationMap +com.fasterxml.jackson.databind.introspect.TypeResolutionContext$Basic +com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector$FieldBuilder +com.fasterxml.jackson.annotation.JsonKey +com.fasterxml.jackson.annotation.JsonValue +com.fasterxml.jackson.annotation.JsonAnyGetter +com.fasterxml.jackson.annotation.JsonAnySetter +com.fasterxml.jackson.databind.PropertyName +com.fasterxml.jackson.annotation.JsonSetter +com.fasterxml.jackson.annotation.JsonProperty +com.fasterxml.jackson.annotation.JsonAutoDetect$1 +com.fasterxml.jackson.annotation.PropertyAccessor +com.fasterxml.jackson.annotation.JsonIgnore +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$WithMember +com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty +com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty$Type +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$Linked +com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector +com.amazonaws.services.lambda.runtime.events.KafkaEvent$KafkaEventBuilder +com.fasterxml.jackson.databind.introspect.MemberKey +com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector$MethodBuilder +com.fasterxml.jackson.databind.introspect.AnnotatedMethodMap +com.fasterxml.jackson.annotation.JsonGetter +com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector +com.fasterxml.jackson.databind.introspect.MethodGenericTypeResolver +com.fasterxml.jackson.annotation.JsonCreator +com.fasterxml.jackson.databind.introspect.PotentialCreator +com.fasterxml.jackson.annotation.JsonCreator$Mode +com.fasterxml.jackson.databind.cfg.ConstructorDetector +com.fasterxml.jackson.databind.cfg.ConstructorDetector$SingleArgConstructor +sun.reflect.generics.scope.ConstructorScope +com.amazonaws.services.lambda.runtime.events.KafkaEvent$KafkaEventRecord +com.fasterxml.jackson.databind.type.TypeBindings$TypeParamStash +com.fasterxml.jackson.databind.type.TypeBindings$AsKey +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$5 +com.fasterxml.jackson.annotation.JsonProperty$Access +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$6 +com.fasterxml.jackson.annotation.JacksonInject +com.fasterxml.jackson.databind.annotation.JsonNaming +com.fasterxml.jackson.annotation.JsonPropertyOrder +com.fasterxml.jackson.annotation.JsonPropertyDescription +com.fasterxml.jackson.databind.PropertyMetadata +com.fasterxml.jackson.databind.deser.impl.CreatorCollector +com.fasterxml.jackson.databind.deser.std.StdValueInstantiator +com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder +com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty +com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer +com.fasterxml.jackson.annotation.JsonIgnoreProperties +com.fasterxml.jackson.annotation.JsonIgnoreProperties$Value +com.fasterxml.jackson.annotation.JsonIncludeProperties +com.fasterxml.jackson.annotation.JsonIncludeProperties$Value +com.fasterxml.jackson.databind.util.IgnorePropertiesUtil +com.fasterxml.jackson.annotation.JsonIgnoreType +com.fasterxml.jackson.databind.deser.impl.FailingDeserializer +com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider +com.fasterxml.jackson.databind.util.AccessPattern +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$2 +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$4 +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$1 +com.fasterxml.jackson.annotation.JsonAlias +com.fasterxml.jackson.annotation.JsonFormat$Feature +com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap +com.fasterxml.jackson.databind.exc.IgnoredPropertyException +com.fasterxml.jackson.databind.deser.SettableBeanProperty$Delegating +com.fasterxml.jackson.databind.deser.impl.ManagedReferenceProperty +com.fasterxml.jackson.databind.deser.impl.ObjectIdReferenceProperty +com.fasterxml.jackson.databind.deser.impl.InnerClassProperty +com.fasterxml.jackson.databind.deser.impl.MergingSettableBeanProperty +com.fasterxml.jackson.databind.deser.impl.ReadableObjectId$Referring +com.fasterxml.jackson.databind.deser.BeanDeserializer$BeanReferring +com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer +com.fasterxml.jackson.databind.deser.BasicDeserializerFactory$ContainerDefaultMappings +java.util.concurrent.ConcurrentNavigableMap +java.util.concurrent.ConcurrentSkipListMap +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringKD +com.fasterxml.jackson.databind.deser.ContextualKeyDeserializer +com.amazonaws.services.lambda.runtime.events.KafkaEvent$SchemaMetadata +com.amazonaws.services.lambda.runtime.events.KafkaEvent$KafkaEventRecord$KafkaEventRecordBuilder +sun.reflect.generics.tree.IntSignature +sun.reflect.generics.tree.LongSignature +sun.reflect.generics.tree.ByteSignature +com.fasterxml.jackson.databind.deser.std.NumberDeserializers +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$PrimitiveOrWrapperDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BooleanDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$LongDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$DoubleDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$CharacterDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ByteDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ShortDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$FloatDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$NumberDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigDecimalDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigIntegerDeserializer +com.amazonaws.services.lambda.runtime.events.KafkaEvent$SchemaMetadata$SchemaMetadataBuilder +jdk.internal.reflect.GeneratedMethodAccessor8 +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$IntDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$LongDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$ByteDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$ShortDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$FloatDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$DoubleDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$BooleanDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$CharDeser +com.fasterxml.jackson.databind.exc.InvalidNullException +com.fasterxml.jackson.databind.annotation.JacksonStdImpl +com.fasterxml.jackson.annotation.JacksonAnnotation +jdk.proxy2.$Proxy73 +com.fasterxml.jackson.core.io.NumberInput +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializer$SchemaRegistryType +java.util.Base64$Decoder +org.apache.avro.specific.SpecificData$2 +org.apache.avro.specific.SpecificData$$Lambda$791/0x00007f420437eb90 +org.apache.avro.util.MapUtil +org.apache.avro.util.MapUtil$$Lambda$792/0x00007f420437efe0 +org.apache.avro.util.ClassUtils +org.apache.avro.io.DecoderFactory +org.apache.avro.io.DecoderFactory$DefaultDecoderFactory +org.apache.avro.io.DirectBinaryDecoder +org.apache.avro.InvalidNumberEncodingException +org.apache.avro.io.BinaryDecoder$ByteSource +org.apache.avro.io.BinaryDecoder$InputStreamByteSource +org.apache.avro.io.BinaryDecoder$ByteArrayByteSource +org.apache.avro.io.BinaryDecoder$BufferAccessor +org.apache.avro.util.WeakIdentityHashMap$IdentityWeakReference +org.apache.avro.io.parsing.ValidatingGrammarGenerator +org.apache.avro.io.parsing.ResolvingGrammarGenerator +org.apache.avro.util.internal.Accessor$ResolvingGrammarGeneratorAccessor +org.apache.avro.io.parsing.ResolvingGrammarGenerator$1 +org.apache.avro.io.parsing.Symbol +org.apache.avro.io.parsing.Symbol$ImplicitAction +org.apache.avro.io.parsing.Symbol$SkipAction +org.apache.avro.Resolver +org.apache.avro.Resolver$Action +org.apache.avro.Resolver$DoNothing +org.apache.avro.Resolver$ErrorAction +org.apache.avro.Resolver$Container +org.apache.avro.Resolver$1 +org.apache.avro.Resolver$RecordAdjust +org.apache.avro.Schema$SeenPair +org.apache.avro.Resolver$Action$Type +org.apache.avro.generic.GenericData$InstanceSupplier +java.lang.invoke.LambdaForm$DMH/0x00007f4204384000 +org.apache.avro.generic.GenericData$$Lambda$793/0x00007f4204380200 +org.apache.avro.Resolver$Skip +org.apache.avro.Resolver$Promote +org.apache.avro.Resolver$ReaderUnion +org.apache.avro.Resolver$EnumAdjust +org.apache.avro.io.parsing.Symbol$Terminal +org.apache.avro.io.parsing.Symbol$WriterUnionAction +org.apache.avro.io.parsing.Symbol$Repeater +org.apache.avro.io.parsing.Symbol$ResolvingAction +org.apache.avro.io.parsing.Symbol$Root +org.apache.avro.io.parsing.Symbol$Sequence +org.apache.avro.io.parsing.Symbol$ErrorAction +org.apache.avro.io.parsing.Symbol$Alternative +org.apache.avro.io.parsing.Symbol$Kind +org.apache.avro.io.parsing.Symbol$FieldOrderAction +org.apache.avro.io.parsing.ResolvingGrammarGenerator$2 +org.apache.avro.io.parsing.Parser +org.apache.avro.io.parsing.SkipParser +org.apache.avro.generic.GenericDatumReader$1 +org.apache.avro.generic.GenericData$StringType +org.apache.avro.specific.SpecificData$SchemaConstructable +org.apache.avro.generic.GenericDatumReader$IdentitySchemaKey +org.apache.avro.generic.GenericDatumReader$ReaderCache$$Lambda$794/0x00007f4204383748 +org.apache.avro.specific.SpecificDatumReader$1 +org.apache.avro.SystemLimitException +org.apache.kafka.common.header.internals.RecordHeaders +org.apache.kafka.common.header.Header +org.apache.kafka.clients.consumer.ConsumerRecord +org.apache.kafka.common.record.TimestampType +org.apache.kafka.common.TopicPartition +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializer$$Lambda$795/0x00007f4204386b98 +org.assertj.core.api.FactoryBasedNavigableIterableAssert +org.assertj.core.api.IterableAssert +org.assertj.core.api.AbstractIterableSizeAssert +org.assertj.core.api.IterableSizeAssert +org.assertj.core.api.AssertFactory +org.assertj.core.api.IterableAssert$$Lambda$796/0x00007f4204387b40 +org.assertj.core.internal.Iterables +org.assertj.core.internal.Predicates +org.assertj.core.api.ListAssert$$Lambda$797/0x00007f420438b3e0 +org.assertj.core.internal.Lists +org.assertj.core.util.IterableUtil +org.assertj.core.internal.WholeNumbers +org.assertj.core.internal.Numbers +org.assertj.core.internal.Integers +org.assertj.core.internal.RealNumbers +org.assertj.core.internal.Doubles +org.assertj.core.internal.ComparatorBasedComparisonStrategy +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext$$Lambda$798/0x00007f420438cdf8 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor$$Lambda$799/0x00007f420438d038 +com.amazonaws.services.lambda.runtime.serialization.factories.PojoSerializerFactory +com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory +com.amazonaws.services.lambda.runtime.serialization.PojoSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Versioned +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.Module +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleModule +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8Module +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TokenStreamFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.DataOutputAsStream +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.SerializableString +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TSFBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonFactoryBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.async.NonBlockingInputFeeder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.async.ByteBufferFeeder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.base.ParserMinimalBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.base.ParserBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingJsonParserBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingUtf8JsonParserBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingByteBufferJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.UTF8DataInputJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.ReaderBasedJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.base.GeneratorBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonGeneratorImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.WriterBasedJsonGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.UTF8JsonGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.UTF8Writer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.async.ByteArrayFeeder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.JacksonFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonFactory$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParser$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonGenerator$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.PrettyPrinter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.Instantiatable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter$Indenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.SerializedString +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.JsonStringEncoder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.CharTypes +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.FormatFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonReadFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonWriteFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$TableInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$Bucket +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer$TableInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TreeCodec +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.ObjectCodec +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.json.JsonMapper +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.MappingJsonFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.SubtypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DatabindContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.SerializerProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.DefaultSerializerProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.DefaultSerializerProvider$Impl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DeserializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BasicDeserializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DeserializationContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DefaultDeserializationContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DefaultDeserializationContext$Impl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.SerializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BasicSerializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AnnotationIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy$Provider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$Provider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.StdDateFormat +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JacksonException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonProcessingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DatabindException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonMappingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.SegmentedStringWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.ByteArrayBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TreeNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonSerializable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonSerializable$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BaseJsonNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ValueNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.NullNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.ClassIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.BasicClassIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.Module$SetupContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.VisibilityChecker +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.TreeTraversingParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.TokenBuffer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.type.ResolvedType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JavaType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ArrayType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.CollectionLikeType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.CollectionType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.MapLikeType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.MapType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.MismatchedInputException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.Annotated +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.TypeResolutionContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedClass +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMember +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.VirtualAnnotatedMember +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.Named +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.BeanProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.PropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanPropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedWithParams +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethod +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonSerialize +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonView +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonTypeInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonRawValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonUnwrapped +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonBackReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonManagedReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonDeserialize +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonMerge +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7Support +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ClassUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ClassUtil$Ctor +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.LookupCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.LRUMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Builder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.Linked +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.LinkedDeque +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$2 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$3 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.BaseSettings +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.SimpleType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.PlaceholderForType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ReferenceType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ResolvedRecursiveType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBindings +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Base64Variants +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Base64Variant +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Base64Variant$PaddingReadBehaviour +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$RecordNaming +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MapperBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.json.JsonMapper$Builder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.RootNameLookup +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.ClassIntrospector$MixInResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.SimpleMixInResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.BeanDescription +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.BasicBeanDescription +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MapperConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MapperConfigBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.SerializationConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DeserializationConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.Annotations +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector$EmptyCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector$NoAnnotations +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedClass$Creators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedConstructor +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedParameter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigOverrides +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JacksonAnnotationValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonInclude$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonInclude$Include +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonSetter$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.Nulls +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.VisibilityChecker$Std +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionConfigs +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.LogicalType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionAction +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MutableCoercionConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionInputShape +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Shape +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Features +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigOverride +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigOverride$Empty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.MapperFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter$NopIndenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter$FixedSpaceIndenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultIndenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.Separators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.SerializationFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DatatypeFeatures +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DatatypeFeatures$DefaultHolder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DatatypeFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.EnumFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.JsonNodeFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ContextAttributes +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ContextAttributes$Impl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DeserializationFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.JsonNodeCreator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.JsonNodeFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.MissingNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BooleanNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.NumericNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.DecimalNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.DoubleNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.IntNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ShortNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.FloatNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.LongNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BigIntegerNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.TextNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BinaryNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.POJONode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsonschema.SchemaAware +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NullSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.FailingSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ToEmptyObjectSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnknownSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.ContextualSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.InvalidDefinitionException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.InvalidTypeIdException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ContainerNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ObjectNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.ResolvableSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.SerializerCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.NullValueProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.PropertyBindingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.InvalidFormatException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.ValueInstantiationException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.UnresolvedForwardReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ContextualDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ResolvableDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiator$Gettable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.EnumMapDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.AbstractDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.MapDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StringDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.MapEntryDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.TokenBufferDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.SettableBeanProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.CreatorProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.EnumDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicReferenceDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.EnumSetDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ArrayBlockingQueueDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.MethodProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.FieldProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.SetterlessProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ErrorThrowingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.ObjectIdGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.ObjectIdGenerators$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.ObjectIdGenerators$PropertyGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyName +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AbstractTypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.KeyDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.KeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$DelegatingKD +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$EnumKD +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringCtorKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringFactoryKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DeserializerCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.JsonValueSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.SerializableSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.DateTimeSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.CalendarSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.DateSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ByteBufferSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.InetAddressSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.InetSocketAddressSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.TimeZoneSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ToStringSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.ContainerSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.CollectionSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.IndexedStringListSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.StringCollectionSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.EnumSetSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.MapSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ArraySerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.StringArraySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ReferenceTypeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.IteratorSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.IterableSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.EnumSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.BeanSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedField +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StringSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntegerSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParser$NumberType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$LongSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntLikeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$ShortSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$DoubleSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$FloatSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.BooleanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.BooleanSerializer$AsNumber +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializer$BigDecimalAsStringSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.UUIDSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicBooleanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicIntegerSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicLongSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.FileSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ClassSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.TokenBufferSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializerModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.JSR310DateTimeDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.DurationDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.OffsetTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.YearDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.JSR310SerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.DurationSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.MonthDaySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.YearSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.YearMonthSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZoneIdSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.key.ZonedDateTimeKeySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.Jsr310KeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.DurationKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.InstantKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.LocalDateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.LocalDateKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.LocalTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.MonthDayKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.OffsetDateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.OffsetTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.PeriodKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.YearKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.YearMonthKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.ZonedDateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.ZoneIdKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.ZoneOffsetKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.PackageVersion +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.VersionUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Version +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.exc.StreamReadException +java.util.regex.Pattern$BnM +java.util.regex.Pattern$Pos +java.time.format.DateTimeFormatter +java.time.format.DateTimeFormatterBuilder +java.time.temporal.TemporalQuery +java.time.format.DateTimeFormatterBuilder$$Lambda$10/0x80000000e +java.time.temporal.IsoFields +java.time.temporal.IsoFields$Field +java.time.temporal.IsoFields$Field$1 +java.time.temporal.IsoFields$Field$2 +java.time.temporal.IsoFields$Field$3 +java.time.temporal.IsoFields$Field$4 +java.time.temporal.IsoFields$Unit +java.time.temporal.JulianFields +java.time.temporal.JulianFields$Field +java.time.format.SignStyle +java.time.format.DateTimeFormatterBuilder$DateTimePrinterParser +java.time.format.DateTimeFormatterBuilder$NumberPrinterParser +java.time.format.DateTimeFormatterBuilder$CharLiteralPrinterParser +java.time.format.ResolverStyle +java.time.chrono.Chronology +java.time.chrono.AbstractChronology +java.time.chrono.IsoChronology +java.time.format.DateTimeFormatterBuilder$CompositePrinterParser +java.time.format.DecimalStyle +java.time.format.DateTimeFormatterBuilder$SettingsParser +java.time.format.DateTimeFormatterBuilder$OffsetIdPrinterParser +java.time.format.DateTimeFormatterBuilder$FractionPrinterParser +java.time.format.DateTimeFormatterBuilder$ZoneIdPrinterParser +java.time.format.DateTimeFormatterBuilder$StringLiteralPrinterParser +java.time.format.DateTimeFormatterBuilder$InstantPrinterParser +java.time.format.TextStyle +java.time.format.DateTimeTextProvider$LocaleStore +java.time.format.DateTimeTextProvider +java.time.format.DateTimeTextProvider$1 +java.time.format.DateTimeFormatterBuilder$1 +java.time.format.DateTimeFormatterBuilder$TextPrinterParser +java.time.chrono.ChronoPeriod +java.time.Period +java.time.format.DateTimeFormatter$$Lambda$8/0x80000000c +java.time.format.DateTimeFormatter$$Lambda$9/0x80000000d +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$803/0x00007f42043e9648 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$FromIntegerArguments +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$804/0x00007f42043e9a98 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$FromDecimalArguments +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$805/0x00007f42043e9ee8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$806/0x00007f42043ea128 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$807/0x00007f42043ea358 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$808/0x00007f42043ea598 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$809/0x00007f42043ea7d8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$810/0x00007f42043eaa18 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$811/0x00007f42043eac48 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$812/0x00007f42043eae88 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$813/0x00007f42043eb0c8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$814/0x00007f42043eb308 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ClassKey +java.time.MonthDay +java.time.OffsetTime +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.JSR310StringParsableDeserializer +java.time.Year +java.time.YearMonth +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleSerializers +java.util.function.ToLongFunction +java.lang.invoke.LambdaForm$DMH/0x00007f42043f0000 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer$$Lambda$815/0x00007f42043ec758 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer$$Lambda$816/0x00007f42043ec978 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer$$Lambda$817/0x00007f42043ecb98 +java.lang.invoke.LambdaForm$DMH/0x00007f42043f0400 +java.lang.invoke.LambdaForm$DMH/0x00007f42043f0800 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer$$Lambda$818/0x00007f42043ecdb8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer$$Lambda$819/0x00007f42043ecfd8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer$$Lambda$820/0x00007f42043ed1f8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer$$Lambda$821/0x00007f42043ed418 +java.lang.invoke.LambdaForm$DMH/0x00007f42043f0c00 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer$$Lambda$822/0x00007f42043ed638 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer$$Lambda$823/0x00007f42043ed858 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleKeyDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ArrayBuilders +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiators$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.JavaTimeModule$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8TypeModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8BeanSerializerModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.PackageVersion +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8Serializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalIntSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalLongSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalDoubleSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.LongStreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.IntStreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.DoubleStreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.StreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8Deserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.BaseScalarOptionalDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalIntDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalLongDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalDoubleDeserializer +com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory$InternalSerializer +com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory$TypeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ClassStack +java.util.OptionalLong +java.util.OptionalDouble +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WeightedValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Node +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$AddTask +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectReader +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.filter.TokenFilter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.filter.JsonPointerBasedFilter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ArrayNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.JsonParserDelegate +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.filter.FilteringParserDelegate +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParseException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector$OneCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAutoDetect +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIdentityInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ArrayIterator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.OptionalHandlerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7Handlers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.NioPathSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.NioPathDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.JdkDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.UUIDDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicBooleanDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicIntegerDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicLongDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ByteBufferDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBuilderDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBufferDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$Std +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.BeanUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiator$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.JsonLocationInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ArrayListInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ConstantValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$LinkedHashMapInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$HashMapInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonLocation +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConstructorDetector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConstructorDetector$SingleArgConstructor +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.CreatorCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.CollectorBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.TypeResolutionContext$Basic +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector$FieldBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAnyGetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAnySetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.InternCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonSetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAutoDetect$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.PropertyAccessor +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnore +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$WithMember +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty$Type +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$Linked +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.MemberKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector$MethodBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethodMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonGetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector +sun.reflect.generics.tree.DoubleSignature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBindings$TypeParamStash +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBindings$AsKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$5 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonProperty$Access +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$6 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JacksonInject +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonNaming +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonPropertyOrder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonPropertyDescription +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyMetadata +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BasicDeserializerFactory$CreatorCollectionState +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonCreator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonCreator$Mode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnoreProperties +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnoreProperties$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIncludeProperties +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIncludeProperties$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.IgnorePropertiesUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnoreType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonTypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.FailingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.AccessPattern +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$2 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$4 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAlias +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.IgnoredPropertyException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.SettableBeanProperty$Delegating +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ManagedReferenceProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ObjectIdReferenceProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.InnerClassProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.MergingSettableBeanProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ReadableObjectId$Referring +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializer$BeanReferring +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$PrimitiveOrWrapperDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BooleanDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$LongDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$DoubleDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$CharacterDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ByteDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ShortDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$FloatDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$NumberDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigDecimalDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigIntegerDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BasicDeserializerFactory$ContainerDefaultMappings +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.LinkedNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JacksonStdImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JacksonAnnotation +jdk.proxy2.$Proxy74 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.MinimalPrettyPrinter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectWriter$GeneratorSettings +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectWriter$Prefetch +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.RuntimeJsonMappingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonEncoding +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.ContentReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.IOContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.BufferRecyclers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.BufferRecycler +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.exc.StreamWriteException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonGenerationException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonStreamContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonWriteContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.StreamWriteCapability +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.JacksonFeatureSet +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$Bucket +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.TypeKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$$Lambda$824/0x00007f4204413340 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntrySet +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntryIterator +java.util.stream.LongStream +java.util.stream.DoubleStream +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializerBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.PropertyBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonInclude +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$3 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonTypeId +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.BeanProperty$Std +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.PropertyBuilder$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Empty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Single +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonAppend +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFilter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.NumberOutput +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$SerializerAndMapResult +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Double +org.assertj.core.internal.Strings$$Lambda$825/0x00007f42044178b8 +org.assertj.core.internal.Strings$$Lambda$826/0x00007f4204417b10 +jdk.internal.reflect.GeneratedConstructorAccessor11 +com.google.protobuf.RuntimeVersion$RuntimeDomain +com.google.protobuf.RuntimeVersion +com.google.protobuf.RuntimeVersion$ProtobufRuntimeVersionException +com.google.protobuf.AbstractParser +com.google.protobuf.UnknownFieldSet$Parser +com.google.protobuf.AbstractMessageLite$Builder$LimitedInputStream +com.google.protobuf.Protobuf +com.google.protobuf.SchemaFactory +com.google.protobuf.ManifestSchemaFactory +com.google.protobuf.MessageInfoFactory +com.google.protobuf.ManifestSchemaFactory$1 +com.google.protobuf.ManifestSchemaFactory$CompositeMessageInfoFactory +com.google.protobuf.GeneratedMessageInfoFactory +com.google.protobuf.DescriptorMessageInfoFactory +com.google.protobuf.Internal$EnumVerifier +com.google.protobuf.MessageInfo +com.google.protobuf.DescriptorMessageInfoFactory$IsInitializedCheckAnalyzer +com.google.protobuf.FieldType +com.google.protobuf.Internal$EnumLite +com.google.protobuf.ProtocolMessageEnum +com.google.protobuf.DescriptorProtos$Edition +com.google.protobuf.ProtoSyntax +com.google.protobuf.DescriptorMessageInfoFactory$OneofState +com.google.protobuf.FieldInfo +com.google.protobuf.Internal +com.google.protobuf.CodedInputStream$ArrayDecoder +com.google.protobuf.CodedInputStream$UnsafeDirectNioDecoder +com.google.protobuf.CodedInputStream$IterableDirectByteBufferDecoder +com.google.protobuf.IterableByteBufferInputStream +com.google.protobuf.CodedInputStream$StreamDecoder +com.google.protobuf.InvalidProtocolBufferException$InvalidWireTypeException +com.google.protobuf.ExtensionRegistryFactory +com.google.protobuf.ExtensionRegistry$ExtensionInfo +com.google.protobuf.Extension$ExtensionType +software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct$1 +com.google.protobuf.CodedOutputStream$AbstractBufferedEncoder +com.google.protobuf.CodedOutputStream$OutputStreamEncoder +com.google.protobuf.CodedOutputStream$ByteOutputEncoder +com.google.protobuf.CodedOutputStream$ArrayEncoder +com.google.protobuf.CodedOutputStream$HeapNioEncoder +com.google.protobuf.Utf8$UnpairedSurrogateException +com.google.protobuf.CodedOutputStream$OutOfSpaceException +com.google.protobuf.CodedOutputStream$UnsafeDirectNioEncoder +com.google.protobuf.CodedOutputStream$SafeDirectNioEncoder +com.google.protobuf.Writer +com.google.protobuf.UnsafeUtil +com.google.protobuf.UnsafeUtil$MemoryAccessor +com.google.protobuf.UnsafeUtil$Android64MemoryAccessor +com.google.protobuf.UnsafeUtil$Android32MemoryAccessor +com.google.protobuf.UnsafeUtil$JvmMemoryAccessor +com.google.protobuf.UnsafeUtil$1 +sun.misc.Unsafe +com.google.protobuf.Android +jdk.internal.access.foreign.MemorySegmentProxy +com.google.protobuf.WireFormat +com.google.protobuf.Utf8 +com.google.protobuf.Utf8$Processor +com.google.protobuf.Utf8$UnsafeProcessor +com.google.protobuf.Utf8$SafeProcessor +software.amazon.lambda.powertools.kafka.serializers.KafkaProtobufDeserializer$1 +com.fasterxml.jackson.core.exc.StreamWriteException +com.fasterxml.jackson.core.JsonGenerationException +com.fasterxml.jackson.core.json.JsonWriteContext +com.fasterxml.jackson.core.StreamWriteCapability +com.fasterxml.jackson.core.FormatFeature +com.fasterxml.jackson.core.json.JsonWriteFeature +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$Bucket +com.fasterxml.jackson.databind.util.TypeKey +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$$Lambda$827/0x00007f4204423ad8 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntrySet +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntryIterator +com.fasterxml.jackson.databind.ObjectReader +com.fasterxml.jackson.databind.ObjectWriter +com.fasterxml.jackson.databind.ser.BeanSerializerBuilder +com.fasterxml.jackson.databind.ser.PropertyBuilder +com.fasterxml.jackson.annotation.JsonInclude +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$3 +com.fasterxml.jackson.annotation.JsonTypeId +com.fasterxml.jackson.databind.BeanProperty$Std +com.fasterxml.jackson.databind.ser.PropertyBuilder$1 +com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Empty +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Single +com.fasterxml.jackson.databind.annotation.JsonAppend +com.fasterxml.jackson.annotation.JsonFilter +com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer +com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$1 +com.fasterxml.jackson.databind.ser.AnyGetterWriter +com.fasterxml.jackson.core.io.NumberOutput +sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$SerializerAndMapResult +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Double +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.UTF8StreamJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.MergedStream +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.UTF32Reader +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.exc.InputCoercionException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.JsonEOFException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonReadContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.StreamReadCapability +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.TextBuffer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonToken +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.NumberInput +org.assertj.core.util.Lists$$Lambda$828/0x00007f420442b718 +org.assertj.core.util.Preconditions +org.assertj.core.internal.IterableDiff +org.assertj.core.internal.IterableDiff$$Lambda$829/0x00007f420442bd58 +org.assertj.core.internal.StandardComparisonStrategy$$Lambda$830/0x00007f420442bfb0 +java.util.AbstractMap$SimpleEntry +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WriteThroughEntry +com.fasterxml.jackson.core.StreamReadFeature +software.amazon.lambda.powertools.kafka.testutils.StringHandler +software.amazon.lambda.powertools.kafka.testutils.DefaultHandler +jdk.internal.reflect.GeneratedConstructorAccessor12 +software.amazon.lambda.powertools.kafka.DeserializationTest$1TestHandler +org.assertj.core.api.AbstractClassAssert +org.assertj.core.api.ClassAssert +org.assertj.core.internal.Classes +org.assertj.core.api.AbstractObjectArrayAssert +org.assertj.core.api.ObjectArrayAssert +org.assertj.core.internal.ObjectArrays +org.assertj.core.internal.Arrays +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$831/0x00007f420442fc20 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$832/0x00007f4204435150 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$833/0x00007f4204435370 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$834/0x00007f4204435598 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$835/0x00007f42044357f0 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$836/0x00007f4204435a18 +software.amazon.lambda.powertools.kafka.serializers.KafkaJsonDeserializerTest$$Lambda$837/0x00007f4204435c40 +software.amazon.lambda.powertools.kafka.serializers.KafkaProtobufDeserializerTest$$Lambda$838/0x00007f4204435e68 +org.apache.kafka.common.utils.ByteUtils +software.amazon.lambda.powertools.kafka.serializers.KafkaProtobufDeserializerTest$$Lambda$839/0x00007f4204436298 +software.amazon.lambda.powertools.kafka.serializers.KafkaAvroDeserializerTest$$Lambda$840/0x00007f42044364c0 +software.amazon.lambda.powertools.kafka.serializers.KafkaAvroDeserializerTest$$Lambda$841/0x00007f42044366e8 +java.util.AbstractMap$2 +java.util.AbstractMap$2$1 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$842/0x00007f4204436b08 +org.assertj.core.util.Throwables +org.assertj.core.util.Throwables$$Lambda$843/0x00007f4204436f38 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$844/0x00007f4204437178 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$845/0x00007f42044373a0 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$846/0x00007f42044375c8 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$847/0x00007f42044377f0 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$848/0x00007f4204437a18 +jdk.internal.reflect.GeneratedConstructorAccessor13 +jdk.internal.reflect.GeneratedMethodAccessor9 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$849/0x00007f4204437c40 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$850/0x00007f4204432000 +jdk.internal.reflect.GeneratedMethodAccessor10 +com.fasterxml.jackson.databind.util.ArrayBuilders +com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder +com.fasterxml.jackson.databind.util.ArrayBuilders$ByteBuilder +org.apache.kafka.common.header.internals.RecordHeader +jdk.internal.reflect.GeneratedConstructorAccessor14 +jdk.internal.reflect.GeneratedMethodAccessor11 +jdk.internal.reflect.GeneratedConstructorAccessor15 +jdk.internal.reflect.GeneratedMethodAccessor12 +jdk.internal.reflect.GeneratedMethodAccessor13 +jdk.internal.reflect.GeneratedMethodAccessor14 +jdk.internal.reflect.GeneratedMethodAccessor15 +jdk.internal.reflect.GeneratedMethodAccessor16 +jdk.internal.reflect.GeneratedMethodAccessor17 +jdk.internal.reflect.GeneratedMethodAccessor18 +jdk.internal.reflect.GeneratedMethodAccessor19 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$851/0x00007f4204432ce0 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$852/0x00007f4204432f08 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$853/0x00007f4204433130 +software.amazon.lambda.powertools.kafka.serializers.AbstractKafkaDeserializerTest$$Lambda$854/0x00007f4204433358 +jdk.internal.reflect.GeneratedConstructorAccessor16 +jdk.internal.reflect.GeneratedConstructorAccessor17 +jdk.internal.reflect.GeneratedMethodAccessor20 +jdk.internal.reflect.GeneratedMethodAccessor21 +jdk.internal.reflect.GeneratedMethodAccessor22 +jdk.internal.reflect.GeneratedMethodAccessor23 +jdk.internal.reflect.GeneratedConstructorAccessor18 +jdk.internal.reflect.GeneratedMethodAccessor24 +jdk.internal.reflect.GeneratedMethodAccessor25 +jdk.internal.reflect.GeneratedMethodAccessor26 +jdk.internal.reflect.GeneratedMethodAccessor27 +jdk.internal.reflect.GeneratedMethodAccessor28 +org.junit.platform.launcher.core.OutcomeDelayingEngineExecutionListener$Outcome +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$855/0x00007f4204433bb8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$856/0x00007f420443e000 +java.lang.invoke.LambdaForm$DMH/0x00007f420443cc00 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$857/0x00007f420443e228 +org.junit.platform.launcher.core.DefaultLauncherSession$ClosedLauncher +org.apache.maven.surefire.api.suite.RunResult +org.apache.maven.surefire.booter.ForkedBooter$6 +org.apache.maven.surefire.booter.ForkedBooter$7 +java.util.concurrent.locks.AbstractQueuedSynchronizer$SharedNode +org.apache.maven.surefire.booter.ForkedBooter$1 +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory$2 +java.util.concurrent.locks.AbstractQueuedSynchronizer$ExclusiveNode diff --git a/powertools-kafka/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-kafka/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..1ce363ad9 --- /dev/null +++ b/powertools-kafka/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.kafka.internal.KafkaUserAgentInterceptor diff --git a/powertools-kafka/src/test/avro/TestProduct.avsc b/powertools-kafka/src/test/avro/TestProduct.avsc new file mode 100644 index 000000000..aad903d40 --- /dev/null +++ b/powertools-kafka/src/test/avro/TestProduct.avsc @@ -0,0 +1,10 @@ +{ + "namespace": "software.amazon.lambda.powertools.kafka.serializers.test.avro", + "type": "record", + "name": "TestProduct", + "fields": [ + {"name": "id", "type": "int"}, + {"name": "name", "type": "string"}, + {"name": "price", "type": "double"} + ] +} \ No newline at end of file diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTest.java new file mode 100644 index 000000000..964498d99 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.Method; + +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.junit.jupiter.api.Test; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +class DeserializationTest { + + @Test + void shouldHaveCorrectAnnotationRetention() { + // Given + Class<Deserialization> annotationClass = Deserialization.class; + + // When/Then + assertThat(annotationClass.isAnnotation()).isTrue(); + assertThat(annotationClass.getAnnotation(java.lang.annotation.Retention.class).value()) + .isEqualTo(java.lang.annotation.RetentionPolicy.RUNTIME); + assertThat(annotationClass.getAnnotation(java.lang.annotation.Target.class).value()) + .contains(java.lang.annotation.ElementType.METHOD); + } + + @Test + void shouldHaveTypeMethod() throws NoSuchMethodException { + // Given + Class<Deserialization> annotationClass = Deserialization.class; + + // When + java.lang.reflect.Method typeMethod = annotationClass.getMethod("type"); + + // Then + assertThat(typeMethod.getReturnType()).isEqualTo(DeserializationType.class); + } + + @Test + void shouldBeAccessibleReflectivelyAtRuntime() throws NoSuchMethodException, SecurityException { + // Given + class TestHandler implements RequestHandler<ConsumerRecords<String, Object>, String> { + @Override + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, Object> input, Context context) { + return "OK"; + } + } + + // When + Method handleRequestMethod = TestHandler.class.getMethod("handleRequest", ConsumerRecords.class, Context.class); + + // Then + Deserialization annotation = handleRequestMethod.getAnnotation(Deserialization.class); + assertThat(annotation).isNotNull(); + assertThat(annotation.type()).isEqualTo(DeserializationType.KAFKA_JSON); + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTypeTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTypeTest.java new file mode 100644 index 000000000..6999b66d4 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTypeTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +// Mainly present to remind us to write unit tests once we add support for a new Deserializer. If we add a new type in +// the enum it will fail this test. +class DeserializationTypeTest { + + @Test + void shouldHaveExpectedEnumValues() { + // Given/When + DeserializationType[] values = DeserializationType.values(); + + // Then + assertThat(values).contains( + DeserializationType.LAMBDA_DEFAULT, + DeserializationType.KAFKA_JSON, + DeserializationType.KAFKA_AVRO, + DeserializationType.KAFKA_PROTOBUF); + } + + @Test + void shouldBeAbleToValueOf() { + // Given/When + DeserializationType jsonType = DeserializationType.valueOf("KAFKA_JSON"); + DeserializationType avroType = DeserializationType.valueOf("KAFKA_AVRO"); + DeserializationType protobufType = DeserializationType.valueOf("KAFKA_PROTOBUF"); + DeserializationType defaultType = DeserializationType.valueOf("LAMBDA_DEFAULT"); + + // Then + assertThat(jsonType).isEqualTo(DeserializationType.KAFKA_JSON); + assertThat(avroType).isEqualTo(DeserializationType.KAFKA_AVRO); + assertThat(protobufType).isEqualTo(DeserializationType.KAFKA_PROTOBUF); + assertThat(defaultType).isEqualTo(DeserializationType.LAMBDA_DEFAULT); + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializerTest.java new file mode 100644 index 000000000..d7d045877 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializerTest.java @@ -0,0 +1,440 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static software.amazon.lambda.powertools.kafka.testutils.TestUtils.createConsumerRecordsType; +import static software.amazon.lambda.powertools.kafka.testutils.TestUtils.serializeAvro; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.common.TopicPartition; +import org.crac.Context; +import org.crac.Resource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.kafka.serializers.LambdaDefaultDeserializer; +import software.amazon.lambda.powertools.kafka.serializers.PowertoolsDeserializer; +import software.amazon.lambda.powertools.kafka.testutils.TestProductPojo; + +// This is testing the whole serializer end-to-end. More detailed serializer tests are placed in serializers folder. +@ExtendWith(MockitoExtension.class) +class PowertoolsSerializerTest { + + @Mock + private PowertoolsDeserializer mockDeserializer; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + // CustomPojoSerializer has fromJson(String input, ...) and fromJson(InputStream input, ...). We want to test both. + static Stream<InputType> inputTypes() { + return Stream.of(InputType.INPUT_STREAM, InputType.STRING); + } + + @ParameterizedTest + @MethodSource("inputTypes") + @SetEnvironmentVariable(key = "_HANDLER", value = "") + void shouldUseDefaultDeserializerWhenHandlerNotFound(InputType inputType) throws JsonProcessingException, IOException { + // When + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // Then + TestProductPojo product = new TestProductPojo(123, "Test Product", 99.99, Arrays.asList("tag1", "tag2")); + String json = objectMapper.writeValueAsString(product); + + // This will use the Lambda default deserializer (no Kafka logic) + TestProductPojo result; + if (inputType == InputType.INPUT_STREAM) { + try (ByteArrayInputStream input = new ByteArrayInputStream(json.getBytes())) { + result = serializer.fromJson(input, TestProductPojo.class); + } + } else { + result = serializer.fromJson(json, TestProductPojo.class); + } + + assertThat(result.getId()).isEqualTo(123); + assertThat(result.getName()).isEqualTo("Test Product"); + assertThat(result.getPrice()).isEqualTo(99.99); + assertThat(result.getTags()).containsExactly("tag1", "tag2"); + } + + @ParameterizedTest + @MethodSource("inputTypes") + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.DefaultHandler::handleRequest") + void shouldUseLambdaDefaultDeserializer(InputType inputType) throws JsonProcessingException, IOException { + // When + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // Then + TestProductPojo product = new TestProductPojo(123, "Test Product", 99.99, Arrays.asList("tag1", "tag2")); + String json = objectMapper.writeValueAsString(product); + + // This will use the Lambda default deserializer (no Kafka logic) + TestProductPojo result; + if (inputType == InputType.INPUT_STREAM) { + try (ByteArrayInputStream input = new ByteArrayInputStream(json.getBytes())) { + result = serializer.fromJson(input, TestProductPojo.class); + } + } else { + result = serializer.fromJson(json, TestProductPojo.class); + } + + assertThat(result.getId()).isEqualTo(123); + assertThat(result.getName()).isEqualTo("Test Product"); + assertThat(result.getPrice()).isEqualTo(99.99); + assertThat(result.getTags()).containsExactly("tag1", "tag2"); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.StringHandler::handleRequest") + void shouldHandleStringInputType() { + // When + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // Then + String testInput = "This is a test string"; + + // This should directly return the input string + String result = serializer.fromJson(testInput, String.class); + + assertThat(result).isEqualTo(testInput); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.InputStreamHandler::handleRequest") + void shouldHandleInputStreamType() throws IOException { + // When + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // Then + String testInput = "This is a test string"; + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(testInput.getBytes()); + InputStream result = serializer.fromJson(inputStream, InputStream.class)) { + // Read the content to verify it's the same + String resultString = new String(result.readAllBytes()); + assertThat(resultString).isEqualTo(testInput); + } + } + + @Test + void shouldConvertInputStreamToString() throws IOException { + // When + LambdaDefaultDeserializer deserializer = new LambdaDefaultDeserializer(); + + // Then + String expected = "This is a test string"; + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(expected.getBytes())) { + // Convert InputStream to String + String result = deserializer.fromJson(inputStream, String.class); + + // Verify the result + assertThat(result).isEqualTo(expected); + } + } + + @Test + void shouldThrowRuntimeExceptionWhenInputStreamIsInvalid() { + // When + LambdaDefaultDeserializer deserializer = new LambdaDefaultDeserializer(); + + // Create a problematic InputStream that throws IOException when read + try (InputStream problematicStream = new InputStream() { + @Override + public int read() throws IOException { + throw new IOException("Simulated IO error"); + } + + @Override + public byte[] readAllBytes() throws IOException { + throw new IOException("Simulated IO error"); + } + }) { + // Then + assertThatThrownBy(() -> deserializer.fromJson(problematicStream, String.class)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to read input stream as String"); + } catch (IOException e) { + // Expected for this test case + } + } + + @Test + void shouldConvertStringToByteArray() { + // When + LambdaDefaultDeserializer deserializer = new LambdaDefaultDeserializer(); + + // Then + String input = "This is a test string"; + + // Convert String to InputStream + byte[] result = deserializer.fromJson(input, InputStream.class); + + // Verify the result + String resultString = new String(result); + assertThat(resultString).isEqualTo(input); + } + + @ParameterizedTest + @MethodSource("inputTypes") + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.JsonHandler::handleRequest") + void shouldUseKafkaJsonDeserializer(InputType inputType) throws IOException { + // When + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // Create a TestProductPojo and serialize it + TestProductPojo product = new TestProductPojo(123, "Test Product", 99.99, Arrays.asList("tag1", "tag2")); + String productJson = objectMapper.writeValueAsString(product); + String base64Value = Base64.getEncoder().encodeToString(productJson.getBytes()); + + // Then + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + base64Value + "\",\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + + Type type = createConsumerRecordsType(String.class, TestProductPojo.class); + + // This should use the KafkaJsonDeserializer + ConsumerRecords<String, TestProductPojo> records; + + if (inputType == InputType.INPUT_STREAM) { + try (ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes())) { + records = serializer.fromJson(input, type); + } + } else { + records = serializer.fromJson(kafkaJson, type); + } + + // Verify we got a valid ConsumerRecords object + assertThat(records).isNotNull(); + + // Get the record and verify its content + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<String, TestProductPojo>> topicRecords = records.records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<String, TestProductPojo> consumerRecord = topicRecords.get(0); + TestProductPojo deserializedProduct = consumerRecord.value(); + + assertThat(deserializedProduct.getId()).isEqualTo(123); + assertThat(deserializedProduct.getName()).isEqualTo("Test Product"); + assertThat(deserializedProduct.getPrice()).isEqualTo(99.99); + assertThat(deserializedProduct.getTags()).containsExactly("tag1", "tag2"); + } + + @ParameterizedTest + @MethodSource("inputTypes") + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.AvroHandler::handleRequest") + void shouldUseKafkaAvroDeserializer(InputType inputType) throws IOException { + // When + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // Create an Avro TestProduct and serialize it + software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct product = new software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct( + 123, "Test Product", 99.99); + String base64Value = Base64.getEncoder().encodeToString(serializeAvro(product)); + + // Then + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + base64Value + "\",\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + + Type type = createConsumerRecordsType(String.class, + software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct.class); + + // This should use the KafkaAvroDeserializer + ConsumerRecords<String, software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct> records; + + if (inputType == InputType.INPUT_STREAM) { + try (ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes())) { + records = serializer.fromJson(input, type); + } + } else { + records = serializer.fromJson(kafkaJson, type); + } + + // Verify we got a valid ConsumerRecords object + assertThat(records).isNotNull(); + + // Get the record and verify its content + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<String, software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct>> topicRecords = records + .records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<String, software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct> consumerRecord = topicRecords + .get(0); + software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct deserializedProduct = consumerRecord + .value(); + + assertThat(deserializedProduct.getId()).isEqualTo(123); + assertThat(deserializedProduct.getName()).isEqualTo("Test Product"); + assertThat(deserializedProduct.getPrice()).isEqualTo(99.99); + } + + @ParameterizedTest + @MethodSource("inputTypes") + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.ProtobufHandler::handleRequest") + void shouldUseKafkaProtobufDeserializer(InputType inputType) throws IOException { + // When + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // Create a Protobuf TestProduct and serialize it + software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct product = software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct + .newBuilder() + .setId(123) + .setName("Test Product") + .setPrice(99.99) + .build(); + String base64Value = Base64.getEncoder().encodeToString(product.toByteArray()); + + // Then + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + base64Value + "\",\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + + Type type = createConsumerRecordsType(String.class, + software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct.class); + + // This should use the KafkaProtobufDeserializer + ConsumerRecords<String, software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct> records; + + if (inputType == InputType.INPUT_STREAM) { + try (ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes())) { + records = serializer.fromJson(input, type); + } + } else { + records = serializer.fromJson(kafkaJson, type); + } + + // Verify we got a valid ConsumerRecords object + assertThat(records).isNotNull(); + + // Get the record and verify its content + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<String, software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct>> topicRecords = records + .records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<String, software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct> consumerRecord = topicRecords + .get(0); + software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct deserializedProduct = consumerRecord + .value(); + + assertThat(deserializedProduct.getId()).isEqualTo(123); + assertThat(deserializedProduct.getName()).isEqualTo("Test Product"); + assertThat(deserializedProduct.getPrice()).isEqualTo(99.99); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "") + void shouldDelegateToJsonOutput() { + // Given + PowertoolsSerializer serializer = new PowertoolsSerializer(); + + // When + TestProductPojo product = new TestProductPojo(123, "Test Product", 99.99, Arrays.asList("tag1", "tag2")); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + // Then + serializer.toJson(product, output, TestProductPojo.class); + String json = output.toString(); + + // Verify the output is valid JSON + assertThat(json).contains("\"id\":123") + .contains("\"name\":\"Test Product\"") + .contains("\"price\":99.99") + .contains("\"tags\":[\"tag1\",\"tag2\"]"); + } + + private enum InputType { + INPUT_STREAM, STRING + } + + @Test + void testBeforeCheckpointDoesNotThrowException() { + PowertoolsSerializer serializer = new PowertoolsSerializer(); + Context<Resource> context = mock(Context.class); + assertThatNoException().isThrownBy(() -> serializer.beforeCheckpoint(context)); + } + + @Test + void testAfterRestoreDoesNotThrowException() { + PowertoolsSerializer serializer = new PowertoolsSerializer(); + Context<Resource> context = mock(Context.class); + assertThatNoException().isThrownBy(() -> serializer.afterRestore(context)); + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtilsTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtilsTest.java new file mode 100644 index 000000000..21f38d9ab --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtilsTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +import software.amazon.lambda.powertools.kafka.DeserializationType; + +class DeserializationUtilsTest { + + // NOTE: We don't use a parameterized test here because this is not compatible with the @SetEnvironmentVariable + // annotation. + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "") + void shouldReturnDefaultDeserializationTypeWhenHandlerIsEmpty() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.LAMBDA_DEFAULT); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = " ") + void shouldReturnDefaultDeserializationTypeWhenHandlerIsWhitespaceOnly() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.LAMBDA_DEFAULT); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "InvalidHandlerFormat") + void shouldReturnDefaultDeserializationTypeWhenHandlerFormatIsInvalid() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.LAMBDA_DEFAULT); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "com.example.NonExistentClass::handleRequest") + void shouldReturnDefaultDeserializationTypeWhenClassNotFound() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.LAMBDA_DEFAULT); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "java.lang.String::toString") + void shouldReturnDefaultDeserializationTypeWhenClassIsNotRequestHandler() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.LAMBDA_DEFAULT); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.internal.DeserializationUtilsTest$TestHandler::nonExistentMethod") + void shouldReturnDefaultDeserializationTypeWhenMethodNotFound() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.LAMBDA_DEFAULT); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.JsonHandler::handleRequest") + void shouldReturnJsonDeserializationTypeFromAnnotation() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.KAFKA_JSON); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.AvroHandler::handleRequest") + void shouldReturnAvroDeserializationTypeFromAnnotation() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.KAFKA_AVRO); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.ProtobufHandler::handleRequest") + void shouldReturnProtobufDeserializationTypeFromAnnotation() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.KAFKA_PROTOBUF); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.JsonHandler") + void shouldReturnJsonDeserializationTypeFromAnnotationWithAbbreviatedHandler() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.KAFKA_JSON); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.AvroHandler") + void shouldReturnAvroDeserializationTypeFromAnnotationWithAbbreviatedHandler() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.KAFKA_AVRO); + } + + @Test + @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.ProtobufHandler") + void shouldReturnProtobufDeserializationTypeFromAnnotationWithAbbreviatedHandler() { + // When + DeserializationType type = DeserializationUtils.determineDeserializationType(); + + // Then + assertThat(type).isEqualTo(DeserializationType.KAFKA_PROTOBUF); + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptorTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptorTest.java new file mode 100644 index 000000000..4323a42e9 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.kafka.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class KafkaUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/KAFKA/"); + } +} \ No newline at end of file diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java new file mode 100644 index 000000000..b40a2a2b0 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java @@ -0,0 +1,580 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Base64; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.common.TopicPartition; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.kafka.testutils.TestProductPojo; +import software.amazon.lambda.powertools.kafka.testutils.TestUtils; + +class AbstractKafkaDeserializerTest { + + private TestDeserializer deserializer; + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() { + deserializer = new TestDeserializer(); + } + + // CustomPojoSerializer has fromJson(String input, ...) and fromJson(InputStream input, ...). We want to test both. + static Stream<InputType> inputTypes() { + return Stream.of(InputType.INPUT_STREAM, InputType.STRING); + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldThrowExceptionWhenTypeIsNotConsumerRecords(InputType inputType) { + // Given + String json = "{}"; + + // When/Then + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(json.getBytes()); + assertThatThrownBy(() -> deserializer.fromJson(inputStream, String.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Type must be ConsumerRecords<K, V>"); + } else { + assertThatThrownBy(() -> deserializer.fromJson(json, String.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Type must be ConsumerRecords<K, V>"); + } + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldThrowExceptionWhenJsonIsInvalid(InputType inputType) { + // Given + String invalidJson = "{invalid json"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When/Then + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(invalidJson.getBytes()); + assertThatThrownBy(() -> deserializer.fromJson(inputStream, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Lambda handler input to ConsumerRecords"); + } else { + assertThatThrownBy(() -> deserializer.fromJson(invalidJson, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Lambda handler input to ConsumerRecords"); + } + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldThrowExceptionWhenKeyDeserializationFails(InputType inputType) { + // Given + // Create a Kafka event with invalid Base64 for the key + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"invalid-base64!\",\n" + + " \"value\": \"eyJrZXkiOiJ2YWx1ZSJ9\",\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When/Then + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + assertThatThrownBy(() -> deserializer.fromJson(inputStream, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Kafka record key"); + } else { + assertThatThrownBy(() -> deserializer.fromJson(kafkaJson, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Kafka record key"); + } + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldThrowExceptionWhenValueDeserializationFails(InputType inputType) { + // Given + // Create a Kafka event with invalid Base64 for the value + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"invalid-base64!\",\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When/Then + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + assertThatThrownBy(() -> deserializer.fromJson(inputStream, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Kafka record value"); + } else { + assertThatThrownBy(() -> deserializer.fromJson(kafkaJson, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Kafka record value"); + } + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldHandleNullKeyAndValue(InputType inputType) { + // Given + // Create a Kafka event with null key and value + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": null,\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When + ConsumerRecords<String, TestProductPojo> records; + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + records = deserializer.fromJson(inputStream, type); + } else { + records = deserializer.fromJson(kafkaJson, type); + } + + // Then + assertThat(records).isNotNull(); + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<String, TestProductPojo>> topicRecords = records.records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<String, TestProductPojo> consumerRecord = topicRecords.get(0); + assertThat(consumerRecord.key()).isNull(); + assertThat(consumerRecord.value()).isNull(); + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldHandleHeadersCorrectly(InputType inputType) { + // Given + // Create a Kafka event with headers + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": null,\n" + + " \"headers\": [\n" + + " {\n" + + " \"headerKey1\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101, 49],\n" + + " \"headerKey2\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101, 50]\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When + ConsumerRecords<String, TestProductPojo> records; + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + records = deserializer.fromJson(inputStream, type); + } else { + records = deserializer.fromJson(kafkaJson, type); + } + + // Then + assertThat(records).isNotNull(); + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<String, TestProductPojo>> topicRecords = records.records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<String, TestProductPojo> consumerRecord = topicRecords.get(0); + assertThat(consumerRecord.headers()).isNotNull(); + assertThat(consumerRecord.headers().toArray()).hasSize(2); + assertThat(new String(consumerRecord.headers().lastHeader("headerKey1").value())).isEqualTo("headerValue1"); + assertThat(new String(consumerRecord.headers().lastHeader("headerKey2").value())).isEqualTo("headerValue2"); + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldHandleEmptyRecords(InputType inputType) { + // Given + // Create a Kafka event with no records + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {}\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When + ConsumerRecords<String, TestProductPojo> records; + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + records = deserializer.fromJson(inputStream, type); + } else { + records = deserializer.fromJson(kafkaJson, type); + } + + // Then + assertThat(records).isNotNull(); + assertThat(records.count()).isZero(); + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldHandleNullRecords(InputType inputType) { + // Given + // Create a Kafka event with null records + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\"\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When + ConsumerRecords<String, TestProductPojo> records; + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + records = deserializer.fromJson(inputStream, type); + } else { + records = deserializer.fromJson(kafkaJson, type); + } + + // Then + assertThat(records).isNotNull(); + assertThat(records.count()).isZero(); + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldThrowExceptionWhenEventSourceIsNull(InputType inputType) { + // Given + // Create a JSON without eventSource property + String kafkaJson = "{\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": null,\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When/Then + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + assertThatThrownBy(() -> deserializer.fromJson(inputStream, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Input is not a valid Kafka event"); + } else { + assertThatThrownBy(() -> deserializer.fromJson(kafkaJson, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Input is not a valid Kafka event"); + } + } + + static Stream<Arguments> primitiveTypesProvider() { + return Stream.of( + // For each primitive type, test with both INPUT_STREAM and STRING + Arguments.of("String-InputStream", String.class, "test-string", "test-string", InputType.INPUT_STREAM), + Arguments.of("String-String", String.class, "test-string", "test-string", InputType.STRING), + Arguments.of("Integer-InputStream", Integer.class, "123", 123, InputType.INPUT_STREAM), + Arguments.of("Integer-String", Integer.class, "123", 123, InputType.STRING), + Arguments.of("Long-InputStream", Long.class, "123456789", 123456789L, InputType.INPUT_STREAM), + Arguments.of("Long-String", Long.class, "123456789", 123456789L, InputType.STRING), + Arguments.of("Double-InputStream", Double.class, "123.456", 123.456, InputType.INPUT_STREAM), + Arguments.of("Double-String", Double.class, "123.456", 123.456, InputType.STRING), + Arguments.of("Float-InputStream", Float.class, "123.45", 123.45f, InputType.INPUT_STREAM), + Arguments.of("Float-String", Float.class, "123.45", 123.45f, InputType.STRING), + Arguments.of("Boolean-InputStream", Boolean.class, "true", true, InputType.INPUT_STREAM), + Arguments.of("Boolean-String", Boolean.class, "true", true, InputType.STRING), + Arguments.of("Byte-InputStream", Byte.class, "127", (byte) 127, InputType.INPUT_STREAM), + Arguments.of("Byte-String", Byte.class, "127", (byte) 127, InputType.STRING), + Arguments.of("Short-InputStream", Short.class, "32767", (short) 32767, InputType.INPUT_STREAM), + Arguments.of("Short-String", Short.class, "32767", (short) 32767, InputType.STRING), + Arguments.of("Character-InputStream", Character.class, "A", 'A', InputType.INPUT_STREAM), + Arguments.of("Character-String", Character.class, "A", 'A', InputType.STRING)); + } + + @ParameterizedTest(name = "Should handle {0}") + @MethodSource("primitiveTypesProvider") + <T> void shouldHandlePrimitiveTypes(String testName, Class<T> keyType, String keyValue, T expectedKey, + InputType inputType) throws IOException { + // Given + // Create a TestProductPojo and serialize it to JSON + TestProductPojo product = new TestProductPojo(123, "Test Product", 99.99, null); + String productJson = objectMapper.writeValueAsString(product); + String base64Value = Base64.getEncoder().encodeToString(productJson.getBytes()); + String base64Key = Base64.getEncoder().encodeToString(keyValue.getBytes()); + + // Create a Kafka event with primitive type for key + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + base64Key + "\",\n" + + " \"value\": \"" + base64Value + "\",\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(keyType, TestProductPojo.class); + + // When + ConsumerRecords<T, TestProductPojo> records; + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + records = deserializer.fromJson(inputStream, type); + } else { + records = deserializer.fromJson(kafkaJson, type); + } + + // Then + assertThat(records).isNotNull(); + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<T, TestProductPojo>> topicRecords = records.records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<T, TestProductPojo> consumerRecord = topicRecords.get(0); + assertThat(consumerRecord.key()).isEqualTo(expectedKey); + assertThat(consumerRecord.value()).isNotNull(); + assertThat(consumerRecord.value().getId()).isEqualTo(123); + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldThrowExceptionWhenConvertingEmptyStringToChar(InputType inputType) { + // Given + String base64EmptyString = Base64.getEncoder().encodeToString("".getBytes()); + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": \"" + base64EmptyString + "\",\n" + + " \"value\": null,\n" + + " \"headers\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(Character.class, TestProductPojo.class); + + // When/Then + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + assertThatThrownBy(() -> deserializer.fromJson(inputStream, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Kafka record key") + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasRootCauseMessage("Cannot convert empty string to char"); + } else { + assertThatThrownBy(() -> deserializer.fromJson(kafkaJson, type)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Failed to deserialize Kafka record key") + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasRootCauseMessage("Cannot convert empty string to char"); + } + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldHandleGlueSchemaMetadata(InputType inputType) throws IOException { + // Given + TestProductPojo product = new TestProductPojo(123, "Test Product", 99.99, null); + String productJson = objectMapper.writeValueAsString(product); + String base64Value = Base64.getEncoder().encodeToString(productJson.getBytes()); + + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + base64Value + "\",\n" + + " \"headers\": [],\n" + + " \"keySchemaMetadata\": {\n" + + " \"schemaId\": \"12345678-1234-1234-1234-123456789012\",\n" + + " \"dataFormat\": \"PROTOBUF\"\n" + + " },\n" + + " \"valueSchemaMetadata\": {\n" + + " \"schemaId\": \"87654321-4321-4321-4321-210987654321\",\n" + + " \"dataFormat\": \"PROTOBUF\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When + ConsumerRecords<String, TestProductPojo> records; + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + records = deserializer.fromJson(inputStream, type); + } else { + records = deserializer.fromJson(kafkaJson, type); + } + + // Then + assertThat(records).isNotNull(); + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<String, TestProductPojo>> topicRecords = records.records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<String, TestProductPojo> consumerRecord = topicRecords.get(0); + assertThat(consumerRecord.value()).isNotNull(); + assertThat(consumerRecord.value().getId()).isEqualTo(123); + } + + @ParameterizedTest + @MethodSource("inputTypes") + void shouldHandleConfluentSchemaMetadata(InputType inputType) throws IOException { + // Given + TestProductPojo product = new TestProductPojo(456, "Confluent Product", 199.99, null); + String productJson = objectMapper.writeValueAsString(product); + String base64Value = Base64.getEncoder().encodeToString(productJson.getBytes()); + + String kafkaJson = "{\n" + + " \"eventSource\": \"aws:kafka\",\n" + + " \"records\": {\n" + + " \"test-topic-1\": [\n" + + " {\n" + + " \"topic\": \"test-topic-1\",\n" + + " \"partition\": 0,\n" + + " \"offset\": 15,\n" + + " \"timestamp\": 1545084650987,\n" + + " \"timestampType\": \"CREATE_TIME\",\n" + + " \"key\": null,\n" + + " \"value\": \"" + base64Value + "\",\n" + + " \"headers\": [],\n" + + " \"keySchemaMetadata\": {\n" + + " \"schemaId\": \"123\",\n" + + " \"dataFormat\": \"PROTOBUF\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Type type = TestUtils.createConsumerRecordsType(String.class, TestProductPojo.class); + + // When + ConsumerRecords<String, TestProductPojo> records; + if (inputType == InputType.INPUT_STREAM) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(kafkaJson.getBytes()); + records = deserializer.fromJson(inputStream, type); + } else { + records = deserializer.fromJson(kafkaJson, type); + } + + // Then + assertThat(records).isNotNull(); + TopicPartition tp = new TopicPartition("test-topic-1", 0); + List<ConsumerRecord<String, TestProductPojo>> topicRecords = records.records(tp); + assertThat(topicRecords).hasSize(1); + + ConsumerRecord<String, TestProductPojo> consumerRecord = topicRecords.get(0); + assertThat(consumerRecord.value()).isNotNull(); + assertThat(consumerRecord.value().getId()).isEqualTo(456); + } + + // Test implementation of AbstractKafkaDeserializer + private static final class TestDeserializer extends AbstractKafkaDeserializer { + @Override + protected <T> T deserializeObject(byte[] data, Class<T> type, SchemaRegistryType schemaRegistryType) + throws IOException { + return objectMapper.readValue(data, type); + } + } + + enum InputType { + INPUT_STREAM, STRING + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializerTest.java new file mode 100644 index 000000000..f501d7d98 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializerTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.lambda.powertools.kafka.testutils.TestUtils.serializeAvro; + +import java.io.IOException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct; + +class KafkaAvroDeserializerTest { + + private KafkaAvroDeserializer deserializer; + + @BeforeEach + void setUp() { + deserializer = new KafkaAvroDeserializer(); + } + + @Test + void shouldThrowExceptionWhenTypeIsNotAvroSpecificRecord() { + // Given + byte[] data = new byte[] { 1, 2, 3 }; + + // When/Then + assertThatThrownBy(() -> deserializer.deserializeObject(data, String.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Unsupported type for Avro deserialization"); + } + + @Test + void shouldDeserializeValidAvroData() throws IOException { + // Given + TestProduct product = new TestProduct(123, "Test Product", 99.99); + byte[] avroData = serializeAvro(product); + + // When + TestProduct result = deserializer.deserializeObject(avroData, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(123); + assertThat(result.getName()).isEqualTo("Test Product"); + assertThat(result.getPrice()).isEqualTo(99.99); + } + + @Test + void shouldThrowExceptionWhenDeserializingInvalidAvroData() { + // Given + byte[] invalidAvroData = new byte[] { 1, 2, 3, 4, 5 }; + + // When/Then + assertThatThrownBy(() -> deserializer.deserializeObject(invalidAvroData, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Failed to deserialize Avro data"); + } + +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializerTest.java new file mode 100644 index 000000000..c01e09d8d --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializerTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.kafka.testutils.TestProductPojo; + +class KafkaJsonDeserializerTest { + + private KafkaJsonDeserializer deserializer; + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() { + deserializer = new KafkaJsonDeserializer(); + } + + @Test + void shouldThrowExceptionWhenTypeIsNotSupportedForJson() { + // Given + byte[] data = new byte[] { 1, 2, 3 }; + + // When/Then + assertThatThrownBy(() -> deserializer.deserializeObject(data, Object.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(JsonParseException.class); + } + + @Test + void shouldDeserializeValidJsonData() throws IOException { + // Given + TestProductPojo product = new TestProductPojo(123, "Test Product", 99.99, Arrays.asList("tag1", "tag2")); + byte[] jsonData = objectMapper.writeValueAsBytes(product); + + // When + TestProductPojo result = deserializer.deserializeObject(jsonData, TestProductPojo.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(123); + assertThat(result.getName()).isEqualTo("Test Product"); + assertThat(result.getPrice()).isEqualTo(99.99); + assertThat(result.getTags()).containsExactly("tag1", "tag2"); + } + +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializerTest.java new file mode 100644 index 000000000..34a376947 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializerTest.java @@ -0,0 +1,200 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.serializers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.kafka.common.utils.ByteUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.google.protobuf.CodedOutputStream; + +import software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct; + +class KafkaProtobufDeserializerTest { + + private KafkaProtobufDeserializer deserializer; + + @BeforeEach + void setUp() { + deserializer = new KafkaProtobufDeserializer(); + } + + @Test + void shouldThrowExceptionWhenTypeIsNotProtobufMessage() { + // Given + byte[] data = new byte[] { 1, 2, 3 }; + + // When/Then + assertThatThrownBy(() -> deserializer.deserializeObject(data, String.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Unsupported type for Protobuf deserialization"); + } + + @Test + void shouldDeserializeValidProtobufData() throws IOException { + // Given + TestProduct product = TestProduct.newBuilder() + .setId(123) + .setName("Test Product") + .setPrice(99.99) + .build(); + byte[] protobufData = product.toByteArray(); + + // When + TestProduct result = deserializer.deserializeObject(protobufData, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(123); + assertThat(result.getName()).isEqualTo("Test Product"); + assertThat(result.getPrice()).isEqualTo(99.99); + } + + @Test + void shouldThrowExceptionWhenDeserializingInvalidProtobufData() { + // Given + byte[] invalidProtobufData = new byte[] { 1, 2, 3, 4, 5 }; + + // When/Then + assertThatThrownBy(() -> deserializer.deserializeObject(invalidProtobufData, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Failed to deserialize Protobuf data"); + } + + @Test + void shouldDeserializeProtobufDataWithSimpleMessageIndexGlue() throws IOException { + // Given + TestProduct product = TestProduct.newBuilder() + .setId(456) + .setName("Simple Index Product") + .setPrice(199.99) + .build(); + + // Create protobuf data with simple message index (single 0) + byte[] protobufDataWithSimpleIndex = createProtobufDataWithGlueMagicByte(product); + + // When + TestProduct result = deserializer.deserializeObject(protobufDataWithSimpleIndex, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.GLUE); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(456); + assertThat(result.getName()).isEqualTo("Simple Index Product"); + assertThat(result.getPrice()).isEqualTo(199.99); + } + + @Test + void shouldDeserializeProtobufDataWithComplexMessageIndex() throws IOException { + // Given + TestProduct product = TestProduct.newBuilder() + .setId(789) + .setName("Complex Index Product") + .setPrice(299.99) + .build(); + + // Create protobuf data with complex message index (array [2,2]) + byte[] protobufDataWithComplexIndex = createProtobufDataWithComplexMessageIndexConfluent(product); + + // When + TestProduct result = deserializer.deserializeObject(protobufDataWithComplexIndex, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.CONFLUENT); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(789); + assertThat(result.getName()).isEqualTo("Complex Index Product"); + assertThat(result.getPrice()).isEqualTo(299.99); + } + + @Test + void shouldDeserializeProtobufDataWithSimpleMessageIndexConfluent() throws IOException { + // Given + TestProduct product = TestProduct.newBuilder() + .setId(789) + .setName("Complex Index Product") + .setPrice(299.99) + .build(); + + // Create protobuf data with simple message index for Confluent + byte[] protobufDataWithComplexIndex = createProtobufDataWithSimpleMessageIndexConfluent(product); + + // When + TestProduct result = deserializer.deserializeObject(protobufDataWithComplexIndex, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.CONFLUENT); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(789); + assertThat(result.getName()).isEqualTo("Complex Index Product"); + assertThat(result.getPrice()).isEqualTo(299.99); + } + + private byte[] createProtobufDataWithGlueMagicByte(TestProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); + + // Write simple message index for Glue (single UInt32) + codedOutput.writeUInt32NoTag(1); + + // Write the protobuf data + product.writeTo(codedOutput); + + codedOutput.flush(); + return baos.toByteArray(); + } + + private byte[] createProtobufDataWithSimpleMessageIndexConfluent(TestProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Write optimized simple message index for Confluent (single 0 byte for [0]) + // https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format + baos.write(0); + + // Write the protobuf data + baos.write(product.toByteArray()); + + return baos.toByteArray(); + } + + private byte[] createProtobufDataWithComplexMessageIndexConfluent(TestProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Write complex message index array [1,0] using ByteUtils + // https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format + ByteBuffer buffer = ByteBuffer.allocate(1024); + ByteUtils.writeVarint(2, buffer); // Array length + ByteUtils.writeVarint(1, buffer); // First index value + ByteUtils.writeVarint(0, buffer); // Second index value + + buffer.flip(); + byte[] indexData = new byte[buffer.remaining()]; + buffer.get(indexData); + baos.write(indexData); + + // Write the protobuf data + baos.write(product.toByteArray()); + + return baos.toByteArray(); + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/AvroHandler.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/AvroHandler.java new file mode 100644 index 000000000..d0fc9c1ba --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/AvroHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.testutils; + +import org.apache.kafka.clients.consumer.ConsumerRecords; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; +import software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct; + +public class AvroHandler implements RequestHandler<ConsumerRecords<String, TestProduct>, String> { + @Override + @Deserialization(type = DeserializationType.KAFKA_AVRO) + public String handleRequest(ConsumerRecords<String, TestProduct> input, Context context) { + return "OK"; + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/DefaultHandler.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/DefaultHandler.java new file mode 100644 index 000000000..31e93d872 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/DefaultHandler.java @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.testutils; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; + +// This is a non-Kafka specific handler. Just a handler using default deserialization into a Pojo. Used for testing +// fallback to default Lambda serialization. +public class DefaultHandler implements RequestHandler<TestProductPojo, String> { + @Override + @Deserialization(type = DeserializationType.LAMBDA_DEFAULT) + public String handleRequest(TestProductPojo input, Context context) { + return "OK"; + } +} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/InputStreamHandler.java similarity index 62% rename from powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java rename to powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/InputStreamHandler.java index de096679f..63e225ab8 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/InputStreamHandler.java @@ -9,21 +9,22 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ +package software.amazon.lambda.powertools.kafka.testutils; -package software.amazon.lambda.powertools.sqs.handlers; +import java.io.IOException; +import java.io.InputStream; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - -public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { +public class InputStreamHandler implements RequestHandler<InputStream, String> { @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); + public String handleRequest(InputStream input, Context context) { + try { + return new String(input.readAllBytes()); + } catch (IOException e) { + throw new RuntimeException("Failed to read input stream", e); + } } } diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/JsonHandler.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/JsonHandler.java new file mode 100644 index 000000000..b6422f73c --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/JsonHandler.java @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.testutils; + +import org.apache.kafka.clients.consumer.ConsumerRecords; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; + +public class JsonHandler implements RequestHandler<ConsumerRecords<String, TestProductPojo>, String> { + @Override + @Deserialization(type = DeserializationType.KAFKA_JSON) + public String handleRequest(ConsumerRecords<String, TestProductPojo> input, Context context) { + return "OK"; + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/ProtobufHandler.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/ProtobufHandler.java new file mode 100644 index 000000000..a4ce61765 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/ProtobufHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.testutils; + +import org.apache.kafka.clients.consumer.ConsumerRecords; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.kafka.Deserialization; +import software.amazon.lambda.powertools.kafka.DeserializationType; +import software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct; + +public class ProtobufHandler implements RequestHandler<ConsumerRecords<String, TestProduct>, String> { + @Override + @Deserialization(type = DeserializationType.KAFKA_PROTOBUF) + public String handleRequest(ConsumerRecords<String, TestProduct> input, Context context) { + return "OK"; + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/StringHandler.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/StringHandler.java new file mode 100644 index 000000000..3ac5649f1 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/StringHandler.java @@ -0,0 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.testutils; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +public class StringHandler implements RequestHandler<String, String> { + @Override + public String handleRequest(String input, Context context) { + return input; + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestProductPojo.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestProductPojo.java new file mode 100644 index 000000000..8cd261aef --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestProductPojo.java @@ -0,0 +1,87 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.testutils; + +import java.util.List; +import java.util.Objects; + +/** + * Simple POJO for testing JSON deserialization + */ +public class TestProductPojo { + private int id; + private String name; + private double price; + private List<String> tags; + + // Default constructor required for Jackson + public TestProductPojo() { + } + + public TestProductPojo(int id, String name, double price, List<String> tags) { + this.id = id; + this.name = name; + this.price = price; + this.tags = tags; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + public List<String> getTags() { + return tags; + } + + public void setTags(List<String> tags) { + this.tags = tags; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + TestProductPojo that = (TestProductPojo) o; + return id == that.id && + Double.compare(that.price, price) == 0 && + Objects.equals(name, that.name) && + Objects.equals(tags, that.tags); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, price, tags); + } +} diff --git a/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestUtils.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestUtils.java new file mode 100644 index 000000000..33623a9b2 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestUtils.java @@ -0,0 +1,75 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.lambda.powertools.kafka.testutils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import org.apache.avro.io.BinaryEncoder; +import org.apache.avro.io.DatumWriter; +import org.apache.avro.io.EncoderFactory; +import org.apache.avro.specific.SpecificDatumWriter; +import org.apache.avro.specific.SpecificRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; + +/** + * Utility class for common test functions + */ +public class TestUtils { + + /** + * Helper method to create a ParameterizedType for ConsumerRecords + * + * @param keyClass The class for the key type + * @param valueClass The class for the value type + * @return A Type representing ConsumerRecords<K, V> + */ + public static Type createConsumerRecordsType(final Class<?> keyClass, final Class<?> valueClass) { + return new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return new Type[] { keyClass, valueClass }; + } + + @Override + public Type getRawType() { + return ConsumerRecords.class; + } + + @Override + public Type getOwnerType() { + return null; + } + }; + } + + /** + * Helper method to serialize an Avro object + * + * @param <T> The type of the Avro record + * @param consumerRecord The Avro record to serialize + * @return The serialized bytes + * @throws IOException If serialization fails + */ + public static <T extends SpecificRecord> byte[] serializeAvro(T consumerRecord) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(baos, null); + @SuppressWarnings("unchecked") + DatumWriter<T> writer = new SpecificDatumWriter<>((Class<T>) consumerRecord.getClass()); + writer.write(consumerRecord, encoder); + encoder.flush(); + return baos.toByteArray(); + } +} diff --git a/powertools-kafka/src/test/proto/TestProduct.proto b/powertools-kafka/src/test/proto/TestProduct.proto new file mode 100644 index 000000000..53c654494 --- /dev/null +++ b/powertools-kafka/src/test/proto/TestProduct.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package software.amazon.lambda.powertools.kafka.serializers.test.protobuf; + +option java_package = "software.amazon.lambda.powertools.kafka.serializers.test.protobuf"; +option java_outer_classname = "TestProductOuterClass"; +option java_multiple_files = true; + +message TestProduct { + int32 id = 1; + string name = 2; + double price = 3; +} \ No newline at end of file diff --git a/powertools-kafka/src/test/resources/simplelogger.properties b/powertools-kafka/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..167581f74 --- /dev/null +++ b/powertools-kafka/src/test/resources/simplelogger.properties @@ -0,0 +1,13 @@ +# SLF4J Simple Logger configuration for tests +org.slf4j.simpleLogger.defaultLogLevel=debug +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss.SSS +org.slf4j.simpleLogger.showThreadName=true +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false + +# Redirect logs to a file instead of console to avoid bloated console output during tests +org.slf4j.simpleLogger.logFile=target/test.log + +# Set specific logger levels +org.slf4j.simpleLogger.log.software.amazon.lambda.powertools=debug diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index a9ded60c5..ad5910ec1 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -14,53 +14,39 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> -<description>A suite of utilities for AWS Lambda Functions that makes handling large messages in SQS and SNS easier.</description> + <description>A suite of utilities for AWS Lambda Functions that makes handling large messages in SQS and SNS easier.</description> <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> <artifactId>powertools-large-messages</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Large messages</name> - - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> + <name>Powertools for AWS Lambda (Java) - Large messages</name> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -125,7 +111,12 @@ </dependency> <dependency> <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> <scope>test</scope> </dependency> <dependency> @@ -133,6 +124,11 @@ <artifactId>commons-lang3</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> @@ -156,11 +152,7 @@ </environmentVariables> </configuration> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java index 4e556966c..eb5b368a5 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java @@ -20,15 +20,15 @@ import java.lang.annotation.Target; /** - * <p>Use this annotation to handle large messages (> 256 KB) from SQS or SNS. + * <p>Use this annotation to handle large messages (> 1 MB) from SQS or SNS. * When large messages are sent to an SQS Queue or SNS Topic, they are offloaded to S3 and only a reference is passed in the message/record.</p> * * <p>{@code @LargeMessage} automatically retrieves and deletes messages * which have been offloaded to S3 using the {@code amazon-sqs-java-extended-client-lib} or {@code amazon-sns-java-extended-client-lib} * client libraries.</p> * - * <p>This version of the {@code @LargeMessage} is compatible with version - * 1.1.0+ of {@code amazon-sqs-java-extended-client-lib} / {@code amazon-sns-java-extended-client-lib}.</p> + * <p>This version of the {@code @LargeMessage} is compatible with version 1.1.0+ and 2.0.0+ + * of {@code amazon-sqs-java-extended-client-lib} / {@code amazon-sns-java-extended-client-lib}.</p> * <br/> * <p>Put this annotation on a method where the first parameter is either a {@link com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage} or {@link com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord}. * <br/> @@ -54,9 +54,11 @@ * </pre> * </p> * - * <p><b>Note 1</b>: Retrieving payloads and deleting objects from S3 will increase the duration of the + * <p><b>Note 1</b>: The message object (SQSMessage or SNSRecord) is modified in-place to avoid duplicating + * the large blob in memory. The message body will be replaced with the S3 object content.</p> + * <p><b>Note 2</b>: Retrieving payloads and deleting objects from S3 will increase the duration of the * Lambda function.</p> - * <p><b>Note 2</b>: Make sure to configure your function with enough memory to be able to retrieve S3 objects.</p> + * <p><b>Note 3</b>: Make sure to configure your function with enough memory to be able to retrieve S3 objects.</p> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfig.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfig.java index fb8ea9b15..6ad529496 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfig.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfig.java @@ -14,7 +14,7 @@ package software.amazon.lambda.powertools.largemessages; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_REGION_ENV; +import static software.amazon.lambda.powertools.common.internal.LambdaConstants.AWS_REGION_ENV; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java new file mode 100644 index 000000000..52675d3eb --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java @@ -0,0 +1,156 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.largemessages; + +import java.util.Optional; +import java.util.function.Function; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import software.amazon.lambda.powertools.largemessages.internal.LargeMessageProcessor; +import software.amazon.lambda.powertools.largemessages.internal.LargeMessageProcessorFactory; + +/** + * Functional API for processing large messages without AspectJ. + * <p> + * Use this class to handle large messages (> 1 MB) from SQS or SNS. + * When large messages are sent to an SQS Queue or SNS Topic, they are offloaded to S3 and only a reference is passed in the message/record. + * <p> + * {@code LargeMessages} automatically retrieves and optionally deletes messages + * which have been offloaded to S3 using the {@code amazon-sqs-java-extended-client-lib} or {@code amazon-sns-java-extended-client-lib} + * client libraries. + * <p> + * This version is compatible with version 1.1.0+ and 2.0.0+ of {@code amazon-sqs-java-extended-client-lib} / {@code amazon-sns-java-extended-client-lib}. + * <p> + * <u>SQS Example</u>: + * <pre> + * public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + * private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + * + * public SqsBatchHandler() { + * handler = new BatchMessageHandlerBuilder() + * .withSqsBatchHandler() + * .buildWithRawMessageHandler(this::processMessage); + * } + * + * @Override + * public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + * return handler.processBatch(sqsEvent, context); + * } + * + * private void processMessage(SQSEvent.SQSMessage sqsMessage) { + * LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage); + * } + * + * private void handleProcessedMessage(SQSEvent.SQSMessage processedMessage) { + * // processedMessage.getBody() will contain the content of the S3 Object + * } + * } + * </pre> + * <p> + * To disable the deletion of S3 objects: + * <pre> + * LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage, false); + * </pre> + * <p> + * For multi-argument methods, use a lambda to pass additional parameters: + * <pre> + * public void handleRequest(SQSEvent event, Context context) { + * event.getRecords().forEach(message -> + * LargeMessages.processLargeMessage(message, processedMsg -> processMessage(processedMsg, context)) + * ); + * } + * + * private void processMessage(SQSMessage processedMessage, Context context) { + * // processedMessage.getBody() will contain the content of the S3 Object + * } + * </pre> + * <p> + * <b>Note 1</b>: The message object (SQSMessage or SNSRecord) is modified in-place to avoid duplicating + * the large blob in memory. The message body will be replaced with the S3 object content. + * <p> + * <b>Note 2</b>: Retrieving payloads and deleting objects from S3 will increase the duration of the Lambda function. + * <p> + * <b>Note 3</b>: Make sure to configure your function with enough memory to be able to retrieve S3 objects. + * + * @see LargeMessage + */ +public final class LargeMessages { + + private static final Logger LOG = LoggerFactory.getLogger(LargeMessages.class); + + private LargeMessages() { + // Utility class + } + + /** + * Process a large message and execute the function with the processed message. + * <p> + * The S3 object will be deleted after processing (default behavior). + * To disable S3 object deletion, use {@link #processLargeMessage(Object, Function, boolean)}. + * <p> + * Example usage: + * <pre> + * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, this::handleMessage); + * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, processedMsg -> processOrder(processedMsg, orderId, amount)); + * </pre> + * + * @param message the message to process (SQSMessage or SNSRecord) + * @param function the function to execute with the processed message + * @param <T> the message type + * @param <R> the return type of the function + * @return the result of the function execution + */ + public static <T, R> R processLargeMessage(T message, Function<T, R> function) { + return processLargeMessage(message, function, true); + } + + /** + * Process a large message and execute the function with the processed message. + * <p> + * Example usage: + * <pre> + * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, this::handleMessage, false); + * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, processedMsg -> processOrder(processedMsg, orderId, amount), false); + * </pre> + * + * @param message the message to process (SQSMessage or SNSRecord) + * @param function the function to execute with the processed message + * @param deleteS3Object whether to delete the S3 object after processing + * @param <T> the message type + * @param <R> the return type of the function + * @return the result of the function execution + */ + public static <T, R> R processLargeMessage(T message, Function<T, R> function, boolean deleteS3Object) { + Optional<LargeMessageProcessor<?>> processor = LargeMessageProcessorFactory.get(message); + + if (!processor.isPresent()) { + LOG.warn("Unsupported message type [{}], proceeding without large message processing", + message.getClass()); + return function.apply(message); + } + + try { + @SuppressWarnings("unchecked") + LargeMessageProcessor<T> typedProcessor = (LargeMessageProcessor<T>) processor.get(); + return typedProcessor.process(message, function::apply, deleteS3Object); + } catch (RuntimeException e) { + throw e; + } catch (Throwable t) { + throw new LargeMessageProcessingException("Failed to process large message", t); + } + } +} diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java index 2aa81691f..0a8b93095 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java @@ -15,12 +15,14 @@ package software.amazon.lambda.powertools.largemessages.internal; import java.util.Optional; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.largemessages.LargeMessage; /** @@ -31,17 +33,17 @@ public class LargeMessageAspect { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageAspect.class); - @SuppressWarnings({"EmptyMethod"}) + @SuppressWarnings({ "EmptyMethod" }) @Pointcut("@annotation(largeMessage)") public void callAt(LargeMessage largeMessage) { + // Pointcut method - body intentionally empty } + @SuppressWarnings("unchecked") @Around(value = "callAt(largeMessage) && execution(@LargeMessage * *.*(..))", argNames = "pjp,largeMessage") - public Object around(ProceedingJoinPoint pjp, - LargeMessage largeMessage) throws Throwable { + public Object around(ProceedingJoinPoint pjp, LargeMessage largeMessage) throws Throwable { Object[] proceedArgs = pjp.getArgs(); - // we need a message to process if (proceedArgs.length == 0) { LOG.warn("@LargeMessage annotation is placed on a method without any message to process, proceeding"); return pjp.proceed(proceedArgs); @@ -56,7 +58,8 @@ public Object around(ProceedingJoinPoint pjp, return pjp.proceed(proceedArgs); } - return largeMessageProcessor.get().process(pjp, largeMessage.deleteS3Object()); + return ((LargeMessageProcessor<Object>) largeMessageProcessor.get()).process(message, + msg -> pjp.proceed(proceedArgs), largeMessage.deleteS3Object()); } } diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java new file mode 100644 index 000000000..1690eedac --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.largemessages.internal; + +/** + * Functional interface for large message processing. + * <p> + * This interface is similar to {@link java.util.function.Function} but throws {@link Throwable} + * instead of no exceptions. This is necessary to support AspectJ's {@code ProceedingJoinPoint.proceed()} + * which throws {@code Throwable}, allowing exceptions to bubble up naturally without wrapping. + * <p> + * This interface should not be exposed to user-facing APIs such as + * {@link software.amazon.lambda.powertools.largemessages.LargeMessages}. These should use plain + * {@link java.util.function.Function}. + * + * @param <T> the input type (message type) + * @param <R> the return type of the function + */ +@FunctionalInterface +public interface LargeMessageFunction<T, R> { + @SuppressWarnings("java:S112") // Throwable is required for AspectJ compatibility + R apply(T processedMessage) throws Throwable; +} diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java index 5478931f1..c41af0cea 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java @@ -17,9 +17,10 @@ import static java.lang.String.format; import java.nio.charset.StandardCharsets; -import org.aspectj.lang.ProceedingJoinPoint; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import software.amazon.awssdk.core.exception.SdkException; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.lambda.powertools.largemessages.LargeMessageConfig; @@ -28,33 +29,44 @@ import software.amazon.payloadoffloading.S3Dao; /** - * Abstract processor for Large Messages. Handle the download from S3 and replace the actual S3 pointer with the content - * of the S3 Object leveraging the payloadoffloading library. + * Abstract processor for Large Messages. + * <p> + * Handles the download from S3 and replaces the S3 pointer with the actual content + * of the S3 Object, leveraging the payloadoffloading library. * - * @param <T> any message type that support Large Messages with S3 pointers - * ({@link com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage} and {@link com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord} at the moment) + * @param <T> any message type that supports Large Messages with S3 pointers + * ({@link com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage} + * and {@link com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord}) */ -abstract class LargeMessageProcessor<T> { +public abstract class LargeMessageProcessor<T> { protected static final String RESERVED_ATTRIBUTE_NAME = "ExtendedPayloadSize"; private static final Logger LOG = LoggerFactory.getLogger(LargeMessageProcessor.class); private final S3Client s3Client = LargeMessageConfig.get().getS3Client(); private final S3BackedPayloadStore payloadStore = new S3BackedPayloadStore(new S3Dao(s3Client), "DUMMY"); - public Object process(ProceedingJoinPoint pjp, boolean deleteS3Object) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); - T message = (T) proceedArgs[0]; - + /** + * Process a large message using a functional interface. + * + * @param message the message to process + * @param function the function to execute with the processed message + * @param deleteS3Object whether to delete the S3 object after processing + * @param <R> the return type of the wrapped function + * @return the result of the function execution + * @throws Throwable if an error occurs during processing + */ + public <R> R process(T message, LargeMessageFunction<T, R> function, boolean deleteS3Object) throws Throwable { if (!isLargeMessage(message)) { LOG.warn("Not a large message, proceeding"); - return pjp.proceed(proceedArgs); + return function.apply(message); } String payloadPointer = getMessageContent(message); if (payloadPointer == null) { LOG.warn("No content in the message, proceeding"); - return pjp.proceed(proceedArgs); + return function.apply(message); } + // legacy attribute (sqs only) payloadPointer = payloadPointer.replace("com.amazon.sqs.javamessaging.MessageS3Pointer", "software.amazon.payloadoffloading.PayloadS3Pointer"); @@ -73,7 +85,7 @@ public Object process(ProceedingJoinPoint pjp, boolean deleteS3Object) throws Th updateMessageContent(message, s3ObjectContent); removeLargeMessageAttributes(message); - Object response = pjp.proceed(proceedArgs); + R result = function.apply(message); if (deleteS3Object) { if (LOG.isInfoEnabled()) { @@ -82,45 +94,45 @@ public Object process(ProceedingJoinPoint pjp, boolean deleteS3Object) throws Th deleteS3Object(payloadPointer); } - return response; + return result; } /** - * Retrieve the message id + * Retrieve the message ID. * - * @param message the message itself - * @return the id of the message (String format) + * @param message the message + * @return the message ID */ protected abstract String getMessageId(T message); /** - * Retrieve the content of the message (ex: body of an SQSMessage) + * Retrieve the content of the message (e.g., body of an SQSMessage). * - * @param message the message itself - * @return the content of the message (String format) + * @param message the message + * @return the message content */ protected abstract String getMessageContent(T message); /** - * Update the message content of the message (ex: body of an SQSMessage) + * Update the message content (e.g., body of an SQSMessage). * - * @param message the message itself - * @param messageContent the new content of the message (String format) + * @param message the message + * @param messageContent the new message content */ protected abstract void updateMessageContent(T message, String messageContent); /** - * Check if the message is actually a large message (indicator in message attributes) + * Check if the message is a large message (based on message attributes). * - * @param message the message itself - * @return true if the message is a large message + * @param message the message + * @return true if the message is a large message, false otherwise */ protected abstract boolean isLargeMessage(T message); /** - * Remove the large message indicator (in message attributes) + * Remove the large message indicator from message attributes. * - * @param message the message itself + * @param message the message */ protected abstract void removeLargeMessageAttributes(T message); diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java index 26c33738a..06ee92968 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java @@ -14,14 +14,15 @@ package software.amazon.lambda.powertools.largemessages.internal; +import java.util.Optional; + import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import java.util.Optional; -class LargeMessageProcessorFactory { +public final class LargeMessageProcessorFactory { private LargeMessageProcessorFactory() { - // not intended to be instantiated + // Utility class } public static Optional<LargeMessageProcessor<?>> get(Object message) { diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptor.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptor.java new file mode 100644 index 000000000..87d179dd7 --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.largemessages.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-largemessages module is on the classpath. + */ +public final class LargeMessagesUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("large-messages"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-large-messages/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-large-messages/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..ab5a6f378 --- /dev/null +++ b/powertools-large-messages/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.largemessages.internal.LargeMessagesUserAgentInterceptor diff --git a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java new file mode 100644 index 000000000..7bd3e80e2 --- /dev/null +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java @@ -0,0 +1,287 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.largemessages; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNS; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.MessageAttribute; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.S3Exception; + +@ExtendWith(MockitoExtension.class) +class LargeMessagesTest { + + private static final String BIG_MSG = "A biiiiiiiig message"; + private static final String BUCKET_NAME = "bucketname"; + private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; + private static final String BIG_MESSAGE_BODY = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\", {\"s3BucketName\":\"" + + BUCKET_NAME + + "\", \"s3Key\":\"" + BUCKET_KEY + "\"}]"; + + @Mock + private S3Client s3Client; + + @BeforeEach + void init() throws NoSuchFieldException, IllegalAccessException { + // need to clean the s3Client with introspection (singleton) + Field client = LargeMessageConfig.class.getDeclaredField("s3Client"); + client.setAccessible(true); + client.set(LargeMessageConfig.get(), null); + LargeMessageConfig.init().withS3Client(s3Client); + } + + @Test + void testProcessLargeSQSMessage_shouldRetrieveFromS3AndDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody); + + // then + assertThat(result).isEqualTo(BIG_MSG); + ArgumentCaptor<DeleteObjectRequest> delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); + verify(s3Client).deleteObject(delete.capture()); + assertThat(delete.getValue().bucket()).isEqualTo(BUCKET_NAME); + assertThat(delete.getValue().key()).isEqualTo(BUCKET_KEY); + } + + @Test + void testProcessLargeSQSMessage_withDeleteDisabled_shouldNotDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody, false); + + // then + assertThat(result).isEqualTo(BIG_MSG); + verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessLargeSNSMessage_shouldRetrieveFromS3AndDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SNSRecord snsRecord = snsRecordWithMessage(BIG_MESSAGE_BODY, true); + + // when + String result = LargeMessages.processLargeMessage(snsRecord, msg -> msg.getSNS().getMessage()); + + // then + assertThat(result).isEqualTo(BIG_MSG); + verify(s3Client).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessSmallMessage_shouldNotInteractWithS3() { + // given + SQSMessage sqsMessage = sqsMessageWithBody("Small message", false); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody); + + // then + assertThat(result).isEqualTo("Small message"); + verifyNoInteractions(s3Client); + } + + @Test + void testProcessUnsupportedMessageType_shouldCallHandlerDirectly() { + // given + KinesisEventRecord kinesisRecord = new KinesisEventRecord(); + kinesisRecord.setEventID("kinesis-123"); + + // when + String result = LargeMessages.processLargeMessage(kinesisRecord, KinesisEventRecord::getEventID); + + // then + assertThat(result).isEqualTo("kinesis-123"); + verifyNoInteractions(s3Client); + } + + @Test + void testProcessMessageWithNullBody_shouldCallHandler() { + // given + SQSMessage sqsMessage = sqsMessageWithBody(null, true); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody); + + // then + assertThat(result).isNull(); + verifyNoInteractions(s3Client); + } + + @Test + void testProcessMessage_whenS3GetFails_shouldThrowException() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))) + .thenThrow(S3Exception.create("Access denied", new Exception("Permission denied"))); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody)) + .isInstanceOf(LargeMessageProcessingException.class) + .hasMessageContaining("Failed processing S3 record"); + } + + @Test + void testProcessMessage_whenS3DeleteFails_shouldThrowException() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + when(s3Client.deleteObject(any(DeleteObjectRequest.class))) + .thenThrow(S3Exception.create("Access denied", new Exception("Permission denied"))); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody)) + .isInstanceOf(LargeMessageProcessingException.class) + .hasMessageContaining("Failed deleting S3 record"); + } + + @Test + void testProcessMessage_whenHandlerThrowsRuntimeException_shouldPropagate() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> LargeMessages.processLargeMessage(sqsMessage, msg -> { + throw new IllegalStateException("Handler error"); + })) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Handler error"); + } + + @Test + void testProcessLargeMessage_withMultiParam_shouldRetrieveFromS3AndDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String orderId = "order-123"; + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, msg -> processOrderSimple(msg, orderId)); + + // then + assertThat(result).isEqualTo("order-123-processed"); + verify(s3Client).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessLargeMessage_withMultiParamAndDeleteDisabled_shouldNotDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String orderId = "order-456"; + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, msg -> processOrderSimple(msg, orderId), false); + + // then + assertThat(result).isEqualTo("order-456-processed"); + verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessLargeMessage_shouldModifyMessageInPlace() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String originalBody = sqsMessage.getBody(); + + // when + LargeMessages.processLargeMessage(sqsMessage, msg -> { + assertThat(msg.getBody()).isEqualTo(BIG_MSG); + return null; + }); + + // then - verify the original message object was modified + assertThat(sqsMessage.getBody()).isEqualTo(BIG_MSG); + assertThat(sqsMessage.getBody()).isNotEqualTo(originalBody); + } + + private String processOrderSimple(SQSMessage message, String orderId) { + assertThat(message.getBody()).isEqualTo(BIG_MSG); + return orderId + "-processed"; + } + + private ResponseInputStream<GetObjectResponse> s3ObjectWithLargeMessage() { + return new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream(BIG_MSG.getBytes()))); + } + + private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage) { + SQSMessage sqsMessage = new SQSMessage(); + sqsMessage.setBody(messageBody); + if (messageBody != null) { + sqsMessage.setMd5OfBody("dummy-md5"); + } + + if (largeMessage) { + Map<String, MessageAttribute> attributeMap = new HashMap<>(); + MessageAttribute payloadAttribute = new MessageAttribute(); + payloadAttribute.setStringValue("300450"); + payloadAttribute.setDataType("Number"); + attributeMap.put("ExtendedPayloadSize", payloadAttribute); + + sqsMessage.setMessageAttributes(attributeMap); + sqsMessage.setMd5OfMessageAttributes("dummy-md5"); + } + return sqsMessage; + } + + private SNSRecord snsRecordWithMessage(String messageBody, boolean largeMessage) { + SNS sns = new SNS().withMessage(messageBody); + if (largeMessage) { + sns.setMessageAttributes(Collections.singletonMap("ExtendedPayloadSize", + new SNSEvent.MessageAttribute())); + } + return new SNSRecord().withSns(sns); + } +} diff --git a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java index c364a89d9..b84709ddc 100644 --- a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java @@ -22,17 +22,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; import static software.amazon.lambda.powertools.largemessages.internal.LargeSQSMessageProcessor.calculateMessageAttributesMd5; import static software.amazon.lambda.powertools.largemessages.internal.LargeSQSMessageProcessor.calculateMessageBodyMd5; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; -import com.amazonaws.services.lambda.runtime.events.SNSEvent; -import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNS; -import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; -import com.amazonaws.services.lambda.runtime.events.SQSEvent.MessageAttribute; -import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import java.io.ByteArrayInputStream; import java.lang.reflect.Field; import java.nio.ByteBuffer; @@ -41,11 +33,22 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; + import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNS; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.MessageAttribute; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.services.s3.S3Client; @@ -53,11 +56,13 @@ import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import software.amazon.awssdk.services.s3.model.S3Exception; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import software.amazon.lambda.powertools.largemessages.LargeMessage; import software.amazon.lambda.powertools.largemessages.LargeMessageConfig; import software.amazon.lambda.powertools.largemessages.LargeMessageProcessingException; -public class LargeMessageAspectTest { +@ExtendWith(MockitoExtension.class) +class LargeMessageAspectTest { private static final String BIG_MSG = "A biiiiiiiig message"; private static final String BIG_MSG_MD5 = "919ebd392d8cb7161f95cb612a903d42"; @@ -65,19 +70,17 @@ public class LargeMessageAspectTest { private static final String BUCKET_NAME = "bucketname"; private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - private static final String BIG_MESSAGE_BODY = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\", {\"s3BucketName\":\"" + BUCKET_NAME + - "\", \"s3Key\":\"" + BUCKET_KEY + "\"}]"; + private static final String BIG_MESSAGE_BODY = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\", {\"s3BucketName\":\"" + + BUCKET_NAME + + "\", \"s3Key\":\"" + BUCKET_KEY + "\"}]"; @Mock private S3Client s3Client; - @Mock - private Context context; + + private final TestLambdaContext context = new TestLambdaContext(); @BeforeEach - public void init() throws NoSuchFieldException, IllegalAccessException { - openMocks(this); - setupContext(); + void init() throws NoSuchFieldException, IllegalAccessException { // need to clean the s3Client with introspection (singleton) Field client = LargeMessageConfig.class.getDeclaredField("s3Client"); client.setAccessible(true); @@ -86,13 +89,13 @@ public void init() throws NoSuchFieldException, IllegalAccessException { } @LargeMessage - private String processSQSMessage(SQSMessage sqsMessage, Context context) { + private String processSQSMessage(SQSMessage sqsMessage, TestLambdaContext context) { return sqsMessage.getBody(); } @LargeMessage private String processSQSMessageWithMd5Checks(SQSMessage transformedMessage, String initialBodyMD5, - String initialAttributesMD5) { + String initialAttributesMD5) { assertThat(transformedMessage.getMd5OfBody()).isNotEqualTo(initialBodyMD5); assertThat(transformedMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); @@ -108,7 +111,7 @@ private String processSNSMessageWithoutContext(SNSRecord snsRecord) { } @LargeMessage(deleteS3Object = false) - private String processSQSMessageNoDelete(SQSMessage sqsMessage, Context context) { + private String processSQSMessageNoDelete(SQSMessage sqsMessage, TestLambdaContext context) { return sqsMessage.getBody(); } @@ -122,8 +125,15 @@ private String processNoMessage() { return "Hello World"; } + @LargeMessage + private void verifyMessageObjectIsModified(SQSMessage sqsMessage) { + // This test verifies the message object itself is modified, not a copy + assertThat(sqsMessage.getBody()).isEqualTo(BIG_MSG); + assertThat(sqsMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); + } + @Test - public void testLargeSQSMessageWithDefaultDeletion() { + void testLargeSQSMessageWithDefaultDeletion() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); @@ -136,8 +146,7 @@ public void testLargeSQSMessageWithDefaultDeletion() { ArgumentCaptor<DeleteObjectRequest> delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { + .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> { assertThat(deleteObjectRequest.bucket()) .isEqualTo(BUCKET_NAME); @@ -147,7 +156,7 @@ public void testLargeSQSMessageWithDefaultDeletion() { } @Test - public void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { + void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); @@ -179,12 +188,12 @@ public void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { } @Test - public void testLargeSNSMessageWithDefaultDeletion() { + void testLargeSNSMessageWithDefaultDeletion() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); SNSRecord snsRecord = snsRecordWithMessage(BIG_MESSAGE_BODY, true); - //when + // when String message = processSNSMessageWithoutContext(snsRecord); // then @@ -192,8 +201,7 @@ public void testLargeSNSMessageWithDefaultDeletion() { ArgumentCaptor<DeleteObjectRequest> delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { + .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> { assertThat(deleteObjectRequest.bucket()) .isEqualTo(BUCKET_NAME); @@ -203,7 +211,7 @@ public void testLargeSNSMessageWithDefaultDeletion() { } @Test - public void testLargeSQSMessageWithNoDeletion_shouldNotDelete() { + void testLargeSQSMessageWithNoDeletion_shouldNotDelete() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); @@ -217,7 +225,7 @@ public void testLargeSQSMessageWithNoDeletion_shouldNotDelete() { } @Test - public void testKinesisMessage_shouldProceedWithoutS3() { + void testKinesisMessage_shouldProceedWithoutS3() { // given KinesisEventRecord kinesisEventRecord = new KinesisEventRecord(); kinesisEventRecord.setEventID("kinesis_id1234567890"); @@ -231,7 +239,7 @@ public void testKinesisMessage_shouldProceedWithoutS3() { } @Test - public void testNoMessage_shouldProceedWithoutS3() { + void testNoMessage_shouldProceedWithoutS3() { // when String message = processNoMessage(); @@ -241,7 +249,7 @@ public void testNoMessage_shouldProceedWithoutS3() { } @Test - public void testSmallMessage_shouldProceedWithoutS3() { + void testSmallMessage_shouldProceedWithoutS3() { // given SQSMessage sqsMessage = sqsMessageWithBody("This is small message", false); @@ -255,7 +263,7 @@ public void testSmallMessage_shouldProceedWithoutS3() { } @Test - public void testNullMessage_shouldProceedWithoutS3() { + void testNullMessage_shouldProceedWithoutS3() { // given SQSMessage sqsMessage = sqsMessageWithBody(null, true); @@ -268,7 +276,7 @@ public void testNullMessage_shouldProceedWithoutS3() { } @Test - public void testGetS3ObjectException_shouldThrowLargeMessageProcessingException() { + void testGetS3ObjectException_shouldThrowLargeMessageProcessingException() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", new Exception("User is not allowed to access bucket " + BUCKET_NAME))); @@ -281,7 +289,7 @@ public void testGetS3ObjectException_shouldThrowLargeMessageProcessingException( } @Test - public void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingException() { + void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingException() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); when(s3Client.deleteObject(any(DeleteObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", @@ -294,6 +302,22 @@ public void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingExcepti .hasMessage(format("Failed deleting S3 record [%s]", BIG_MESSAGE_BODY)); } + @Test + void testMessageObjectIsModifiedInPlace() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String originalBody = sqsMessage.getBody(); + + // when + verifyMessageObjectIsModified(sqsMessage); + + // then - verify the original message object was modified + assertThat(sqsMessage.getBody()).isEqualTo(BIG_MSG); + assertThat(sqsMessage.getBody()).isNotEqualTo(originalBody); + assertThat(sqsMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); + } + private ResponseInputStream<GetObjectResponse> s3ObjectWithLargeMessage() { return new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream(BIG_MSG.getBytes()))); @@ -304,7 +328,7 @@ private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage) } private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage, - Map<String, MessageAttribute> optionalAttributes) { + Map<String, MessageAttribute> optionalAttributes) { SQSMessage sqsMessage = new SQSMessage(); sqsMessage.setBody(messageBody); if (messageBody != null) { @@ -338,10 +362,4 @@ private SNSRecord snsRecordWithMessage(String messageBody, boolean largeMessage) return new SNSRecord().withSns(sns); } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(1024); - } } diff --git a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptorTest.java b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptorTest.java new file mode 100644 index 000000000..b32ee1176 --- /dev/null +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.largemessages.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class LargeMessagesUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/LARGE-MESSAGES/"); + } +} \ No newline at end of file diff --git a/powertools-large-messages/src/test/resources/simplelogger.properties b/powertools-large-messages/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..559c22385 --- /dev/null +++ b/powertools-large-messages/src/test/resources/simplelogger.properties @@ -0,0 +1,7 @@ +org.slf4j.simpleLogger.logFile=target/large-messages-test.log +org.slf4j.simpleLogger.defaultLogLevel=warn +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS +org.slf4j.simpleLogger.showThreadName=false +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 83650fcde..200358e0b 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -14,50 +14,29 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> - <artifactId>powertools-logging</artifactId> - <packaging>jar</packaging> - <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> - <name>Powertools for AWS Lambda (Java) library Logging</name> - <description> - A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. - </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> + <name>Powertools for AWS Lambda (Java) - Logging</name> + <description>Set of utility for better logging - common</description> + <artifactId>powertools-logging</artifactId> + <packaging>jar</packaging> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-serialization</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -68,24 +47,13 @@ <artifactId>jackson-databind</artifactId> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-layout-template-json</artifactId> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> + <scope>provided</scope> </dependency> <!-- Test dependencies --> @@ -100,18 +68,18 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> <scope>test</scope> </dependency> <dependency> @@ -139,14 +107,88 @@ <artifactId>jsonassert</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> </dependencies> - + <profiles> + <profile> + <id>generate-graalvm-files</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-logging</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> <build> + <resources> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_LAMBDA_LOG_FORMAT>JSON</AWS_LAMBDA_LOG_FORMAT> + <POWERTOOLS_SERVICE_NAME>testService</POWERTOOLS_SERVICE_NAME> + <AWS_LAMBDA_INITIALIZATION_TYPE>on-demand</AWS_LAMBDA_INITIALIZATION_TYPE> + </environmentVariables> + </configuration> </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml new file mode 100644 index 000000000..aa4aca181 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -0,0 +1,217 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>powertools-parent</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>2.9.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>powertools-logging-log4j</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Logging with Log4j2</name> + <description>Set of utility for better logging with log4j</description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-layout-template-json</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-tests</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.skyscreamer</groupId> + <artifactId>jsonassert</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-logging-log4j</imageName> + <buildArgs> + <buildArg> + --initialize-at-build-time=org.junit.platform.launcher.core.DiscoveryIssueNotifier$1 + </buildArg> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> + <resources> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <POWERTOOLS_SERVICE_NAME>testLog4j</POWERTOOLS_SERVICE_NAME> + <AWS_REGION>eu-central-1</AWS_REGION> + <_X_AMZN_TRACE_ID>Root=1-63441c4a-abcdef012345678912345678</_X_AMZN_TRACE_ID> + <AWS_LAMBDA_INITIALIZATION_TYPE>on-demand</AWS_LAMBDA_INITIALIZATION_TYPE> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java new file mode 100644 index 000000000..cef5b86ee --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java @@ -0,0 +1,262 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.logging.log4j.layout.template.json.resolver; + +import static java.util.Arrays.stream; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_NAME; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_REQUEST_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_TRACE_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_VERSION; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SAMPLING_RATE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SERVICE; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.layout.template.json.util.JsonWriter; +import org.apache.logging.log4j.util.ReadOnlyStringMap; +import software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.common.internal.SystemWrapper; +import software.amazon.lambda.powertools.logging.argument.StructuredArgument; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; + +/** + * Custom {@link org.apache.logging.log4j.layout.template.json.resolver.TemplateResolver} + * used by {@link org.apache.logging.log4j.layout.template.json.JsonTemplateLayout} + * to be able to recognize powertools fields in the LambdaJsonLayout.json file. + */ +final class PowertoolsResolver implements EventResolver { + + private static final EventResolver COLD_START_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String coldStart = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_COLD_START.getName()); + return null != coldStart; + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String coldStart = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_COLD_START.getName()); + jsonWriter.writeBoolean(Boolean.parseBoolean(coldStart)); + } + }; + + private static final EventResolver FUNCTION_NAME_RESOLVER = + (final LogEvent logEvent, final JsonWriter jsonWriter) -> { + final String functionName = + logEvent.getContextData().getValue(FUNCTION_NAME.getName()); + jsonWriter.writeString(functionName); + }; + + private static final EventResolver FUNCTION_VERSION_RESOLVER = + (final LogEvent logEvent, final JsonWriter jsonWriter) -> { + final String functionVersion = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_VERSION.getName()); + jsonWriter.writeString(functionVersion); + }; + + private static final EventResolver FUNCTION_ARN_RESOLVER = + (final LogEvent logEvent, final JsonWriter jsonWriter) -> { + final String functionArn = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_ARN.getName()); + jsonWriter.writeString(functionArn); + }; + + private static final EventResolver FUNCTION_REQ_RESOLVER = + (final LogEvent logEvent, final JsonWriter jsonWriter) -> { + final String functionRequestId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_REQUEST_ID.getName()); + jsonWriter.writeString(functionRequestId); + }; + + private static final EventResolver FUNCTION_MEMORY_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String functionMemory = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE.getName()); + return null != functionMemory; + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String functionMemory = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE.getName()); + jsonWriter.writeNumber(Integer.parseInt(functionMemory)); + } + }; + + private static final EventResolver SAMPLING_RATE_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String samplingRate = + logEvent.getContextData().getValue(PowertoolsLoggedFields.SAMPLING_RATE.getName()); + try { + return null != samplingRate && Float.parseFloat(samplingRate) > 0.f; + } catch (NumberFormatException nfe) { + return false; + } + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String samplingRate = + logEvent.getContextData().getValue(PowertoolsLoggedFields.SAMPLING_RATE.getName()); + jsonWriter.writeNumber(Float.parseFloat(samplingRate)); + } + }; + + private static final EventResolver XRAY_TRACE_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String traceId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_TRACE_ID.getName()); + return null != traceId; + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String traceId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_TRACE_ID.getName()); + jsonWriter.writeString(traceId); + } + }; + + private static final EventResolver CORRELATION_ID_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String correlationId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.CORRELATION_ID.getName()); + return null != correlationId; + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String correlationId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.CORRELATION_ID.getName()); + jsonWriter.writeString(correlationId); + } + }; + + private static final EventResolver SERVICE_RESOLVER = + (final LogEvent logEvent, final JsonWriter jsonWriter) -> { + final String service = logEvent.getContextData().getValue(PowertoolsLoggedFields.SERVICE.getName()); + jsonWriter.writeString(service); + }; + + private static final EventResolver REGION_RESOLVER = + (final LogEvent logEvent, final JsonWriter jsonWriter) -> + jsonWriter.writeString(SystemWrapper.getenv(LambdaConstants.AWS_REGION_ENV)); + + public static final String LAMBDA_ARN_REGEX = + "^arn:(aws|aws-us-gov|aws-cn):lambda:[a-zA-Z0-9-]+:\\d{12}:function:[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$"; + + private static final EventResolver ACCOUNT_ID_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String arn = logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_ARN.getName()); + return null != arn && !arn.isEmpty() && arn.matches(LAMBDA_ARN_REGEX); + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String arn = logEvent.getContextData().getValue(PowertoolsLoggedFields.FUNCTION_ARN.getName()); + jsonWriter.writeString(arn.split(":")[4]); + } + }; + + @SuppressWarnings("java:S106") + private static final EventResolver NON_POWERTOOLS_FIELD_RESOLVER = + (LogEvent logEvent, JsonWriter jsonWriter) -> { + StringBuilder stringBuilder = jsonWriter.getStringBuilder(); + try (JsonSerializer serializer = new JsonSerializer(stringBuilder)) { + + // remove dummy field to kick in powertools resolver + stringBuilder.setLength(stringBuilder.length() - 4); + + // log other MDC values + ReadOnlyStringMap contextData = logEvent.getContextData(); + contextData.forEach((key, value) -> { + if (!PowertoolsLoggedFields.stringValues().contains(key)) { + serializer.writeSeparator(); + serializer.writeObjectField(key, value); + } + }); + + // log structured arguments + Object[] arguments = logEvent.getMessage().getParameters(); + if (arguments != null) { + stream(arguments).filter(StructuredArgument.class::isInstance).forEach(argument -> { + try { + serializer.writeRaw(','); + ((StructuredArgument) argument).writeTo(serializer); + } catch (IOException e) { + System.err.printf("Failed to encode log event, error: %s.%n", e.getMessage()); + } + }); + } + } + }; + + private final EventResolver internalResolver; + + private static final Map<String, EventResolver> eventResolverMap = Collections + .unmodifiableMap(Stream.of(new Object[][] { + { SERVICE.getName(), SERVICE_RESOLVER }, + { FUNCTION_NAME.getName(), FUNCTION_NAME_RESOLVER }, + { FUNCTION_VERSION.getName(), FUNCTION_VERSION_RESOLVER }, + { FUNCTION_ARN.getName(), FUNCTION_ARN_RESOLVER }, + { FUNCTION_MEMORY_SIZE.getName(), FUNCTION_MEMORY_RESOLVER }, + { FUNCTION_REQUEST_ID.getName(), FUNCTION_REQ_RESOLVER }, + { FUNCTION_COLD_START.getName(), COLD_START_RESOLVER }, + { FUNCTION_TRACE_ID.getName(), XRAY_TRACE_RESOLVER }, + { CORRELATION_ID.getName(), CORRELATION_ID_RESOLVER }, + { SAMPLING_RATE.getName(), SAMPLING_RATE_RESOLVER }, + { "region", REGION_RESOLVER }, + { "account_id", ACCOUNT_ID_RESOLVER } + }).collect(Collectors.toMap(data -> (String) data[0], data -> (EventResolver) data[1]))); + + + PowertoolsResolver(final TemplateResolverConfig config) { + final String fieldName = config.getString("field"); + if (fieldName == null) { + internalResolver = NON_POWERTOOLS_FIELD_RESOLVER; + } else { + internalResolver = eventResolverMap.get(fieldName); + if (internalResolver == null) { + throw new IllegalArgumentException("Unknown field: " + fieldName); + } + } + } + + @Override + public void resolve(LogEvent value, JsonWriter jsonWriter) { + internalResolver.resolve(value, jsonWriter); + } + + @Override + public boolean isResolvable(LogEvent value) { + return value != null && value.getContextData() != null && internalResolver.isResolvable(value); + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverFactory.java similarity index 71% rename from powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java rename to powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverFactory.java index 7d688f469..297c00691 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java +++ b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverFactory.java @@ -12,21 +12,20 @@ * */ -package software.amazon.lambda.powertools.logging.internal; +package org.apache.logging.log4j.layout.template.json.resolver; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginFactory; -import org.apache.logging.log4j.layout.template.json.resolver.EventResolverContext; -import org.apache.logging.log4j.layout.template.json.resolver.EventResolverFactory; -import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolver; -import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig; -import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverFactory; +/** + * Factory for {@link PowertoolsResolver}. Log4j plugin to process powertools fields in the layout.json + */ @Plugin(name = "PowertoolsResolverFactory", category = TemplateResolverFactory.CATEGORY) public final class PowertoolsResolverFactory implements EventResolverFactory { private static final PowertoolsResolverFactory INSTANCE = new PowertoolsResolverFactory(); + private static final String RESOLVER_NAME = "powertools"; private PowertoolsResolverFactory() { } @@ -38,12 +37,12 @@ public static PowertoolsResolverFactory getInstance() { @Override public String getName() { - return PowertoolsResolver.getName(); + return RESOLVER_NAME; } @Override public TemplateResolver<LogEvent> create(EventResolverContext context, TemplateResolverConfig config) { - return new PowertoolsResolver(); + return new PowertoolsResolver(config); } } diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppender.java b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppender.java new file mode 100644 index 000000000..fcf4a2040 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppender.java @@ -0,0 +1,201 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.log4j; + +import java.io.Serializable; +import java.util.Deque; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.AppenderRef; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.message.SimpleMessage; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.internal.BufferManager; +import software.amazon.lambda.powertools.logging.internal.KeyBuffer; + +/** + * A Log4j2 appender that buffers log events by AWS X-Ray trace ID for optimized Lambda logging. + * + * <p>This appender is designed specifically for AWS Lambda functions to reduce log ingestion + * by buffering lower-level logs and only outputting them when errors occur, preserving + * full context for troubleshooting while minimizing routine log volume. + * + * <h3>Key Features:</h3> + * <ul> + * <li><strong>Trace-based buffering:</strong> Groups logs by AWS X-Ray trace ID</li> + * <li><strong>Selective output:</strong> Only buffers logs at or below configured verbosity level</li> + * <li><strong>Auto-flush on errors:</strong> Automatically outputs buffered logs when ERROR/FATAL events occur</li> + * <li><strong>Memory management:</strong> Prevents memory leaks with configurable buffer size limits</li> + * <li><strong>Overflow protection:</strong> Warns when logs are discarded due to buffer limits</li> + * </ul> + * + * <h3>Configuration Example:</h3> + * <pre>{@code + * <BufferingAppender name="BufferedAppender" + * bufferAtVerbosity="DEBUG" + * maxBytes="20480" + * flushOnErrorLog="true"> + * <AppenderRef ref="ConsoleAppender"/> + * </BufferingAppender> + * }</pre> + * + * <h3>Configuration Parameters:</h3> + * <ul> + * <li><strong>bufferAtVerbosity:</strong> Log level to buffer (default: DEBUG). Logs at this level and below are buffered</li> + * <li><strong>maxBytes:</strong> Maximum buffer size in bytes per trace ID (default: 20480)</li> + * <li><strong>flushOnErrorLog:</strong> Whether to flush buffer on ERROR/FATAL logs (default: true)</li> + * </ul> + * + * <h3>Behavior:</h3> + * <ul> + * <li>During Lambda INIT phase (no trace ID): logs are output directly</li> + * <li>During Lambda execution (with trace ID): logs are buffered or output based on level</li> + * <li>When buffer overflows: oldest logs are discarded and a warning is logged</li> + * <li>On Lambda completion: buffer is auto-cleared when used with {@code @Logging} annotation</li> + * </ul> + * + * @see software.amazon.lambda.powertools.logging.PowertoolsLogging#flushBuffer() + */ +@Plugin(name = "BufferingAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) +public class BufferingAppender extends AbstractAppender implements BufferManager { + + private final AppenderRef[] appenderRefs; + private final Configuration configuration; + private final Level bufferAtVerbosity; + private final boolean flushOnErrorLog; + private final KeyBuffer<String, LogEvent> buffer; + + @SuppressWarnings("java:S107") // Constructor has too many parameters, which is OK for a Log4j2 plugin + protected BufferingAppender(String name, Filter filter, Layout<? extends Serializable> layout, + AppenderRef[] appenderRefs, Configuration configuration, Level bufferAtVerbosity, int maxBytes, + boolean flushOnErrorLog) { + super(name, filter, layout, false, null); + this.appenderRefs = appenderRefs; + this.configuration = configuration; + this.bufferAtVerbosity = bufferAtVerbosity; + this.flushOnErrorLog = flushOnErrorLog; + this.buffer = new KeyBuffer<>(maxBytes, event -> event.getMessage().getFormattedMessage().length(), + this::logOverflowWarning); + } + + @Override + public void append(LogEvent event) { + if (appenderRefs == null || appenderRefs.length == 0) { + return; + } + + LambdaHandlerProcessor.getXrayTraceId().ifPresentOrElse( + traceId -> { + if (shouldBuffer(event.getLevel())) { + bufferEvent(traceId, event); + } else { + callAppenders(event); + } + + // Flush buffer on error logs if configured + if (flushOnErrorLog && event.getLevel().isMoreSpecificThan(Level.WARN)) { + flushBuffer(traceId); + } + }, + () -> callAppenders(event) // No trace ID (INIT phase), log directly + ); + } + + private void callAppenders(LogEvent event) { + for (AppenderRef ref : appenderRefs) { + Appender appender = configuration.getAppender(ref.getRef()); + if (appender != null) { + appender.append(event); + } + } + } + + private boolean shouldBuffer(Level level) { + return level.isLessSpecificThan(bufferAtVerbosity) || level.equals(bufferAtVerbosity); + } + + private void bufferEvent(String traceId, LogEvent event) { + LogEvent immutableEvent = Log4jLogEvent.createMemento(event); + buffer.add(traceId, immutableEvent); + } + + public void clearBuffer() { + LambdaHandlerProcessor.getXrayTraceId().ifPresent(buffer::clear); + } + + public void flushBuffer() { + LambdaHandlerProcessor.getXrayTraceId().ifPresent(this::flushBuffer); + } + + private void flushBuffer(String traceId) { + Deque<LogEvent> events = buffer.removeAll(traceId); + if (events != null) { + events.forEach(this::callAppenders); + } + } + + @PluginFactory + @SuppressWarnings("java:S107") // Method has too many parameters, which is OK for a Log4j2 plugin factory + public static BufferingAppender createAppender( + @PluginAttribute("name") String name, + @PluginElement("Filter") Filter filter, + @PluginElement("Layout") Layout<? extends Serializable> layout, + @PluginElement("AppenderRef") AppenderRef[] appenderRefs, + @PluginConfiguration Configuration configuration, + @PluginAttribute(value = "bufferAtVerbosity", defaultString = "DEBUG") String bufferAtVerbosity, + @PluginAttribute(value = "maxBytes", defaultInt = 20480) int maxBytes, + @PluginAttribute(value = "flushOnErrorLog", defaultBoolean = true) boolean flushOnErrorLog) { + + if (name == null) { + LOGGER.error("No name provided for BufferingAppender"); + return null; + } + + Level level = Level.getLevel(bufferAtVerbosity); + if (level == null) { + level = Level.DEBUG; + } + + return new BufferingAppender(name, filter, layout, appenderRefs, configuration, level, maxBytes, + flushOnErrorLog); + } + + private void logOverflowWarning() { + // Create a properly formatted warning event and send directly to child appenders. Used to avoid circular + // dependency between KeyBuffer and BufferingAppender. + SimpleMessage message = new SimpleMessage( + "Some logs are not displayed because they were evicted from the buffer. Increase buffer size to store more logs in the buffer."); + LogEvent warningEvent = Log4jLogEvent.newBuilder() + .setLoggerName(BufferingAppender.class.getName()) + .setLevel(Level.WARN) + .setMessage(message) + .setTimeMillis(System.currentTimeMillis()) + .build(); + callAppenders(warningEvent); + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManager.java b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManager.java new file mode 100644 index 000000000..90bbe1d32 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManager.java @@ -0,0 +1,80 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.log4j.internal; + +import java.util.Collection; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configurator; +import org.slf4j.Logger; + +import software.amazon.lambda.powertools.logging.internal.BufferManager; +import software.amazon.lambda.powertools.logging.internal.LoggingManager; +import software.amazon.lambda.powertools.logging.log4j.BufferingAppender; + +/** + * LoggingManager for Log4j2 that provides log level management and buffer operations. + * Implements both {@link LoggingManager} and {@link BufferManager} interfaces. + */ +public class Log4jLoggingManager implements LoggingManager, BufferManager { + + /** + * @inheritDoc + */ + @Override + @SuppressWarnings("java:S4792") + public void setLogLevel(org.slf4j.event.Level logLevel) { + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + Configurator.setAllLevels(LogManager.getRootLogger().getName(), Level.getLevel(logLevel.toString())); + ctx.updateLoggers(); + } + + /** + * @inheritDoc + */ + @Override + public org.slf4j.event.Level getLogLevel(Logger logger) { + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + return org.slf4j.event.Level.valueOf(ctx.getLogger(logger.getName()).getLevel().toString()); + } + + /** + * @inheritDoc + */ + @Override + public void flushBuffer() { + getBufferingAppenders().forEach(BufferingAppender::flushBuffer); + } + + /** + * @inheritDoc + */ + @Override + public void clearBuffer() { + getBufferingAppenders().forEach(BufferingAppender::clearBuffer); + } + + private Collection<BufferingAppender> getBufferingAppenders() { + // Search all buffering appenders to avoid relying on the appender name given by the user + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + return ctx.getConfiguration().getAppenders().values().stream() + .filter(BufferingAppender.class::isInstance) + .map(BufferingAppender.class::cast) + .collect(Collectors.toList()); + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptor.java b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptor.java new file mode 100644 index 000000000..510ee858c --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.logging.log4j.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-logging-log4j module is on the classpath. + */ +public final class Log4jUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("logging-log4j"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json new file mode 100644 index 000000000..58b30f60e --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json @@ -0,0 +1,93 @@ +{ + "@timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + "timeZone": "UTC" + } + }, + "ecs.version": "1.2.0", + "log.level": { + "$resolver": "level", + "field": "name" + }, + "message": { + "$resolver": "message" + }, + "error.type": { + "$resolver": "exception", + "field": "className" + }, + "error.message": { + "$resolver": "exception", + "field": "message" + }, + "error.stack_trace": { + "$resolver": "exception", + "field": "stackTrace", + "stackTrace": { + "stringified": true + } + }, + "service.name": { + "$resolver": "powertools", + "field": "service" + }, + "service.version": { + "$resolver": "powertools", + "field": "function_version" + }, + "log.logger": { + "$resolver": "logger", + "field": "name" + }, + "process.thread.name": { + "$resolver": "thread", + "field": "name" + }, + "cloud.provider": "aws", + "cloud.service.name": "lambda", + "cloud.region": { + "$resolver": "powertools", + "field": "region" + }, + "cloud.account.id": { + "$resolver": "powertools", + "field": "account_id" + }, + "faas.id": { + "$resolver": "powertools", + "field": "function_arn" + }, + "faas.name": { + "$resolver": "powertools", + "field": "function_name" + }, + "faas.version": { + "$resolver": "powertools", + "field": "function_version" + }, + "faas.memory": { + "$resolver": "powertools", + "field": "function_memory_size" + }, + "faas.execution": { + "$resolver": "powertools", + "field": "function_request_id" + }, + "faas.coldstart": { + "$resolver": "powertools", + "field": "cold_start" + }, + "trace.id": { + "$resolver": "powertools", + "field": "xray_trace_id" + }, + "correlation.id": { + "$resolver": "powertools", + "field": "correlation_id" + }, + "": { + "$resolver": "powertools" + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json new file mode 100644 index 000000000..793006502 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json @@ -0,0 +1,75 @@ +{ + "level": { + "$resolver": "level", + "field": "name" + }, + "message": { + "$resolver": "message" + }, + "error": { + "message": { + "$resolver": "exception", + "field": "message" + }, + "name": { + "$resolver": "exception", + "field": "className" + }, + "stack": { + "$resolver": "exception", + "field": "stackTrace", + "stackTrace": { + "stringified": true + } + } + }, + "cold_start": { + "$resolver": "powertools", + "field": "cold_start" + }, + "function_arn": { + "$resolver": "powertools", + "field": "function_arn" + }, + "function_memory_size": { + "$resolver": "powertools", + "field": "function_memory_size" + }, + "function_name": { + "$resolver": "powertools", + "field": "function_name" + }, + "function_request_id": { + "$resolver": "powertools", + "field": "function_request_id" + }, + "function_version": { + "$resolver": "powertools", + "field": "function_version" + }, + "sampling_rate": { + "$resolver": "powertools", + "field": "sampling_rate" + }, + "service": { + "$resolver": "powertools", + "field": "service" + }, + "timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + } + }, + "xray_trace_id": { + "$resolver": "powertools", + "field": "xray_trace_id" + }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, + "": { + "$resolver": "powertools" + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/jni-config.json b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/jni-config.json new file mode 100644 index 000000000..c8b081385 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/jni-config.json @@ -0,0 +1,10 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/reflect-config.json b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/reflect-config.json new file mode 100644 index 000000000..43084dad2 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/reflect-config.json @@ -0,0 +1,1091 @@ +[ +{ + "name":"[Ljava.lang.Object;" +}, +{ + "name":"[Ljava.lang.String;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.Appender;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.config.AppenderRef;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.config.LoggerConfig;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.config.Property;" +}, +{ + "name":"[Lorg.apache.logging.log4j.layout.template.json.JsonTemplateLayout$EventTemplateAdditionalField;" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.Context" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$MessageAttribute", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getBinaryListValues","parameterTypes":[] }, {"name":"getBinaryValue","parameterTypes":[] }, {"name":"getDataType","parameterTypes":[] }, {"name":"getStringListValues","parameterTypes":[] }, {"name":"getStringValue","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getAttributes","parameterTypes":[] }, {"name":"getAwsRegion","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getEventSource","parameterTypes":[] }, {"name":"getEventSourceArn","parameterTypes":[] }, {"name":"getMd5OfBody","parameterTypes":[] }, {"name":"getMd5OfMessageAttributes","parameterTypes":[] }, {"name":"getMessageAttributes","parameterTypes":[] }, {"name":"getMessageId","parameterTypes":[] }, {"name":"getReceiptHandle","parameterTypes":[] }] +}, +{ + "name":"com.fasterxml.jackson.core.JsonParser" +}, +{ + "name":"com.fasterxml.jackson.databind.JsonNode" +}, +{ + "name":"com.fasterxml.jackson.databind.ObjectMapper" +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.fasterxml.jackson.dataformat.yaml.YAMLFactory" +}, +{ + "name":"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"jakarta.servlet.Servlet" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Cloneable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Iterable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Object", + "allDeclaredFields":true +}, +{ + "name":"java.lang.ProcessEnvironment", + "fields":[{"name":"theCaseInsensitiveEnvironment"}, {"name":"theEnvironment"}] +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.Thread", + "fields":[{"name":"threadLocalRandomProbe"}] +}, +{ + "name":"java.sql.Date" +}, +{ + "name":"java.sql.Time" +}, +{ + "name":"java.util.AbstractCollection", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.AbstractList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.AbstractMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.Arrays$ArrayList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.util.Collection", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.Collections$SingletonMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] +}, +{ + "name":"java.util.List", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.Map", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.RandomAccess", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.Striped64", + "fields":[{"name":"base"}, {"name":"cellsBusy"}] +}, +{ + "name":"java.util.function.Consumer", + "queryAllPublicMethods":true +}, +{ + "name":"javax.servlet.Servlet" +}, +{ + "name":"kotlin.Metadata" +}, +{ + "name":"org.apache.logging.log4j.core.appender.AbstractAppender$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.appender.AppenderSet" +}, +{ + "name":"org.apache.logging.log4j.core.appender.AsyncAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.ConsoleAppender", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.appender.ConsoleAppender$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.appender.CountingNoOpAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.FailoverAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.FailoversPlugin" +}, +{ + "name":"org.apache.logging.log4j.core.appender.FileAppender", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.appender.FileAppender$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.appender.HttpAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.MemoryMappedFileAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.NullAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.OutputStreamAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.RandomAccessFileAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.RollingFileAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.ScriptAppenderSelector" +}, +{ + "name":"org.apache.logging.log4j.core.appender.SmtpAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.SocketAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.SyslogAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.WriterAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.db.ColumnMapping" +}, +{ + "name":"org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig" +}, +{ + "name":"org.apache.logging.log4j.core.appender.db.jdbc.DataSourceConnectionSource" +}, +{ + "name":"org.apache.logging.log4j.core.appender.db.jdbc.DriverManagerConnectionSource" +}, +{ + "name":"org.apache.logging.log4j.core.appender.db.jdbc.FactoryMethodConnectionSource" +}, +{ + "name":"org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.mom.JmsAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.mom.jeromq.JeroMqAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.mom.kafka.KafkaAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.nosql.NoSqlAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rewrite.LoggerNameLevelRewritePolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rewrite.MapRewritePolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rewrite.PropertiesRewritePolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rewrite.RewriteAppender" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.CronTriggeringPolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.DirectWriteRolloverStrategy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.NoOpTriggeringPolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.OnStartupTriggeringPolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.DeleteAction" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAccumulatedFileCount" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAccumulatedFileSize" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAll" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAny" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.IfFileName" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.IfLastModified" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.IfNot" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.PathSortByModificationTime" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.PosixViewAttributeAction" +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.action.ScriptCondition" +}, +{ + "name":"org.apache.logging.log4j.core.appender.routing.IdlePurgePolicy" +}, +{ + "name":"org.apache.logging.log4j.core.appender.routing.Route" +}, +{ + "name":"org.apache.logging.log4j.core.appender.routing.Routes" +}, +{ + "name":"org.apache.logging.log4j.core.appender.routing.RoutingAppender" +}, +{ + "name":"org.apache.logging.log4j.core.async.ArrayBlockingQueueFactory" +}, +{ + "name":"org.apache.logging.log4j.core.async.AsyncLoggerConfig" +}, +{ + "name":"org.apache.logging.log4j.core.async.AsyncLoggerConfig$RootLogger" +}, +{ + "name":"org.apache.logging.log4j.core.async.AsyncWaitStrategyFactoryConfig" +}, +{ + "name":"org.apache.logging.log4j.core.async.DisruptorBlockingQueueFactory" +}, +{ + "name":"org.apache.logging.log4j.core.async.JCToolsBlockingQueueFactory" +}, +{ + "name":"org.apache.logging.log4j.core.async.LinkedTransferQueueFactory" +}, +{ + "name":"org.apache.logging.log4j.core.config.AppenderControlArraySet", + "fields":[{"name":"appenderArray"}] +}, +{ + "name":"org.apache.logging.log4j.core.config.AppenderRef", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createAppenderRef","parameterTypes":["java.lang.String","org.apache.logging.log4j.Level","org.apache.logging.log4j.core.Filter"] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.AppendersPlugin", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createAppenders","parameterTypes":["org.apache.logging.log4j.core.Appender[]"] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.CustomLevelConfig" +}, +{ + "name":"org.apache.logging.log4j.core.config.CustomLevels" +}, +{ + "name":"org.apache.logging.log4j.core.config.DefaultAdvertiser" +}, +{ + "name":"org.apache.logging.log4j.core.config.HttpWatcher" +}, +{ + "name":"org.apache.logging.log4j.core.config.LoggerConfig", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.LoggerConfig$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.config.LoggerConfig$RootLogger", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newRootBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.LoggerConfig$RootLogger$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.config.LoggersPlugin", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createLoggers","parameterTypes":["org.apache.logging.log4j.core.config.LoggerConfig[]"] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.MonitorResource" +}, +{ + "name":"org.apache.logging.log4j.core.config.MonitorResources" +}, +{ + "name":"org.apache.logging.log4j.core.config.PropertiesPlugin" +}, +{ + "name":"org.apache.logging.log4j.core.config.Property" +}, +{ + "name":"org.apache.logging.log4j.core.config.ScriptsPlugin" +}, +{ + "name":"org.apache.logging.log4j.core.config.arbiters.ClassArbiter" +}, +{ + "name":"org.apache.logging.log4j.core.config.arbiters.DefaultArbiter" +}, +{ + "name":"org.apache.logging.log4j.core.config.arbiters.EnvironmentArbiter" +}, +{ + "name":"org.apache.logging.log4j.core.config.arbiters.ScriptArbiter" +}, +{ + "name":"org.apache.logging.log4j.core.config.arbiters.SelectArbiter" +}, +{ + "name":"org.apache.logging.log4j.core.config.arbiters.SystemPropertyArbiter" +}, +{ + "name":"org.apache.logging.log4j.core.config.json.JsonConfigurationFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigDecimalConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigIntegerConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BooleanConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteArrayConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharArrayConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharacterConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharsetConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ClassConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CronExpressionConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DoubleConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DurationConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FileConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FloatConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$InetAddressConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$IntegerConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LongConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PathConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PatternConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$SecurityProviderConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ShortConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$StringConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UriConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UrlConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UuidConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.validation.validators.RequiredValidator", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginConfigurationVisitor", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.filter.AbstractFilterable$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.filter.BurstFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.CompositeFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.DenyAllFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.DynamicThresholdFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.LevelMatchFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.LevelRangeFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.MapFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.MarkerFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.MutableThreadContextMapFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.NoMarkerFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.RegexFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.ScriptFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.StringMatchFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.StructuredDataFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.ThreadContextMapFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.ThresholdFilter" +}, +{ + "name":"org.apache.logging.log4j.core.filter.TimeFilter" +}, +{ + "name":"org.apache.logging.log4j.core.layout.CsvLogEventLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.CsvParameterLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.GelfLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.HtmlLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.JsonLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.LevelPatternSelector" +}, +{ + "name":"org.apache.logging.log4j.core.layout.LoggerFields" +}, +{ + "name":"org.apache.logging.log4j.core.layout.MarkerPatternSelector" +}, +{ + "name":"org.apache.logging.log4j.core.layout.MessageLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.PatternLayout", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.layout.PatternLayout$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.layout.PatternMatch" +}, +{ + "name":"org.apache.logging.log4j.core.layout.Rfc5424Layout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.ScriptPatternSelector" +}, +{ + "name":"org.apache.logging.log4j.core.layout.SerializedLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.SyslogLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.XmlLayout" +}, +{ + "name":"org.apache.logging.log4j.core.layout.YamlLayout" +}, +{ + "name":"org.apache.logging.log4j.core.lookup.ContextMapLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.DateLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.EnvironmentLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.EventLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.JavaLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.JmxRuntimeInputArgumentsLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.JndiLookup" +}, +{ + "name":"org.apache.logging.log4j.core.lookup.Log4jLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.LowerLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.MainMapLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.MapLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.MarkerLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.ResourceBundleLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.StructuredDataLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.SystemPropertiesLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.lookup.UpperLookup", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.net.MulticastDnsAdvertiser" +}, +{ + "name":"org.apache.logging.log4j.core.net.SocketAddress" +}, +{ + "name":"org.apache.logging.log4j.core.net.SocketOptions" +}, +{ + "name":"org.apache.logging.log4j.core.net.SocketPerformancePreferences" +}, +{ + "name":"org.apache.logging.log4j.core.net.ssl.KeyStoreConfiguration" +}, +{ + "name":"org.apache.logging.log4j.core.net.ssl.SslConfiguration" +}, +{ + "name":"org.apache.logging.log4j.core.net.ssl.TrustStoreConfiguration" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Black" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Blue" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Cyan" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Green" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Magenta" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Red" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$White" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Yellow" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.ClassNamePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.DatePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.EncodingPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.EndOfBatchPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.EqualsIgnoreCaseReplacementConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.EqualsReplacementConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.ExtendedThrowablePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.FileDatePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.FileLocationPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.FullLocationPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.HighlightConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.IntegerPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.LevelPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.LineLocationPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.LineSeparatorPatternConverter", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"org.apache.logging.log4j.core.pattern.LoggerFqcnPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.LoggerPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.MapPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.MarkerPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.MarkerSimpleNamePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.MaxLengthConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.MdcPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.MessagePatternConverter", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["org.apache.logging.log4j.core.config.Configuration","java.lang.String[]"] }] +}, +{ + "name":"org.apache.logging.log4j.core.pattern.MethodLocationPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.NanoTimePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.NdcPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.ProcessIdPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.RegexReplacement" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.RegexReplacementConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.RelativeTimePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.RepeatPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.RootThrowablePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.SequenceNumberPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.StyleConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.ThreadIdPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.ThreadNamePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.ThreadPriorityPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.ThrowablePatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.UuidPatternConverter" +}, +{ + "name":"org.apache.logging.log4j.core.pattern.VariablesNotEmptyReplacementConverter" +}, +{ + "name":"org.apache.logging.log4j.core.script.Script" +}, +{ + "name":"org.apache.logging.log4j.core.script.ScriptFile" +}, +{ + "name":"org.apache.logging.log4j.core.script.ScriptRef" +}, +{ + "name":"org.apache.logging.log4j.core.util.KeyValuePair" +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.JsonTemplateLayout", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$EventTemplateAdditionalField" +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.CaseConverterResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.CounterResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.EndOfBatchResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.EventAdditionalFieldInterceptor", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.EventRootObjectKeyInterceptor", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.ExceptionResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.ExceptionRootCauseResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.LevelResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.LoggerResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.MainMapResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.MapResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.MarkerResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.MessageParameterResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.MessageResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.PatternResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.SourceResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.ThreadContextDataResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.ThreadContextStackResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.ThreadResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.TimestampResolverFactory", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getInstance","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.util.RecyclerFactoryConverter", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.jctools.queues.MpmcArrayQueue" +}, +{ + "name":"org.osgi.framework.FrameworkUtil" +}, +{ + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"isColdStart"}] +}, +{ + "name":"software.amazon.lambda.powertools.logging.log4j.BufferingAppender", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createAppender","parameterTypes":["java.lang.String","org.apache.logging.log4j.core.Filter","org.apache.logging.log4j.core.Layout","org.apache.logging.log4j.core.config.AppenderRef[]","org.apache.logging.log4j.core.config.Configuration","java.lang.String","int","boolean"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.log4j.internal.Log4jUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/resource-config.json b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/resource-config.json new file mode 100644 index 000000000..a4bcd55fa --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/resource-config.json @@ -0,0 +1,41 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QLambdaEcsLayout.json\\E" + }, { + "pattern":"\\QLambdaJsonLayout.json\\E" + }, { + "pattern":"\\QMETA-INF/log4j-provider.properties\\E" + }, { + "pattern":"\\QMETA-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat\\E" + }, { + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/javax.xml.parsers.DocumentBuilderFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.core.util.ContextDataProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.core.util.WatchEventService\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.spi.Provider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.util.PropertySource\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.configuration.Configuration\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.presentation.Representation\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager\\E" + }, { + "pattern":"\\QStackTraceElementLayout.json\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }]}, + "bundles":[] +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager new file mode 100644 index 000000000..d4b2a72a0 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager @@ -0,0 +1 @@ +software.amazon.lambda.powertools.logging.log4j.internal.Log4jLoggingManager diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-logging/powertools-logging-log4j/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..22150f4b7 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.logging.log4j.internal.Log4jUserAgentInterceptor diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowerToolsResolverFactoryTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowerToolsResolverFactoryTest.java new file mode 100644 index 000000000..46b5b65d4 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowerToolsResolverFactoryTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.logging.log4j.layout.template.json.resolver; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.logging.PowertoolsLogging; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; + +@Order(1) +class PowerToolsResolverFactoryTest { + + private Context context; + + @BeforeEach + void setUp() throws IllegalAccessException, IOException { + MDC.clear(); + // Reset cold start state + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); + writeStaticField(PowertoolsLogging.class, "hasBeenInitialized", new AtomicBoolean(false), true); + + context = new TestLambdaContext(); + // Make sure file is cleaned up before running tests + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // file may not exist on the first launch + } + } + + @AfterEach + void cleanUp() throws IOException { + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // file may not exist on the first launch + } + } + + @Test + void shouldLogInJsonFormat() { + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); + handler.handleRequest("Input", context); + + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains( + "{\"level\":\"DEBUG\",\"message\":\"Test debug event\",\"cold_start\":true,\"function_arn\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"function_memory_size\":128,\"function_name\":\"test-function\",\"function_request_id\":\"test-request-id\",\"function_version\":\"1\",\"service\":\"testLog4j\",\"timestamp\":") + .contains("\"xray_trace_id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\"}\n"); + } + + @Test + void shouldLogInEcsFormat() { + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); + handler.handleRequest("Input", context); + + File logFile = new File("target/ecslogfile.json"); + assertThat(contentOf(logFile)).contains( + "\"ecs.version\":\"1.2.0\",\"log.level\":\"DEBUG\",\"message\":\"Test debug event\",\"service.name\":\"testLog4j\",\"service.version\":\"1\",\"log.logger\":\"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled\",\"process.thread.name\":\"main\",\"cloud.provider\":\"aws\",\"cloud.service.name\":\"lambda\",\"cloud.region\":\"eu-central-1\",\"cloud.account.id\":\"123456789012\",\"faas.id\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"faas.name\":\"test-function\",\"faas.version\":\"1\",\"faas.memory\":128,\"faas.execution\":\"test-request-id\",\"faas.coldstart\":true,\"trace.id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\"}\n"); + } + +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java new file mode 100644 index 000000000..573eaddbf --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java @@ -0,0 +1,138 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.logging.log4j.layout.template.json.resolver; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; + +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments; + +@Order(2) +class PowertoolsResolverArgumentsTest { + + private Context context; + + @BeforeEach + void setUp() throws IOException { + MDC.clear(); + context = new TestLambdaContext(); + + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @AfterEach + void cleanUp() throws IOException { + try { + // Make sure file is cleaned up before running full stack logging regression + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @Test + void shouldLogArgumentsAsJsonWhenUsingRawJson() { + // GIVEN + PowertoolsArguments requestHandler = new PowertoolsArguments(PowertoolsArguments.ArgumentFormat.JSON); + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("us-east-1"); + SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); + attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); + msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); + + // WHEN + requestHandler.handleRequest(msg, context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains( + "\"input\":{\"awsRegion\":\"us-east-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains("\"message\":\"1212abcd\"") + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); + // Reserved keys should be ignored + PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { + assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); + assertThat(contentOf(logFile)).contains( + "\"message\":\"Attempted to use reserved key '" + reservedKey + + "' in structured argument. This key will be ignored.\""); + }); + } + + @Test + void shouldLogArgumentsAsJsonWhenUsingKeyValue() { + // GIVEN + PowertoolsArguments requestHandler = new PowertoolsArguments(PowertoolsArguments.ArgumentFormat.ENTRY); + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("us-east-1"); + SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); + attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); + msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); + + // WHEN + requestHandler.handleRequest(msg, context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains( + "\"input\":{\"awsRegion\":\"us-east-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains("\"message\":\"1212abcd\"") + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); + + // Reserved keys should be ignored + PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { + assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); + assertThat(contentOf(logFile)).contains( + "\"message\":\"Attempted to use reserved key '" + reservedKey + + "' in structured argument. This key will be ignored.\""); + }); + } + +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverTest.java new file mode 100644 index 000000000..d1b9fec83 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.logging.log4j.layout.template.json.resolver; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SAMPLING_RATE; + +import java.util.HashMap; +import java.util.Map; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.layout.template.json.util.JsonWriter; +import org.apache.logging.log4j.util.SortedArrayStringMap; +import org.apache.logging.log4j.util.StringMap; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; + +class PowertoolsResolverTest { + + @ParameterizedTest + @EnumSource(value = PowertoolsLoggedFields.class, + mode = EnumSource.Mode.EXCLUDE, + names = {"FUNCTION_MEMORY_SIZE", "SAMPLING_RATE", "FUNCTION_COLD_START", "CORRELATION_ID"}) + void shouldResolveFunctionStringInfo(PowertoolsLoggedFields field) { + String result = resolveField(field.getName(), "value"); + assertThat(result).isEqualTo("\"value\""); + } + + @Test + void shouldResolveMemorySize() { + String result = resolveField(FUNCTION_MEMORY_SIZE.getName(), "42"); + assertThat(result).isEqualTo("42"); + } + + @Test + void shouldResolveSamplingRate() { + String result = resolveField(SAMPLING_RATE.getName(), "0.4"); + assertThat(result).isEqualTo("0.4"); + } + + @Test + void shouldResolveColdStart() { + String result = resolveField(FUNCTION_COLD_START.getName(), "true"); + assertThat(result).isEqualTo("true"); + } + + @Test + void shouldResolveAccountId() { + String result = resolveField(FUNCTION_ARN.getName(), "account_id", "arn:aws:lambda:us-east-2:123456789012:function:my-function"); + assertThat(result).isEqualTo("\"123456789012\""); + } + + @Test + void unknownField_shouldThrowException() { + assertThatThrownBy(() -> resolveField("custom-random-unknown-field", "custom-random-unknown-field", "Once apon a time in Switzerland...")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unknown field: custom-random-unknown-field"); + } + + @Test + @SetEnvironmentVariable(key = "AWS_REGION", value = "eu-central-2") + void shouldResolveRegion() { + String result = resolveField("region", "dummy, will use the env var"); + assertThat(result).isEqualTo("\"eu-central-2\""); + } + + private static String resolveField(String field, String value) { + return resolveField(field, field, value); + } + + private static String resolveField(String data, String field, String value) { + Map<String, Object> configMap = new HashMap<>(); + configMap.put("field", field); + + TemplateResolverConfig config = new TemplateResolverConfig(configMap); + PowertoolsResolver resolver = new PowertoolsResolver(config); + JsonWriter writer = JsonWriter + .newBuilder() + .setMaxStringLength(1000) + .setTruncatedStringSuffix("") + .build(); + + StringMap contextMap = new SortedArrayStringMap(); + contextMap.putValue(data, value); + + Log4jLogEvent logEvent = Log4jLogEvent.newBuilder().setContextData(contextMap).build(); + if (resolver.isResolvable(logEvent)) { + resolver.resolve(logEvent, writer); + } + + return writer.getStringBuilder().toString(); + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java new file mode 100644 index 000000000..0d95f29fa --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java @@ -0,0 +1,82 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal.handler; + +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.fasterxml.jackson.core.JsonProcessingException; + +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.argument.StructuredArguments; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +public class PowertoolsArguments implements RequestHandler<SQSEvent.SQSMessage, String> { + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsArguments.class); + private final ArgumentFormat argumentFormat; + + public PowertoolsArguments(ArgumentFormat argumentFormat) { + this.argumentFormat = argumentFormat; + } + + @Override + @Logging(clearState = true) + public String handleRequest(SQSEvent.SQSMessage input, Context context) { + try { + MDC.put(CORRELATION_ID.getName(), input.getMessageId()); + if (argumentFormat == ArgumentFormat.JSON) { + LOG.debug("SQS Event", + StructuredArguments.json("input", + JsonConfig.get().getObjectMapper().writeValueAsString(input)), + // function_name is a reserved key by PowertoolsLoggedFields and should be omitted + StructuredArguments.entry("function_name", "shouldBeIgnored")); + } else { + LOG.debug("SQS Event", + StructuredArguments.entry("input", input), + // function_name is a reserved key by PowertoolsLoggedFields and should be omitted + StructuredArguments.entry("function_name", "shouldBeIgnored")); + } + + // Attempt logging all reserved keys, the values should not be overwritten by "shouldBeIgnored" + final Map<String, String> reservedKeysMap = new HashMap<>(); + for (String field : PowertoolsLoggedFields.stringValues()) { + reservedKeysMap.put(field, "shouldBeIgnored"); + } + reservedKeysMap.put("message", "shouldBeIgnored"); + reservedKeysMap.put("level", "shouldBeIgnored"); + reservedKeysMap.put("timestamp", "shouldBeIgnored"); + LOG.debug("Reserved keys", StructuredArguments.entries(reservedKeysMap)); + LOG.debug("{}", input.getMessageId()); + LOG.warn("Message body = {} and id = \"{}\"", input.getBody(), input.getMessageId()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + return input.getMessageId(); + } + + public enum ArgumentFormat { + JSON, ENTRY + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java new file mode 100644 index 000000000..0ee7f14fa --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal.handler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); + + @Override + @Logging(clearState = true) + public Object handleRequest(Object input, Context context) { + MDC.put("myKey", "myValue"); + LOG.debug("Test debug event"); + return "Bonjour le monde"; + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppenderTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppenderTest.java new file mode 100644 index 000000000..a0fac8d7a --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppenderTest.java @@ -0,0 +1,145 @@ +package software.amazon.lambda.powertools.logging.log4j; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LoggerContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.ClearEnvironmentVariable; + +class BufferingAppenderTest { + + private Logger logger; + + @BeforeEach + void setUp() throws IOException { + logger = LogManager.getLogger(BufferingAppenderTest.class); + + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @AfterEach + void cleanUp() throws IOException { + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @Test + void shouldBufferDebugLogsAndFlushOnError() { + // When - log debug messages (should be buffered) + logger.debug("Debug message 1"); + logger.debug("Debug message 2"); + + // Then - no logs written yet + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).isEmpty(); + + // When - log error (should flush buffer) + logger.error("Error message"); + + // Then - all logs written + assertThat(contentOf(logFile)) + .contains("Debug message 1") + .contains("Debug message 2") + .contains("Error message"); + } + + @Test + @ClearEnvironmentVariable(key = "_X_AMZN_TRACE_ID") + void shouldLogDirectlyWhenNoTraceId() { + // When + logger.debug("Debug without trace"); + + // Then - log written directly + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Debug without trace"); + } + + @Test + void shouldNotBufferInfoLogs() { + // When - log info message (above buffer level) + logger.info("Info message"); + + // Then - log written directly + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Info message"); + } + + @Test + void shouldFlushBufferManually() { + // When - buffer debug logs + logger.debug("Buffered message"); + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).isEmpty(); + + // When - manual flush + BufferingAppender appender = getBufferingAppender(); + appender.flushBuffer(); + + // Then - logs written + assertThat(contentOf(logFile)).contains("Buffered message"); + } + + @Test + void shouldClearBufferManually() { + // When - buffer debug logs then clear + logger.debug("Buffered message"); + BufferingAppender appender = getBufferingAppender(); + appender.clearBuffer(); + + // When - log error (should not flush cleared buffer) + logger.error("Error after clear"); + + // Then - only error logged + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("Error after clear") + .doesNotContain("Buffered message"); + } + + @Test + void shouldLogOverflowWarningWhenBufferOverflows() { + // When - fill buffer beyond capacity to trigger overflow + for (int i = 0; i < 100; i++) { + logger.debug("Debug message {}", i); + } + + // When - flush buffer to trigger overflow warning + BufferingAppender appender = getBufferingAppender(); + appender.flushBuffer(); + + // Then - overflow warning should be logged + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("Some logs are not displayed because they were evicted from the buffer"); + } + + private BufferingAppender getBufferingAppender() { + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Appender appender = context.getConfiguration().getAppender("BufferingAppender"); + if (appender == null) { + throw new IllegalStateException("BufferingAppender not found in configuration. Available appenders: " + + context.getConfiguration().getAppenders().keySet()); + } + return (BufferingAppender) appender; + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManagerTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManagerTest.java new file mode 100644 index 000000000..16036fe3e --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManagerTest.java @@ -0,0 +1,118 @@ +package software.amazon.lambda.powertools.logging.log4j.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; +import static org.slf4j.event.Level.DEBUG; +import static org.slf4j.event.Level.ERROR; +import static org.slf4j.event.Level.WARN; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +class Log4jLoggingManagerTest { + + private static final Logger LOG = LoggerFactory.getLogger(Log4jLoggingManagerTest.class); + private static final Logger ROOT = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + + @BeforeEach + void setUp() { + // Force reconfiguration from XML to ensure clean state + Configurator.reconfigure(); + } + + @Test + void getLogLevel_shouldReturnConfiguredLogLevel() { + // Given log4j2.xml in resources + + // When + Log4jLoggingManager manager = new Log4jLoggingManager(); + Level logLevel = manager.getLogLevel(LOG); + Level rootLevel = manager.getLogLevel(ROOT); + + // Then + assertThat(logLevel).isEqualTo(DEBUG); + assertThat(rootLevel).isEqualTo(WARN); + } + + @Test + void resetLogLevel() { + // Given log4j2.xml in resources + + // When + Log4jLoggingManager manager = new Log4jLoggingManager(); + manager.setLogLevel(ERROR); + + Level rootLevel = manager.getLogLevel(ROOT); + Level logLevel = manager.getLogLevel(LOG); + + // Then + assertThat(rootLevel).isEqualTo(ERROR); + assertThat(logLevel).isEqualTo(ERROR); + } + + @Test + void shouldDetectMultipleBufferingAppendersRegardlessOfName() throws IOException { + // Given - configuration with multiple BufferingAppenders with different names + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + ConfigurationFactory factory = new XmlConfigurationFactory(); + ConfigurationSource source = new ConfigurationSource( + getClass().getResourceAsStream("/log4j2-multiple-buffering.xml")); + Configuration config = factory.getConfiguration(null, source); + + ctx.setConfiguration(config); + ctx.updateLoggers(); + + org.apache.logging.log4j.Logger logger = LogManager.getLogger("test.multiple.appenders"); + + // When - log messages and flush buffers + logger.debug("Test message 1"); + logger.debug("Test message 2"); + + Log4jLoggingManager manager = new Log4jLoggingManager(); + manager.flushBuffer(); + + // Then - both appenders should have flushed their buffers + File logFile = new File("target/logfile.json"); + assertThat(logFile).exists(); + String content = contentOf(logFile); + // Each message should appear twice (once from each BufferingAppender) + assertThat(content.split("Test message 1", -1)).hasSize(3); // 2 occurrences = 3 parts + assertThat(content.split("Test message 2", -1)).hasSize(3); // 2 occurrences = 3 parts + } + + @AfterEach + void cleanUp() throws IOException { + // Reset to original configuration from XML + Configurator.reconfigure(); + + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there + } + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptorTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptorTest.java new file mode 100644 index 000000000..77d5b06e3 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.logging.log4j.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class Log4jUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/LOGGING-LOG4J/"); + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/resources/junit-platform.properties b/powertools-logging/powertools-logging-log4j/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..80a2481d7 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/resources/junit-platform.properties @@ -0,0 +1,17 @@ +# +# Copyright 2023 Amazon.com, Inc. or its affiliates. +# Licensed under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +# because of LambdaLoggingAspect static initialization of the LoggingManager, we need to +# set an order in the unit tests, especially LambdaLoggingAspectTest needs to be first +junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$OrderAnnotation \ No newline at end of file diff --git a/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2-multiple-buffering.xml b/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2-multiple-buffering.xml new file mode 100644 index 000000000..53995e4f5 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2-multiple-buffering.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <File name="testFile" fileName="target/logfile.json"> + <PatternLayout pattern="%msg%n" /> + </File> + <BufferingAppender name="FirstBufferingAppender" + bufferAtVerbosity="DEBUG" + maxBytes="1024" + flushOnErrorLog="true"> + <AppenderRef ref="testFile" /> + </BufferingAppender> + <BufferingAppender name="SecondBufferingAppender" + bufferAtVerbosity="DEBUG" + maxBytes="1024" + flushOnErrorLog="true"> + <AppenderRef ref="testFile" /> + </BufferingAppender> + </Appenders> + <Loggers> + <Logger name="test.multiple.appenders" level="DEBUG" additivity="false"> + <AppenderRef ref="FirstBufferingAppender" /> + <AppenderRef ref="SecondBufferingAppender" /> + </Logger> + </Loggers> +</Configuration> diff --git a/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml b/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml new file mode 100644 index 000000000..870e3a803 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="console" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <File name="logFile" fileName="target/logfile.json"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </File> + <File name="logFileWithEcs" fileName="target/ecslogfile.json"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaEcsLayout.json" /> + </File> + <BufferingAppender name="BufferingAppender" + bufferAtVerbosity="DEBUG" + maxBytes="1024" + flushOnErrorLog="true"> + <AppenderRef ref="logFile"/> + </BufferingAppender> + </Appenders> + <Loggers> + <Logger name="software.amazon.lambda.powertools.logging.log4j.BufferingAppenderTest" level="DEBUG" additivity="false"> + <AppenderRef ref="BufferingAppender"/> + </Logger> + <Logger name="software.amazon.lambda.powertools" level="DEBUG" additivity="false"> + <AppenderRef ref="logFileWithEcs"/> + <AppenderRef ref="logFile"/> + </Logger> + <Root level="WARN"> + <AppenderRef ref="logFileWithEcs"/> + <AppenderRef ref="logFile"/> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml new file mode 100644 index 000000000..dbf7f5207 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -0,0 +1,216 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>powertools-parent</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>2.9.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>powertools-logging-logback</artifactId> + <name>Powertools for AWS Lambda (Java) - Logging with LogBack</name> + <description>Set of utility for better logging with logback</description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.5.18</version> + <exclusions> + <exclusion> + <groupId>com.sun.mail</groupId> + <artifactId>javax.mail</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-tests</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.skyscreamer</groupId> + <artifactId>jsonassert</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-logging-logback</imageName> + <buildArgs> + <buildArg> + --initialize-at-build-time=org.junit.platform.launcher.core.DiscoveryIssueNotifier$1 + </buildArg> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <resources> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <complianceLevel>${maven.compiler.target}</complianceLevel> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <POWERTOOLS_SERVICE_NAME>testLogback</POWERTOOLS_SERVICE_NAME> + <AWS_REGION>eu-central-1</AWS_REGION> + <_X_AMZN_TRACE_ID>Root=1-63441c4a-abcdef012345678912345678</_X_AMZN_TRACE_ID> + <AWS_LAMBDA_INITIALIZATION_TYPE>on-demand</AWS_LAMBDA_INITIALIZATION_TYPE> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/BufferingAppender.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/BufferingAppender.java new file mode 100644 index 000000000..8f323ff46 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/BufferingAppender.java @@ -0,0 +1,196 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.logback; + +import java.util.Deque; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.spi.AppenderAttachable; +import ch.qos.logback.core.spi.AppenderAttachableImpl; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.internal.BufferManager; +import software.amazon.lambda.powertools.logging.internal.KeyBuffer; + +/** + * A Logback appender that buffers log events by AWS X-Ray trace ID for optimized Lambda logging. + * + * <p>This appender is designed specifically for AWS Lambda functions to reduce log ingestion + * by buffering lower-level logs and only outputting them when errors occur, preserving + * full context for troubleshooting while minimizing routine log volume. + * + * <h3>Key Features:</h3> + * <ul> + * <li><strong>Trace-based buffering:</strong> Groups logs by AWS X-Ray trace ID</li> + * <li><strong>Selective output:</strong> Only buffers logs at or below configured verbosity level</li> + * <li><strong>Auto-flush on errors:</strong> Automatically outputs buffered logs when ERROR/FATAL events occur</li> + * <li><strong>Memory management:</strong> Prevents memory leaks with configurable buffer size limits</li> + * <li><strong>Overflow protection:</strong> Warns when logs are discarded due to buffer limits</li> + * </ul> + * + * <h3>Configuration Example:</h3> + * <pre>{@code + * <appender name="BufferedAppender" class="software.amazon.lambda.powertools.logging.logback.BufferingAppender"> + * <bufferAtVerbosity>DEBUG</bufferAtVerbosity> + * <maxBytes>20480</maxBytes> + * <flushOnErrorLog>true</flushOnErrorLog> + * <appender-ref ref="ConsoleAppender"/> + * </appender> + * }</pre> + * + * <h3>Configuration Parameters:</h3> + * <ul> + * <li><strong>bufferAtVerbosity:</strong> Log level to buffer (default: DEBUG). Logs at this level and below are buffered</li> + * <li><strong>maxBytes:</strong> Maximum buffer size in bytes per trace ID (default: 20480)</li> + * <li><strong>flushOnErrorLog:</strong> Whether to flush buffer on ERROR/FATAL logs (default: true)</li> + * </ul> + * + * <h3>Behavior:</h3> + * <ul> + * <li>During Lambda INIT phase (no trace ID): logs are output directly</li> + * <li>During Lambda execution (with trace ID): logs are buffered or output based on level</li> + * <li>When buffer overflows: oldest logs are discarded and a warning is logged</li> + * <li>On Lambda completion: buffer is auto-cleared when used with {@code @Logging} annotation</li> + * </ul> + * + * @see software.amazon.lambda.powertools.logging.PowertoolsLogging#flushBuffer() + */ +public class BufferingAppender extends AppenderBase<ILoggingEvent> + implements AppenderAttachable<ILoggingEvent>, BufferManager { + + private static final int DEFAULT_BUFFER_SIZE = 20480; + + private final AppenderAttachableImpl<ILoggingEvent> aai = new AppenderAttachableImpl<>(); + private Level bufferAtVerbosity = Level.DEBUG; + private boolean flushOnErrorLog = true; + private int maxBytes = DEFAULT_BUFFER_SIZE; + private KeyBuffer<String, ILoggingEvent> buffer; + + @Override + public void start() { + // Initialize lazily to ensure configuration properties are set first. + if (buffer == null) { + buffer = new KeyBuffer<>(maxBytes, event -> event.getFormattedMessage().length(), this::logOverflowWarning); + } + super.start(); + } + + @Override + protected void append(ILoggingEvent event) { + LambdaHandlerProcessor.getXrayTraceId().ifPresentOrElse( + traceId -> { + if (shouldBuffer(event.getLevel())) { + buffer.add(traceId, event); + } else { + aai.appendLoopOnAppenders(event); + } + + // Flush buffer on error logs if configured + if (flushOnErrorLog && event.getLevel().isGreaterOrEqual(Level.ERROR)) { + flushBuffer(traceId); + } + }, + () -> aai.appendLoopOnAppenders(event) // No trace ID (INIT phase), log directly + ); + } + + private boolean shouldBuffer(Level level) { + return level.levelInt <= bufferAtVerbosity.levelInt; + } + + public void clearBuffer() { + LambdaHandlerProcessor.getXrayTraceId().ifPresent(buffer::clear); + } + + public void flushBuffer() { + LambdaHandlerProcessor.getXrayTraceId().ifPresent(this::flushBuffer); + } + + private void flushBuffer(String traceId) { + Deque<ILoggingEvent> events = buffer.removeAll(traceId); + if (events != null) { + events.forEach(aai::appendLoopOnAppenders); + } + } + + // Configuration setters. These will be inspected as JavaBean properties by Logback + // when configuring the appender via XML or programmatically. They run before start(). + public void setBufferAtVerbosity(String level) { + this.bufferAtVerbosity = Level.toLevel(level, Level.DEBUG); + } + + public void setMaxBytes(int maxBytes) { + this.maxBytes = maxBytes; + } + + public void setFlushOnErrorLog(boolean flushOnErrorLog) { + this.flushOnErrorLog = flushOnErrorLog; + } + + // AppenderAttachable implementation. We simply delegate to the internal logback AppenderAttachableImpl. This is + // needed to be able to attach other appenders to this appender so that customers can wrap existing appenders with + // this buffering appender. + @Override + public void addAppender(Appender<ILoggingEvent> newAppender) { + aai.addAppender(newAppender); + } + + @Override + public java.util.Iterator<Appender<ILoggingEvent>> iteratorForAppenders() { + return aai.iteratorForAppenders(); + } + + @Override + public Appender<ILoggingEvent> getAppender(String name) { + return aai.getAppender(name); + } + + @Override + public boolean isAttached(Appender<ILoggingEvent> appender) { + return aai.isAttached(appender); + } + + @Override + public void detachAndStopAllAppenders() { + aai.detachAndStopAllAppenders(); + } + + @Override + public boolean detachAppender(Appender<ILoggingEvent> appender) { + return aai.detachAppender(appender); + } + + @Override + public boolean detachAppender(String name) { + return aai.detachAppender(name); + } + + private void logOverflowWarning() { + // Create a properly formatted warning event and send directly to child appenders. Used to avoid circular + // dependency between KeyBuffer and BufferingAppender. + Logger logbackLogger = (Logger) org.slf4j.LoggerFactory + .getLogger(BufferingAppender.class); + LoggingEvent warningEvent = new LoggingEvent( + BufferingAppender.class.getName(), logbackLogger, Level.WARN, + "Some logs are not displayed because they were evicted from the buffer. Increase buffer size to store more logs in the buffer.", + null, null); + warningEvent.setTimeStamp(System.currentTimeMillis()); + aai.appendLoopOnAppenders(warningEvent); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java new file mode 100644 index 000000000..67d6b268d --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java @@ -0,0 +1,140 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.logback; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; +import java.util.TreeMap; + +import software.amazon.lambda.powertools.logging.argument.StructuredArgument; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; + +/** + * Json tools to serialize common fields + */ +final class JsonUtils { + + private JsonUtils() { + // static utils + } + + static void serializeTimestamp(JsonSerializer generator, long timestamp, String timestampFormat, + String timestampFormatTimezoneId, String timestampAttributeName) { + String formattedTimestamp; + if (timestampFormat == null || timestamp < 0) { + formattedTimestamp = String.valueOf(timestamp); + } else { + Date date = new Date(timestamp); + DateFormat format = new SimpleDateFormat(timestampFormat); + + if (timestampFormatTimezoneId != null) { + TimeZone tz = TimeZone.getTimeZone(timestampFormatTimezoneId); + format.setTimeZone(tz); + } + formattedTimestamp = format.format(date); + } + generator.writeStringField(timestampAttributeName, formattedTimestamp); + } + + static void serializeMDCEntries(Map<String, String> mdcPropertyMap, JsonSerializer serializer) { + TreeMap<String, String> sortedMap = new TreeMap<>(mdcPropertyMap); + for (Map.Entry<String, String> entry : sortedMap.entrySet()) { + if (!PowertoolsLoggedFields.stringValues().contains(entry.getKey())) { + serializeMDCEntry(entry, serializer); + } + } + } + + static void serializeMDCEntry(Map.Entry<String, String> entry, JsonSerializer serializer) { + serializer.writeRaw(','); + serializer.writeFieldName(entry.getKey()); + if (isString(entry.getValue())) { + serializer.writeString(entry.getValue()); + } else { + serializer.writeRaw(entry.getValue()); + } + } + + static void serializeArguments(ILoggingEvent event, JsonSerializer serializer) throws IOException { + Object[] arguments = event.getArgumentArray(); + if (arguments != null) { + for (Object argument : arguments) { + if (argument instanceof StructuredArgument) { + serializer.writeRaw(','); + ((StructuredArgument) argument).writeTo(serializer); + } + } + } + } + + /** + * As MDC is a {@code Map<String, String>}, we need to check the type + * to output numbers and booleans correctly (without quotes) + */ + private static boolean isString(String str) { + if (str == null) { + return true; + } + if ("true".equals(str) || "false".equals(str)) { + return false; // boolean + } + return !isNumeric(str); // number + } + + /** + * Taken from commons-lang3 NumberUtils to avoid include the library + */ + private static boolean isNumeric(final String str) { + if (str == null || str.isEmpty()) { + return false; + } + if (str.charAt(str.length() - 1) == '.') { + return false; + } + if (str.charAt(0) == '-') { + if (str.length() == 1) { + return false; + } + return withDecimalsParsing(str, 1); + } + return withDecimalsParsing(str, 0); + } + + /** + * Taken from commons-lang3 NumberUtils + */ + private static boolean withDecimalsParsing(final String str, final int beginIdx) { + int decimalPoints = 0; + for (int i = beginIdx; i < str.length(); i++) { + final boolean isDecimalPoint = str.charAt(i) == '.'; + if (isDecimalPoint) { + decimalPoints++; + } + if (decimalPoints > 1) { + return false; + } + if (!isDecimalPoint && !Character.isDigit(str.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java new file mode 100644 index 000000000..6a82d8e67 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java @@ -0,0 +1,289 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.logback; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_NAME; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_REQUEST_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_TRACE_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_VERSION; +import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeArguments; +import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntries; +import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeTimestamp; + +import ch.qos.logback.classic.pattern.ThrowableHandlingConverter; +import ch.qos.logback.classic.pattern.ThrowableProxyConverter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.ThrowableProxy; +import ch.qos.logback.core.encoder.EncoderBase; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + + +/** + * This class will encode the logback event into the format expected by the Elastic Common Schema (ECS) service (for Elasticsearch). + * <br/> + * Inspired from <code>co.elastic.logging.logback.EcsEncoder</code>, this class doesn't use + * any JSON (de)serialization library (Jackson, Gson, etc.) or Elastic library to avoid the dependency. + * <br/> + * This encoder also adds cloud information (see <a href="https://www.elastic.co/guide/en/ecs/current/ecs-cloud.html">doc</a>) + * and Lambda function information (see <a href="https://www.elastic.co/guide/en/ecs/current/ecs-faas.html">doc</a>, currently in beta). + */ +public class LambdaEcsEncoder extends EncoderBase<ILoggingEvent> { + + protected static final String TIMESTAMP_ATTR_NAME = "@timestamp"; + protected static final String ECS_VERSION_ATTR_NAME = "ecs.version"; + protected static final String LOGGER_ATTR_NAME = "log.logger"; + protected static final String LEVEL_ATTR_NAME = "log.level"; + protected static final String SERVICE_NAME_ATTR_NAME = "service.name"; + protected static final String SERVICE_VERSION_ATTR_NAME = "service.version"; + protected static final String FORMATTED_MESSAGE_ATTR_NAME = "message"; + protected static final String THREAD_ATTR_NAME = "process.thread.name"; + protected static final String EXCEPTION_MSG_ATTR_NAME = "error.message"; + protected static final String EXCEPTION_CLASS_ATTR_NAME = "error.type"; + protected static final String EXCEPTION_STACK_ATTR_NAME = "error.stack_trace"; + protected static final String CLOUD_PROVIDER_ATTR_NAME = "cloud.provider"; + protected static final String CLOUD_REGION_ATTR_NAME = "cloud.region"; + protected static final String CLOUD_ACCOUNT_ATTR_NAME = "cloud.account.id"; + protected static final String CLOUD_SERVICE_ATTR_NAME = "cloud.service.name"; + protected static final String FUNCTION_COLD_START_ATTR_NAME = "faas.coldstart"; + protected static final String FUNCTION_REQUEST_ID_ATTR_NAME = "faas.execution"; + protected static final String FUNCTION_ARN_ATTR_NAME = "faas.id"; + protected static final String FUNCTION_NAME_ATTR_NAME = "faas.name"; + protected static final String FUNCTION_VERSION_ATTR_NAME = "faas.version"; + protected static final String FUNCTION_MEMORY_ATTR_NAME = "faas.memory"; + protected static final String FUNCTION_TRACE_ID_ATTR_NAME = "trace.id"; + protected static final String CORRELATION_ID_ATTR_NAME = "correlation.id"; + + protected static final String ECS_VERSION = "1.2.0"; + protected static final String CLOUD_PROVIDER = "aws"; + protected static final String CLOUD_SERVICE = "lambda"; + + private final ThrowableProxyConverter throwableProxyConverter = new ThrowableProxyConverter(); + protected ThrowableHandlingConverter throwableConverter = null; + private boolean includeCloudInfo = true; + private boolean includeFaasInfo = true; + + @Override + public byte[] headerBytes() { + return new byte[0]; + } + + /** + * Main method of the encoder. Encode a logging event into Json format (with Elastic Search fields) + * + * @param event the logging event + * @return the encoded bytes + */ + + @SuppressWarnings("java:S106") + @Override + public byte[] encode(ILoggingEvent event) { + final Map<String, String> mdcPropertyMap = event.getMDCPropertyMap(); + + StringBuilder builder = new StringBuilder(); + try (JsonSerializer serializer = new JsonSerializer(builder)) { + serializer.writeStartObject(); + serializeTimestamp(serializer, event.getTimeStamp(), "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + "UTC", TIMESTAMP_ATTR_NAME); + serializer.writeRaw(','); + serializer.writeStringField(ECS_VERSION_ATTR_NAME, ECS_VERSION); + serializer.writeRaw(','); + serializer.writeStringField(LEVEL_ATTR_NAME, event.getLevel().toString()); + serializer.writeRaw(','); + serializer.writeStringField(FORMATTED_MESSAGE_ATTR_NAME, event.getFormattedMessage()); + + serializeException(event, serializer); + + serializer.writeRaw(','); + serializer.writeStringField(SERVICE_NAME_ATTR_NAME, LambdaHandlerProcessor.serviceName()); + serializer.writeRaw(','); + serializer.writeStringField(SERVICE_VERSION_ATTR_NAME, mdcPropertyMap.get(FUNCTION_VERSION.getName())); + serializer.writeRaw(','); + serializer.writeStringField(LOGGER_ATTR_NAME, event.getLoggerName()); + serializer.writeRaw(','); + serializer.writeStringField(THREAD_ATTR_NAME, event.getThreadName()); + + String arn = mdcPropertyMap.get(FUNCTION_ARN.getName()); + + serializeCloudInfo(serializer, arn); + + serializeFunctionInfo(serializer, arn, mdcPropertyMap); + + serializeMDCEntries(mdcPropertyMap, serializer); + + serializeArguments(event, serializer); + + serializer.writeEndObject(); + serializer.writeRaw('\n'); + } catch (IOException e) { + System.err.printf("Failed to encode log event, error: %s.%n", e.getMessage()); + } + return builder.toString().getBytes(UTF_8); + } + + private void serializeFunctionInfo(JsonSerializer serializer, String arn, Map<String, String> mdcPropertyMap) { + if (includeFaasInfo) { + serializer.writeRaw(','); + serializer.writeStringField(FUNCTION_ARN_ATTR_NAME, arn); + serializer.writeRaw(','); + serializer.writeStringField(FUNCTION_NAME_ATTR_NAME, mdcPropertyMap.get(FUNCTION_NAME.getName())); + serializer.writeRaw(','); + serializer.writeStringField(FUNCTION_VERSION_ATTR_NAME, mdcPropertyMap.get(FUNCTION_VERSION.getName())); + serializer.writeRaw(','); + serializer.writeStringField(FUNCTION_MEMORY_ATTR_NAME, mdcPropertyMap.get(FUNCTION_MEMORY_SIZE.getName())); + serializer.writeRaw(','); + serializer.writeStringField(FUNCTION_REQUEST_ID_ATTR_NAME, mdcPropertyMap.get(FUNCTION_REQUEST_ID.getName())); + serializer.writeRaw(','); + serializer.writeStringField(FUNCTION_COLD_START_ATTR_NAME, mdcPropertyMap.get(FUNCTION_COLD_START.getName())); + serializer.writeRaw(','); + serializer.writeStringField(FUNCTION_TRACE_ID_ATTR_NAME, mdcPropertyMap.get(FUNCTION_TRACE_ID.getName())); + String correlationId = mdcPropertyMap.get(CORRELATION_ID.getName()); + if (correlationId != null) { + serializer.writeRaw(','); + serializer.writeStringField(CORRELATION_ID_ATTR_NAME, correlationId); + } + } + } + + private void serializeCloudInfo(JsonSerializer serializer, String arn) { + if (includeCloudInfo) { + serializer.writeRaw(','); + serializer.writeStringField(CLOUD_PROVIDER_ATTR_NAME, CLOUD_PROVIDER); + serializer.writeRaw(','); + serializer.writeStringField(CLOUD_SERVICE_ATTR_NAME, CLOUD_SERVICE); + if (arn != null) { + String[] arnParts = arn.split(":"); + serializer.writeRaw(','); + serializer.writeStringField(CLOUD_REGION_ATTR_NAME, arnParts[3]); + serializer.writeRaw(','); + serializer.writeStringField(CLOUD_ACCOUNT_ATTR_NAME, arnParts[4]); + } + } + } + + private void serializeException(ILoggingEvent event, JsonSerializer serializer) { + IThrowableProxy throwableProxy = event.getThrowableProxy(); + if (throwableProxy != null) { + if (throwableConverter != null) { + serializeException(serializer, throwableProxy.getClassName(), + throwableProxy.getMessage(), throwableConverter.convert(event)); + } else if (throwableProxy instanceof ThrowableProxy) { + Throwable throwable = ((ThrowableProxy) throwableProxy).getThrowable(); + serializeException(serializer, throwable.getClass().getName(), throwable.getMessage(), + Arrays.toString(throwable.getStackTrace())); + } else { + serializeException(serializer, throwableProxy.getClassName(), + throwableProxy.getMessage(), throwableProxyConverter.convert(event)); + } + } + } + + @Override + public byte[] footerBytes() { + return new byte[0]; + } + + /** + * Specify a throwable converter to format the stacktrace according to your need + * (default is <b>null</b>, no throwableConverter): + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"> + * <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"> + * <maxDepthPerThrowable>30</maxDepthPerThrowable> + * <maxLength>2048</maxLength> + * <shortenedClassNameLength>20</shortenedClassNameLength> + * <exclude>sun\.reflect\..*\.invoke.*</exclude> + * <exclude>net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude> + * <evaluator class="myorg.MyCustomEvaluator"/> + * <rootCauseFirst>true</rootCauseFirst> + * <inlineHash>true</inlineHash> + * </throwableConverter> + * </encoder> + * }</pre> + * + * @param throwableConverter converter for the throwable + */ + public void setThrowableConverter(ThrowableHandlingConverter throwableConverter) { + this.throwableConverter = throwableConverter; + } + + /** + * Specify if cloud information should be logged (default is <b>true</b>): + * <ul> + * <li>cloud.provider</li> + * <li>cloud.service.name</li> + * <li>cloud.region</li> + * <li>cloud.account.id</li> + * </ul> + * <br/> + * We strongly recommend to keep these information. + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"> + * <includeCloudInfo>false</includeCloudInfo> + * </encoder> + * }</pre> + * + * @param includeCloudInfo if thread information should be logged + */ + public void setIncludeCloudInfo(boolean includeCloudInfo) { + this.includeCloudInfo = includeCloudInfo; + } + + /** + * Specify if Lambda function information should be logged (default is <b>true</b>): + * <ul> + * <li>faas.id</li> + * <li>faas.name</li> + * <li>faas.version</li> + * <li>faas.memory</li> + * <li>faas.execution</li> + * <li>faas.coldstart</li> + * <li>trace.id</li> + * </ul> + * <br/> + * We strongly recommend to keep these information. + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"> + * <includeFaasInfo>false</includeFaasInfo> + * </encoder> + * }</pre> + * + * @param includeFaasInfo if function information should be logged + */ + public void setIncludeFaasInfo(boolean includeFaasInfo) { + this.includeFaasInfo = includeFaasInfo; + } + + private void serializeException(JsonSerializer serializer, String className, String message, String stackTrace) { + serializer.writeRaw(','); + serializer.writeObjectField(EXCEPTION_MSG_ATTR_NAME, message); + serializer.writeRaw(','); + serializer.writeObjectField(EXCEPTION_CLASS_ATTR_NAME, className); + serializer.writeRaw(','); + serializer.writeObjectField(EXCEPTION_STACK_ATTR_NAME, stackTrace); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java new file mode 100644 index 000000000..9afaf0ab7 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java @@ -0,0 +1,260 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.logback; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeArguments; +import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntries; +import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntry; +import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeTimestamp; + +import ch.qos.logback.classic.pattern.ThrowableHandlingConverter; +import ch.qos.logback.classic.pattern.ThrowableProxyConverter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.ThrowableProxy; +import ch.qos.logback.core.encoder.EncoderBase; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; + +/** + * Custom encoder for logback that encodes logs in JSON format. + */ +public class LambdaJsonEncoder extends EncoderBase<ILoggingEvent> { + + protected static final String TIMESTAMP_ATTR_NAME = "timestamp"; + protected static final String LEVEL_ATTR_NAME = "level"; + protected static final String FORMATTED_MESSAGE_ATTR_NAME = "message"; + protected static final String THREAD_ATTR_NAME = "thread"; + protected static final String THREAD_ID_ATTR_NAME = "thread_id"; + protected static final String THREAD_PRIORITY_ATTR_NAME = "thread_priority"; + protected static final String EXCEPTION_MSG_ATTR_NAME = "message"; + protected static final String EXCEPTION_CLASS_ATTR_NAME = "name"; + protected static final String EXCEPTION_STACK_ATTR_NAME = "stack"; + protected static final String EXCEPTION_ATTR_NAME = "error"; + + private final ThrowableProxyConverter throwableProxyConverter = new ThrowableProxyConverter(); + protected ThrowableHandlingConverter throwableConverter = null; + protected String timestampFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + protected String timestampFormatTimezoneId = null; + private boolean includeThreadInfo = false; + private boolean includePowertoolsInfo = true; + + @Override + public byte[] headerBytes() { + return new byte[0]; + } + + @Override + public void start() { + super.start(); + throwableProxyConverter.start(); + if (throwableConverter != null) { + throwableConverter.start(); + } + } + + @SuppressWarnings("java:S106") + @Override + public byte[] encode(ILoggingEvent event) { + StringBuilder builder = new StringBuilder(); + try (JsonSerializer serializer = new JsonSerializer(builder)) { + serializer.writeStartObject(); + serializer.writeStringField(LEVEL_ATTR_NAME, event.getLevel().toString()); + serializer.writeRaw(','); + serializer.writeStringField(FORMATTED_MESSAGE_ATTR_NAME, event.getFormattedMessage()); + + serializeException(event, serializer); + + TreeMap<String, String> sortedMap = new TreeMap<>(event.getMDCPropertyMap()); + serializePowertools(sortedMap, serializer); + + serializeMDCEntries(sortedMap, serializer); + + serializeArguments(event, serializer); + + serializeThreadInfo(event, serializer); + + serializer.writeRaw(','); + serializeTimestamp(serializer, event.getTimeStamp(), + timestampFormat, timestampFormatTimezoneId, TIMESTAMP_ATTR_NAME); + + serializer.writeEndObject(); + serializer.writeRaw('\n'); + } catch (IOException e) { + System.err.printf("Failed to encode log event, error: %s.%n", e.getMessage()); + } + return builder.toString().getBytes(UTF_8); + } + + private void serializeThreadInfo(ILoggingEvent event, JsonSerializer serializer) { + if (includeThreadInfo) { + if (event.getThreadName() != null) { + serializer.writeRaw(','); + serializer.writeStringField(THREAD_ATTR_NAME, event.getThreadName()); + } + serializer.writeRaw(','); + serializer.writeNumberField(THREAD_ID_ATTR_NAME, Thread.currentThread().getId()); + serializer.writeRaw(','); + serializer.writeNumberField(THREAD_PRIORITY_ATTR_NAME, Thread.currentThread().getPriority()); + } + } + + private void serializePowertools(TreeMap<String, String> sortedMap, JsonSerializer serializer) { + if (includePowertoolsInfo) { + for (Map.Entry<String, String> entry : sortedMap.entrySet()) { + if (PowertoolsLoggedFields.stringValues().contains(entry.getKey()) + && !(entry.getKey().equals(PowertoolsLoggedFields.SAMPLING_RATE.getName()) && entry.getValue().equals("0.0"))) { + serializeMDCEntry(entry, serializer); + } + } + } + } + + private void serializeException(ILoggingEvent event, JsonSerializer serializer) { + IThrowableProxy throwableProxy = event.getThrowableProxy(); + if (throwableProxy != null) { + if (throwableConverter != null) { + serializeException(serializer, throwableProxy.getClassName(), + throwableProxy.getMessage(), throwableConverter.convert(event)); + } else if (throwableProxy instanceof ThrowableProxy) { + Throwable throwable = ((ThrowableProxy) throwableProxy).getThrowable(); + serializeException(serializer, throwable.getClass().getName(), throwable.getMessage(), + Arrays.toString(throwable.getStackTrace())); + } else { + serializeException(serializer, throwableProxy.getClassName(), throwableProxy.getMessage(), throwableProxyConverter.convert( + event)); + } + } + } + + @Override + public byte[] footerBytes() { + return new byte[0]; + } + + /** + * Specify the format of the timestamp (default is <b>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</b>). + * Note that if you use the Lambda Advanced Logging Configuration, you should keep the default format. + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + * <timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSSZz</timestampFormat> + * </encoder> + * }</pre> + * + * @param timestampFormat format of the timestamp (compatible with {@link java.text.SimpleDateFormat}) + */ + public void setTimestampFormat(String timestampFormat) { + this.timestampFormat = timestampFormat; + } + + /** + * Specify the format of the time zone id for timestamp (default is <b>null</b>, no timezone): + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + * <timestampFormatTimezoneId>Europe/Paris</timestampFormatTimezoneId> + * </encoder> + * }</pre> + * + * @param timestampFormatTimezoneId Zone Id (see {@link java.util.TimeZone}) + */ + public void setTimestampFormatTimezoneId(String timestampFormatTimezoneId) { + this.timestampFormatTimezoneId = timestampFormatTimezoneId; + } + + /** + * Specify a throwable converter to format the stacktrace according to your need + * (default is <b>null</b>, no throwableConverter): + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + * <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"> + * <maxDepthPerThrowable>30</maxDepthPerThrowable> + * <maxLength>2048</maxLength> + * <shortenedClassNameLength>20</shortenedClassNameLength> + * <exclude>sun\.reflect\..*\.invoke.*</exclude> + * <exclude>net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude> + * <evaluator class="myorg.MyCustomEvaluator"/> + * <rootCauseFirst>true</rootCauseFirst> + * <inlineHash>true</inlineHash> + * </throwableConverter> + * </encoder> + * }</pre> + * + * @param throwableConverter converter for the throwable + */ + public void setThrowableConverter(ThrowableHandlingConverter throwableConverter) { + this.throwableConverter = throwableConverter; + } + + /** + * Specify if thread information should be logged (default is <b>false</b>) + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + * <includeThreadInfo>true</includeThreadInfo> + * </encoder> + * }</pre> + * + * @param includeThreadInfo if thread information should be logged + */ + public void setIncludeThreadInfo(boolean includeThreadInfo) { + this.includeThreadInfo = includeThreadInfo; + } + + /** + * Specify if Lambda function information should be logged (default is <b>true</b>): + * <ul> + * <li>function_name</li> + * <li>function_version</li> + * <li>function_arn</li> + * <li>function_memory_size</li> + * <li>function_request_id</li> + * <li>cold_start</li> + * <li>xray_trace_id</li> + * <li>sampling_rate</li> + * <li>service</li> + * </ul> + * <br/> + * We strongly recommend to keep these information. + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + * <includePowertoolsInfo>false</includePowertoolsInfo> + * </encoder> + * }</pre> + * + * @param includePowertoolsInfo if function information should be logged + */ + public void setIncludePowertoolsInfo(boolean includePowertoolsInfo) { + this.includePowertoolsInfo = includePowertoolsInfo; + } + + private void serializeException(JsonSerializer serializer, String className, String message, String stackTrace) { + Map<Object, Object> map = new HashMap<>(); + map.put(EXCEPTION_MSG_ATTR_NAME, message); + map.put(EXCEPTION_CLASS_ATTR_NAME, className); + map.put(EXCEPTION_STACK_ATTR_NAME, stackTrace); + serializer.writeRaw(','); + serializer.writeObjectField(EXCEPTION_ATTR_NAME, map); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackLoggingManager.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackLoggingManager.java new file mode 100644 index 000000000..906ebdad5 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackLoggingManager.java @@ -0,0 +1,100 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.logback.internal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import software.amazon.lambda.powertools.logging.internal.BufferManager; +import software.amazon.lambda.powertools.logging.internal.LoggingManager; +import software.amazon.lambda.powertools.logging.logback.BufferingAppender; + +/** + * LoggingManager for Logback that provides log level management and buffer operations. + * Implements both {@link LoggingManager} and {@link BufferManager} interfaces. + */ +public class LogbackLoggingManager implements LoggingManager, BufferManager { + + private final LoggerContext loggerContext; + + public LogbackLoggingManager() { + ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); + if (!(loggerFactory instanceof LoggerContext)) { + throw new RuntimeException("LoggerFactory does not match required type: " + LoggerContext.class.getName()); + } + loggerContext = (LoggerContext) loggerFactory; + } + + /** + * @inheritDoc + */ + @Override + @SuppressWarnings("java:S4792") + public void setLogLevel(org.slf4j.event.Level logLevel) { + List<Logger> loggers = loggerContext.getLoggerList(); + for (Logger logger : loggers) { + logger.setLevel(Level.convertAnSLF4JLevel(logLevel)); + } + } + + /** + * @inheritDoc + */ + @Override + public org.slf4j.event.Level getLogLevel(org.slf4j.Logger logger) { + return org.slf4j.event.Level.valueOf(loggerContext.getLogger(logger.getName()).getEffectiveLevel().toString()); + } + + /** + * @inheritDoc + */ + @Override + public void flushBuffer() { + getBufferingAppenders().forEach(BufferingAppender::flushBuffer); + } + + /** + * @inheritDoc + */ + @Override + public void clearBuffer() { + getBufferingAppenders().forEach(BufferingAppender::clearBuffer); + } + + private Collection<BufferingAppender> getBufferingAppenders() { + // Search all buffering appenders to avoid relying on the appender name given by the user + return loggerContext.getLoggerList().stream() + .flatMap(logger -> { + Iterator<Appender<ILoggingEvent>> iterator = logger.iteratorForAppenders(); + List<Appender<ILoggingEvent>> appenders = new ArrayList<>(); + iterator.forEachRemaining(appenders::add); + return appenders.stream(); + }) + .filter(BufferingAppender.class::isInstance) + .map(BufferingAppender.class::cast) + .collect(Collectors.toList()); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptor.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptor.java new file mode 100644 index 000000000..c35171a01 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.logging.logback.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-logging-logback module is on the classpath. + */ +public final class LogbackUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("logging-logback"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/jni-config.json b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/jni-config.json new file mode 100644 index 000000000..c8b081385 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/jni-config.json @@ -0,0 +1,10 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/reflect-config.json b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/reflect-config.json new file mode 100644 index 000000000..5c5eb83fa --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/reflect-config.json @@ -0,0 +1,187 @@ +[ +{ + "name":"[Ljava.lang.Object;" +}, +{ + "name":"[Ljava.lang.String;" +}, +{ + "name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder", + "queryAllPublicMethods":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.joran.SerializedModelConfigurator", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.util.DefaultJoranConfigurator", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.core.FileAppender", + "queryAllPublicMethods":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setFile","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"ch.qos.logback.core.OutputStreamAppender", + "methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }] +}, +{ + "name":"ch.qos.logback.core.encoder.Encoder", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder", + "methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }] +}, +{ + "name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase", + "methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"ch.qos.logback.core.spi.ContextAware", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.Context" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$MessageAttribute", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getBinaryListValues","parameterTypes":[] }, {"name":"getBinaryValue","parameterTypes":[] }, {"name":"getDataType","parameterTypes":[] }, {"name":"getStringListValues","parameterTypes":[] }, {"name":"getStringValue","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getAttributes","parameterTypes":[] }, {"name":"getAwsRegion","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getEventSource","parameterTypes":[] }, {"name":"getEventSourceArn","parameterTypes":[] }, {"name":"getMd5OfBody","parameterTypes":[] }, {"name":"getMd5OfMessageAttributes","parameterTypes":[] }, {"name":"getMessageAttributes","parameterTypes":[] }, {"name":"getMessageId","parameterTypes":[] }, {"name":"getReceiptHandle","parameterTypes":[] }] +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"java.io.IOException" +}, +{ + "name":"java.io.InputStream" +}, +{ + "name":"java.io.OutputStream" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Cloneable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Iterable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Object" +}, +{ + "name":"java.lang.ProcessEnvironment", + "fields":[{"name":"theCaseInsensitiveEnvironment"}, {"name":"theEnvironment"}] +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.util.AbstractCollection", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.AbstractList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.AbstractMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.Arrays$ArrayList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.util.Collection", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.Collections$SingletonMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] +}, +{ + "name":"java.util.List", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.Map", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.RandomAccess", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"kotlin.Metadata" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"isColdStart"}] +}, +{ + "name":"software.amazon.lambda.powertools.logging.logback.BufferingAppender", + "queryAllPublicMethods":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBufferAtVerbosity","parameterTypes":["java.lang.String"] }, {"name":"setFlushOnErrorLog","parameterTypes":["boolean"] }, {"name":"setMaxBytes","parameterTypes":["int"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder", + "queryAllPublicMethods":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder", + "queryAllPublicMethods":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.logback.internal.LogbackUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/resource-config.json b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/resource-config.json new file mode 100644 index 000000000..b60a39ad5 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/resource-config.json @@ -0,0 +1,17 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E" + }, { + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }]}, + "bundles":[] +} diff --git a/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager new file mode 100644 index 000000000..958f2e459 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager @@ -0,0 +1 @@ +software.amazon.lambda.powertools.logging.logback.internal.LogbackLoggingManager \ No newline at end of file diff --git a/powertools-logging/powertools-logging-logback/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-logging/powertools-logging-logback/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..1ce49119f --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.logging.logback.internal.LogbackUserAgentInterceptor diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/LogbackLoggingManagerTest.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/LogbackLoggingManagerTest.java new file mode 100644 index 000000000..60f158739 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/LogbackLoggingManagerTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; +import static org.slf4j.event.Level.DEBUG; +import static org.slf4j.event.Level.ERROR; +import static org.slf4j.event.Level.WARN; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; +import software.amazon.lambda.powertools.logging.logback.internal.LogbackLoggingManager; + +class LogbackLoggingManagerTest { + + private static final Logger LOG = LoggerFactory.getLogger(LogbackLoggingManagerTest.class); + private static final Logger ROOT = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + + @BeforeEach + void setUp() throws JoranException, IOException { + resetLogbackConfig("/logback-test.xml"); + + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @Test + void getLogLevel_shouldReturnConfiguredLogLevel() { + LogbackLoggingManager manager = new LogbackLoggingManager(); + Level logLevel = manager.getLogLevel(LOG); + assertThat(logLevel).isEqualTo(DEBUG); + + logLevel = manager.getLogLevel(ROOT); + assertThat(logLevel).isEqualTo(WARN); + } + + @Test + void resetLogLevel() { + LogbackLoggingManager manager = new LogbackLoggingManager(); + manager.setLogLevel(ERROR); + + Level logLevel = manager.getLogLevel(LOG); + assertThat(logLevel).isEqualTo(ERROR); + } + + @Test + void shouldDetectMultipleBufferingAppendersRegardlessOfName() throws JoranException { + // Given - configuration with multiple BufferingAppenders with different names + resetLogbackConfig("/logback-multiple-buffering.xml"); + + Logger logger = LoggerFactory.getLogger("test.multiple.appenders"); + + // When - log messages and flush buffers + logger.debug("Test message 1"); + logger.debug("Test message 2"); + + LogbackLoggingManager manager = new LogbackLoggingManager(); + manager.flushBuffer(); + + // Then - both appenders should have flushed their buffers + File logFile = new File("target/logfile.json"); + assertThat(logFile).exists(); + String content = contentOf(logFile); + // Each message should appear twice (once from each BufferingAppender) + assertThat(content.split("Test message 1", -1)).hasSize(3); // 2 occurrences = 3 parts + assertThat(content.split("Test message 2", -1)).hasSize(3); // 2 occurrences = 3 parts + } + + private void resetLogbackConfig(String configFileName) throws JoranException { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.reset(); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + configurator.doConfigure(getClass().getResourceAsStream(configFileName)); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java new file mode 100644 index 000000000..30ede8ba8 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java @@ -0,0 +1,182 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverter; +import ch.qos.logback.classic.spi.LoggingEvent; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.logging.PowertoolsLogging; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; +import software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder; + +@Order(3) +class LambdaEcsEncoderTest { + + private static final Logger logger = (Logger) LoggerFactory.getLogger(LambdaEcsEncoderTest.class.getName()); + + private Context context; + + @BeforeEach + void setUp() throws IllegalAccessException, IOException { + MDC.clear(); + // Reset cold start state + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); + writeStaticField(PowertoolsLogging.class, "hasBeenInitialized", new AtomicBoolean(false), true); + + context = new TestLambdaContext(); + // Make sure file is cleaned up before running tests + try { + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // file may not exist on the first launch + } + } + + @AfterEach + void cleanUp() throws IOException { + try { + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // file may not exist on the first launch + } + } + + @Test + void shouldLogInEcsFormat() { + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); + handler.handleRequest("Input", context); + + File logFile = new File("target/ecslogfile.json"); + assertThat(contentOf(logFile)).contains( + "\"ecs.version\":\"1.2.0\",\"log.level\":\"DEBUG\",\"message\":\"Test debug event\",\"service.name\":\"testLogback\",\"service.version\":\"1\",\"log.logger\":\"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled\",\"process.thread.name\":\"main\",\"cloud.provider\":\"aws\",\"cloud.service.name\":\"lambda\",\"cloud.region\":\"us-east-1\",\"cloud.account.id\":\"123456789012\",\"faas.id\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"faas.name\":\"test-function\",\"faas.version\":\"1\",\"faas.memory\":\"128\",\"faas.execution\":\"test-request-id\",\"faas.coldstart\":\"true\",\"trace.id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\"}\n"); + } + + private final LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "message", null, null); + + @Test + void shouldNotLogFunctionInfo() { + // GIVEN + LambdaEcsEncoder encoder = new LambdaEcsEncoder(); + setMDC(); + + // WHEN + byte[] encoded = encoder.encode(loggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN + assertThat(result).contains( + "\"faas.id\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"faas.name\":\"test-function\",\"faas.version\":\"1\",\"faas.memory\":\"128\",\"faas.execution\":\"test-request-id\",\"faas.coldstart\":\"false\"") + .contains("\"correlation.id\":\"test-correlation-id\""); + + // WHEN (includeFaasInfo = false) + encoder.setIncludeFaasInfo(false); + encoded = encoder.encode(loggingEvent); + result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (no faas info in logs) + assertThat(result).doesNotContain("faas"); + } + + @Test + void shouldNotLogCloudInfo() { + // GIVEN + LambdaEcsEncoder encoder = new LambdaEcsEncoder(); + setMDC(); + + // WHEN + byte[] encoded = encoder.encode(loggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN + assertThat(result).contains( + "\"cloud.provider\":\"aws\",\"cloud.service.name\":\"lambda\",\"cloud.region\":\"us-east-1\",\"cloud.account.id\":\"123456789012\""); + + // WHEN (includeCloudInfo = false) + encoder.setIncludeCloudInfo(false); + encoded = encoder.encode(loggingEvent); + result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (no faas info in logs) + assertThat(result).doesNotContain("cloud"); + } + + @Test + void shouldLogException() { + // GIVEN + LambdaEcsEncoder encoder = new LambdaEcsEncoder(); + encoder.start(); + LoggingEvent errorloggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Error", + new IllegalStateException("Unexpected value"), null); + + // WHEN + byte[] encoded = encoder.encode(errorloggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN + assertThat(result).contains( + "\"message\":\"Error\",\"error.message\":\"Unexpected value\",\"error.type\":\"java.lang.IllegalStateException\",\"error.stack_trace\":\"[software.amazon.lambda.powertools.logging.internal.LambdaEcsEncoderTest.shouldLogException"); + + // WHEN (configure a custom throwableConverter) + encoder = new LambdaEcsEncoder(); + RootCauseFirstThrowableProxyConverter throwableConverter = new RootCauseFirstThrowableProxyConverter(); + encoder.setThrowableConverter(throwableConverter); + encoder.start(); + encoded = encoder.encode(errorloggingEvent); + result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (stack is logged with root cause first) + assertThat(result).contains( + "\"message\":\"Error\",\"error.message\":\"Unexpected value\",\"error.type\":\"java.lang.IllegalStateException\",\"error.stack_trace\":\"java.lang.IllegalStateException: Unexpected value\\n"); + } + + private void setMDC() { + MDC.put(PowertoolsLoggedFields.FUNCTION_NAME.getName(), context.getFunctionName()); + MDC.put(PowertoolsLoggedFields.FUNCTION_ARN.getName(), context.getInvokedFunctionArn()); + MDC.put(PowertoolsLoggedFields.FUNCTION_VERSION.getName(), context.getFunctionVersion()); + MDC.put(PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE.getName(), + String.valueOf(context.getMemoryLimitInMB())); + MDC.put(PowertoolsLoggedFields.FUNCTION_REQUEST_ID.getName(), context.getAwsRequestId()); + MDC.put(PowertoolsLoggedFields.FUNCTION_COLD_START.getName(), "false"); + MDC.put(PowertoolsLoggedFields.SAMPLING_RATE.getName(), "0.2"); + MDC.put(PowertoolsLoggedFields.SERVICE.getName(), "Service"); + MDC.put(PowertoolsLoggedFields.CORRELATION_ID.getName(), "test-correlation-id"); + } + +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java new file mode 100644 index 000000000..16bd9e92a --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java @@ -0,0 +1,445 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.joining; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverter; +import ch.qos.logback.classic.spi.LoggingEvent; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.PowertoolsLogging; +import software.amazon.lambda.powertools.logging.argument.StructuredArgument; +import software.amazon.lambda.powertools.logging.argument.StructuredArguments; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEvent; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEventDisabled; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEventForStream; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogResponse; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogResponseForStream; +import software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder; + +@Order(2) +class LambdaJsonEncoderTest { + private static final Logger logger = (Logger) LoggerFactory.getLogger(LambdaJsonEncoderTest.class.getName()); + private final LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "message", null, null); + + private Context context; + + @BeforeEach + void setUp() throws IllegalAccessException, IOException { + MDC.clear(); + // Reset cold start state + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); + writeStaticField(PowertoolsLogging.class, "hasBeenInitialized", new AtomicBoolean(false), true); + + context = new TestLambdaContext(); + // Make sure file is cleaned up before running tests + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // file may not exist on the first launch + } + } + + @AfterEach + void cleanUp() throws IOException { + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // file may not exist on the first launch + } + } + + @Test + void shouldLogInJsonFormat() { + // GIVEN + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); + + // WHEN + handler.handleRequest("Input", context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains( + "{\"level\":\"DEBUG\",\"message\":\"Test debug event\",\"cold_start\":true,\"function_arn\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"function_memory_size\":128,\"function_name\":\"test-function\",\"function_request_id\":\"test-request-id\",\"function_version\":1,\"service\":\"testLogback\",\"xray_trace_id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\",\"timestamp\":"); + } + + @Test + void shouldLogArgumentsAsJsonWhenUsingRawJson() { + // GIVEN + PowertoolsArguments requestHandler = new PowertoolsArguments(PowertoolsArguments.ArgumentFormat.JSON); + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("eu-central-1"); + SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); + attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); + msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); + + // WHEN + requestHandler.handleRequest(msg, context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains( + "\"input\":{\"awsRegion\":\"eu-central-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains("\"message\":\"1212abcd\"") + // Should auto-escape double quotes around id + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); + // Reserved keys should be ignored + PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { + assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); + assertThat(contentOf(logFile)).contains( + "\"message\":\"Attempted to use reserved key '" + reservedKey + + "' in structured argument. This key will be ignored.\""); + }); + } + + @Test + void shouldLogArgumentsAsJsonWhenUsingKeyValue() { + // GIVEN + PowertoolsArguments requestHandler = new PowertoolsArguments(PowertoolsArguments.ArgumentFormat.ENTRY); + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("eu-central-1"); + SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); + attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); + msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); + + // WHEN + requestHandler.handleRequest(msg, context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains( + "\"input\":{\"awsRegion\":\"eu-central-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains("\"message\":\"1212abcd\"") + // Should auto-escape double quotes around id + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); + // Reserved keys should be ignored + PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { + assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); + assertThat(contentOf(logFile)).contains( + "\"message\":\"Attempted to use reserved key '" + reservedKey + + "' in structured argument. This key will be ignored.\""); + }); + } + + @Test + void shouldNotLogPowertoolsInfo() { + // GIVEN + LambdaJsonEncoder encoder = new LambdaJsonEncoder(); + + MDC.put(PowertoolsLoggedFields.FUNCTION_NAME.getName(), context.getFunctionName()); + MDC.put(PowertoolsLoggedFields.FUNCTION_ARN.getName(), context.getInvokedFunctionArn()); + MDC.put(PowertoolsLoggedFields.FUNCTION_VERSION.getName(), context.getFunctionVersion()); + MDC.put(PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE.getName(), + String.valueOf(context.getMemoryLimitInMB())); + MDC.put(PowertoolsLoggedFields.FUNCTION_REQUEST_ID.getName(), context.getAwsRequestId()); + MDC.put(PowertoolsLoggedFields.FUNCTION_COLD_START.getName(), "false"); + MDC.put(PowertoolsLoggedFields.SAMPLING_RATE.getName(), "0.2"); + MDC.put(PowertoolsLoggedFields.SERVICE.getName(), "Service"); + + // WHEN + byte[] encoded = encoder.encode(loggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN + assertThat(result).contains( + "{\"level\":\"INFO\",\"message\":\"message\",\"cold_start\":false,\"function_arn\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"function_memory_size\":128,\"function_name\":\"test-function\",\"function_request_id\":\"test-request-id\",\"function_version\":1,\"sampling_rate\":0.2,\"service\":\"Service\",\"timestamp\":"); + + // WHEN (powertoolsInfo = false) + encoder.setIncludePowertoolsInfo(false); + encoded = encoder.encode(loggingEvent); + result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (no powertools info in logs) + assertThat(result).doesNotContain("cold_start", "function_arn", "function_memory_size", "function_name", + "function_request_id", "function_version", "sampling_rate", "service"); + } + + @Test + void shouldLogStructuredArgumentsAsNewEntries() { + // GIVEN + LambdaJsonEncoder encoder = new LambdaJsonEncoder(); + + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("eu-central-1"); + SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); + attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); + msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); + StructuredArgument argument = StructuredArguments.entry("msg", msg); + + // WHEN + LoggingEvent structuredLoggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "A message", null, + new Object[] { argument }); + byte[] encoded = encoder.encode(structuredLoggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (logged as JSON) + assertThat(result) + .contains( + "\"message\":\"A message\",\"msg\":{\"awsRegion\":\"eu-central-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}"); + } + + @Test + void shouldLogEventForHandlerWithLogEventAnnotation() { + // GIVEN + PowertoolsLogEvent requestHandler = new PowertoolsLogEvent(); + + // WHEN + requestHandler.handleRequest(singletonList("ListOfOneElement"), context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("\"event\":[\"ListOfOneElement\"]"); + } + + @Test + void shouldLogEventForHandlerWhenEnvVariableSetToTrue() { + try { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_EVENT = true; + + PowertoolsLogEnabled requestHandler = new PowertoolsLogEnabled(); + + SQSEvent.SQSMessage message = new SQSEvent.SQSMessage(); + message.setBody("body"); + message.setMessageId("1234abcd"); + message.setAwsRegion("eu-central-1"); + + // WHEN + requestHandler.handleRequest(message, context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("\"message\":\"Handler Event\"") + .contains( + "\"event\":{\"awsRegion\":\"eu-central-1\",\"body\":\"body\",\"messageId\":\"1234abcd\"}"); + } finally { + LoggingConstants.POWERTOOLS_LOG_EVENT = false; + } + } + + @Test + void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() throws IOException { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_EVENT = false; + + // WHEN + PowertoolsLogEventDisabled requestHandler = new PowertoolsLogEventDisabled(); + requestHandler.handleRequest(singletonList("ListOfOneElement"), context); + + // THEN + Assertions.assertEquals(0, + Files.lines(Paths.get("target/logfile.json")).collect(joining()).length()); + } + + @Test + void shouldLogEventAsStringForStreamHandler() throws IOException { + // GIVEN + PowertoolsLogEventForStream requestStreamHandler = new PowertoolsLogEventForStream(); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + // WHEN + requestStreamHandler.handleRequest( + new ByteArrayInputStream( + new ObjectMapper().writeValueAsBytes(Collections.singletonMap("key", "value"))), + output, context); + + // THEN + assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) + .isNotEmpty(); + + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("\"message\":\"Handler Event\"") + // logged as String for StreamHandler (should auto-escape double-quotes to avoid breaking JSON format) + .contains("\"event\":\"{\\\"key\\\":\\\"value\\\"}\""); + } + + @Test + void shouldLogResponseForHandlerWithLogResponseAnnotation() { + // GIVEN + PowertoolsLogResponse requestHandler = new PowertoolsLogResponse(); + + // WHEN + requestHandler.handleRequest("input", context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("\"message\":\"Handler Response\"") + .contains("\"response\":\"Hola mundo\""); + } + + @Test + void shouldLogResponseForHandlerWhenEnvVariableSetToTrue() { + try { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_RESPONSE = true; + + PowertoolsLogEnabled requestHandler = new PowertoolsLogEnabled(); + + // WHEN + requestHandler.handleRequest("input", context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("\"message\":\"Handler Response\"") + .contains("\"response\":\"Bonjour le monde\""); + } finally { + LoggingConstants.POWERTOOLS_LOG_RESPONSE = false; + } + } + + @Test + void shouldLogResponseForStreamHandler() throws IOException { + // GIVEN + PowertoolsLogResponseForStream requestStreamHandler = new PowertoolsLogResponseForStream(); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + String input = "<user><firstName>Bob</firstName><lastName>The Sponge</lastName></user>"; + + // WHEN + requestStreamHandler.handleRequest(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), output, + context); + + // THEN + assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) + .isEqualTo(input); + + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("\"message\":\"Handler Response\"") + .contains("\"response\":\"" + input + "\""); + } + + @Test + void shouldLogThreadInfo() { + // GIVEN + LambdaJsonEncoder encoder = new LambdaJsonEncoder(); + encoder.setIncludeThreadInfo(true); + + // WHEN + byte[] encoded = encoder.encode(loggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN + assertThat(result).contains( + "\"thread\":\"main\",\"thread_id\":" + Thread.currentThread().getId() + ",\"thread_priority\":5"); + } + + @Test + void shouldLogTimestampDifferently() { + // GIVEN + LambdaJsonEncoder encoder = new LambdaJsonEncoder(); + String pattern = "yyyy-MM-dd_HH"; + String timeZone = "Europe/Paris"; + encoder.setTimestampFormat(pattern); + encoder.setTimestampFormatTimezoneId(timeZone); + + // WHEN + Date date = new Date(); + byte[] encoded = encoder.encode(loggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone(timeZone)); + assertThat(result).contains("\"timestamp\":\"" + simpleDateFormat.format(date) + "\""); + } + + @Test + void shouldLogException() { + // GIVEN + LambdaJsonEncoder encoder = new LambdaJsonEncoder(); + encoder.start(); + LoggingEvent errorloggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Error", + new IllegalStateException("Unexpected value"), null); + + // WHEN + byte[] encoded = encoder.encode(errorloggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN + assertThat(result).contains("\"message\":\"Error\",\"error\":{") + .contains("\"message\":\"Unexpected value\"") + .contains("\"name\":\"java.lang.IllegalStateException\"") + .contains( + "\"stack\":\"[software.amazon.lambda.powertools.logging.internal.LambdaJsonEncoderTest.shouldLogException"); + + // WHEN (configure a custom throwableConverter) + encoder = new LambdaJsonEncoder(); + RootCauseFirstThrowableProxyConverter throwableConverter = new RootCauseFirstThrowableProxyConverter(); + encoder.setThrowableConverter(throwableConverter); + encoder.start(); + encoded = encoder.encode(errorloggingEvent); + result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (stack is logged with root cause first) + assertThat(result).contains("\"message\":\"Unexpected value\"") + .contains("\"name\":\"java.lang.IllegalStateException\"") + .contains("\"stack\":\"java.lang.IllegalStateException: Unexpected value\\n"); + } + +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java new file mode 100644 index 000000000..1fc235ff7 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java @@ -0,0 +1,82 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal.handler; + +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.fasterxml.jackson.core.JsonProcessingException; + +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.argument.StructuredArguments; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +public class PowertoolsArguments implements RequestHandler<SQSEvent.SQSMessage, String> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsArguments.class); + private final ArgumentFormat argumentFormat; + + public PowertoolsArguments(ArgumentFormat argumentFormat) { + this.argumentFormat = argumentFormat; + } + + @Override + @Logging(clearState = true) + public String handleRequest(SQSEvent.SQSMessage input, Context context) { + try { + MDC.put(CORRELATION_ID.getName(), input.getMessageId()); + if (argumentFormat == ArgumentFormat.JSON) { + LOG.debug("SQS Event", + StructuredArguments.json("input", + JsonConfig.get().getObjectMapper().writeValueAsString(input)), + // function_name is a reserved key by PowertoolsLoggedFields and should be omitted + StructuredArguments.entry("function_name", "shouldBeIgnored")); + } else { + LOG.debug("SQS Event", + StructuredArguments.entry("input", input), + // function_name is a reserved key by PowertoolsLoggedFields and should be omitted + StructuredArguments.entry("function_name", "shouldBeIgnored")); + } + + // Attempt logging all reserved keys, the values should not be overwritten by "shouldBeIgnored" + final Map<String, String> reservedKeysMap = new HashMap<>(); + for (String field : PowertoolsLoggedFields.stringValues()) { + reservedKeysMap.put(field, "shouldBeIgnored"); + } + reservedKeysMap.put("message", "shouldBeIgnored"); + reservedKeysMap.put("level", "shouldBeIgnored"); + reservedKeysMap.put("timestamp", "shouldBeIgnored"); + LOG.debug("Reserved keys", StructuredArguments.entries(reservedKeysMap)); + LOG.debug("{}", input.getMessageId()); + LOG.warn("Message body = {} and id = \"{}\"", input.getBody(), input.getMessageId()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + return input.getMessageId(); + } + + public enum ArgumentFormat { + JSON, ENTRY + } +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java new file mode 100644 index 000000000..e8c0c5851 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal.handler; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); + + @Override + @Logging(clearState = true) + public Object handleRequest(Object input, Context context) { + MDC.put("myKey", "myValue"); + LOG.debug("Test debug event"); + return "Bonjour le monde"; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEvent.java similarity index 86% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java rename to powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEvent.java index 8a960fa87..14a7874cb 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEvent.java @@ -12,16 +12,16 @@ * */ -package software.amazon.lambda.powertools.logging.handlers; +package software.amazon.lambda.powertools.logging.internal.handler; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.logging.Logging; -public class PowerToolLogEventEnabled implements RequestHandler<Object, Object> { +public class PowertoolsLogEvent implements RequestHandler<Object, Object> { - @Logging(logEvent = true) @Override + @Logging(logEvent = true) public Object handleRequest(Object input, Context context) { return null; } diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventDisabled.java similarity index 74% rename from powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java rename to powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventDisabled.java index c13f28df0..8171bee3e 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventDisabled.java @@ -12,16 +12,16 @@ * */ -package software.amazon.lambda.powertools.tracing.handlers; +package software.amazon.lambda.powertools.logging.internal.handler; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.logging.Logging; -public class PowerTracerToolEnabledWithNoMetaDataDeprecated implements RequestHandler<Object, Object> { +public class PowertoolsLogEventDisabled implements RequestHandler<Object, Object> { @Override - @Tracing(captureResponse = false, captureError = false) + @Logging(logEvent = false) public Object handleRequest(Object input, Context context) { return null; } diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventForStream.java similarity index 75% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java rename to powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventForStream.java index 9de76586f..443051204 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventForStream.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.logging.handlers; +package software.amazon.lambda.powertools.logging.internal.handler; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -23,12 +23,12 @@ import java.util.Map; import software.amazon.lambda.powertools.logging.Logging; -public class PowerToolLogEventEnabledForStream implements RequestStreamHandler { +public class PowertoolsLogEventForStream implements RequestStreamHandler { - @Logging(logEvent = true) @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + @Logging(logEvent = true) + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(output, mapper.readValue(input, Map.class)); + mapper.writeValue(outputStream, mapper.readValue(inputStream, Map.class)); } } diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponse.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponse.java new file mode 100644 index 000000000..5cbeef487 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponse.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal.handler; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogResponse implements RequestHandler<Object, Object> { + + @Override + @Logging(logResponse = true) + public Object handleRequest(Object input, Context context) { + return "Hola mundo"; + } +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponseForStream.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponseForStream.java new file mode 100644 index 000000000..3378b9421 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponseForStream.java @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal.handler; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogResponseForStream implements RequestStreamHandler { + + @Override + @Logging(logResponse = true) + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { + byte[] buf = new byte[1024]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/BufferingAppenderTest.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/BufferingAppenderTest.java new file mode 100644 index 000000000..62d2e3056 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/BufferingAppenderTest.java @@ -0,0 +1,150 @@ +package software.amazon.lambda.powertools.logging.logback; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.ClearEnvironmentVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; + +class BufferingAppenderTest { + + private Logger logger; + + @BeforeEach + void setUp() throws IOException, JoranException { + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + + // Configure Logback with BufferingAppender + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.reset(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + configurator.doConfigure(getClass().getResourceAsStream("/logback-buffering-test.xml")); + + logger = LoggerFactory.getLogger(BufferingAppenderTest.class); + } + + @AfterEach + void cleanUp() throws IOException { + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there + } + } + + @Test + void shouldBufferDebugLogsAndFlushOnError() { + // When - log debug messages (should be buffered) + logger.debug("Debug message 1"); + logger.debug("Debug message 2"); + + // Then - no logs written yet + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).isEmpty(); + + // When - log error (should flush buffer) + logger.error("Error message"); + + // Then - all logs written + assertThat(contentOf(logFile)) + .contains("Debug message 1") + .contains("Debug message 2") + .contains("Error message"); + } + + @Test + @ClearEnvironmentVariable(key = "_X_AMZN_TRACE_ID") + void shouldLogDirectlyWhenNoTraceId() { + // When + logger.debug("Debug without trace"); + + // Then - log written directly + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Debug without trace"); + } + + @Test + void shouldNotBufferInfoLogs() { + // When - log info message (above buffer level) + logger.info("Info message"); + + // Then - log written directly + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Info message"); + } + + @Test + void shouldFlushBufferManually() { + // When - buffer debug logs + logger.debug("Buffered message"); + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).isEmpty(); + + // When - manual flush + BufferingAppender appender = getBufferingAppender(); + appender.flushBuffer(); + + // Then - logs written + assertThat(contentOf(logFile)).contains("Buffered message"); + } + + @Test + void shouldClearBufferManually() { + // When - buffer debug logs then clear + logger.debug("Buffered message"); + BufferingAppender appender = getBufferingAppender(); + appender.clearBuffer(); + + // When - log error (should not flush cleared buffer) + logger.error("Error after clear"); + + // Then - only error logged + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("Error after clear") + .doesNotContain("Buffered message"); + } + + @Test + void shouldLogOverflowWarningWhenBufferOverflows() { + // When - fill buffer beyond capacity to trigger overflow + for (int i = 0; i < 100; i++) { + logger.debug("Debug message {}", i); + } + + // When - flush buffer to trigger overflow warning + BufferingAppender appender = getBufferingAppender(); + appender.flushBuffer(); + + // Then - overflow warning should be logged + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .contains("Some logs are not displayed because they were evicted from the buffer"); + } + + private BufferingAppender getBufferingAppender() { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + return (BufferingAppender) context.getLogger(BufferingAppenderTest.class).getAppender("TestBufferingAppender"); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptorTest.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptorTest.java new file mode 100644 index 000000000..622343668 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.logging.logback.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class LogbackUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/LOGGING-LOGBACK/"); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/test/resources/junit-platform.properties b/powertools-logging/powertools-logging-logback/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..80a2481d7 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/resources/junit-platform.properties @@ -0,0 +1,17 @@ +# +# Copyright 2023 Amazon.com, Inc. or its affiliates. +# Licensed under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +# because of LambdaLoggingAspect static initialization of the LoggingManager, we need to +# set an order in the unit tests, especially LambdaLoggingAspectTest needs to be first +junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$OrderAnnotation \ No newline at end of file diff --git a/powertools-logging/powertools-logging-logback/src/test/resources/logback-buffering-test.xml b/powertools-logging/powertools-logging-logback/src/test/resources/logback-buffering-test.xml new file mode 100644 index 000000000..b9ec3917f --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/resources/logback-buffering-test.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <appender name="testFile" class="ch.qos.logback.core.FileAppender"> + <file>target/logfile.json</file> + <encoder> + <pattern>%msg%n</pattern> + </encoder> + </appender> + + <appender name="TestBufferingAppender" class="software.amazon.lambda.powertools.logging.logback.BufferingAppender"> + <bufferAtVerbosity>DEBUG</bufferAtVerbosity> + <maxBytes>1024</maxBytes> + <flushOnErrorLog>true</flushOnErrorLog> + <appender-ref ref="testFile" /> + </appender> + + <logger name="software.amazon.lambda.powertools.logging.logback.BufferingAppenderTest" level="DEBUG" + additivity="false"> + <appender-ref ref="TestBufferingAppender" /> + </logger> +</configuration> diff --git a/powertools-logging/powertools-logging-logback/src/test/resources/logback-multiple-buffering.xml b/powertools-logging/powertools-logging-logback/src/test/resources/logback-multiple-buffering.xml new file mode 100644 index 000000000..84c348d61 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/resources/logback-multiple-buffering.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <appender name="testFile" class="ch.qos.logback.core.FileAppender"> + <file>target/logfile.json</file> + <encoder> + <pattern>%msg%n</pattern> + </encoder> + </appender> + + <appender name="FirstBufferingAppender" class="software.amazon.lambda.powertools.logging.logback.BufferingAppender"> + <bufferAtVerbosity>DEBUG</bufferAtVerbosity> + <maxBytes>1024</maxBytes> + <flushOnErrorLog>true</flushOnErrorLog> + <appender-ref ref="testFile" /> + </appender> + + <appender name="SecondBufferingAppender" class="software.amazon.lambda.powertools.logging.logback.BufferingAppender"> + <bufferAtVerbosity>DEBUG</bufferAtVerbosity> + <maxBytes>1024</maxBytes> + <flushOnErrorLog>true</flushOnErrorLog> + <appender-ref ref="testFile" /> + </appender> + + <logger name="test.multiple.appenders" level="DEBUG" additivity="false"> + <appender-ref ref="FirstBufferingAppender" /> + <appender-ref ref="SecondBufferingAppender" /> + </logger> +</configuration> diff --git a/powertools-logging/powertools-logging-logback/src/test/resources/logback-test.xml b/powertools-logging/powertools-logging-logback/src/test/resources/logback-test.xml new file mode 100644 index 000000000..e118a03de --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ +<configuration> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + </encoder> + </appender> + + <appender name="logFile" class="ch.qos.logback.core.FileAppender"> + <file>target/logfile.json</file> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + </encoder> + </appender> + + <appender name="logFileWithEcs" class="ch.qos.logback.core.FileAppender"> + <file>target/ecslogfile.json</file> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"> + </encoder> + </appender> + + <logger name="software.amazon.lambda.powertools" level="DEBUG" additivity="false"> + <appender-ref ref="logFile" /> + <appender-ref ref="logFileWithEcs" /> + </logger> + <root level="WARN"> + <appender-ref ref="logFile" /> + <appender-ref ref="logFileWithEcs" /> + </root> +</configuration> \ No newline at end of file diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPaths.java similarity index 69% rename from powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java rename to powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPaths.java index ce43c9aa0..6fb38502f 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPaths.java @@ -17,21 +17,25 @@ /** * Supported Event types from which Correlation ID can be extracted */ -public class CorrelationIdPathConstants { +public class CorrelationIdPaths { /** * To use when function is expecting API Gateway Rest API Request event */ - public static final String API_GATEWAY_REST = "/requestContext/requestId"; + public static final String API_GATEWAY_REST = "requestContext.requestId"; /** * To use when function is expecting API Gateway HTTP API Request event */ - public static final String API_GATEWAY_HTTP = "/requestContext/requestId"; + public static final String API_GATEWAY_HTTP = "requestContext.requestId"; /** * To use when function is expecting Application Load balancer Request event */ - public static final String APPLICATION_LOAD_BALANCER = "/headers/x-amzn-trace-id"; + public static final String APPLICATION_LOAD_BALANCER = "headers.\"x-amzn-trace-id\""; /** * To use when function is expecting Event Bridge Request event */ - public static final String EVENT_BRIDGE = "/id"; + public static final String EVENT_BRIDGE = "id"; + /** + * To use when function is expecting an AppSync request + */ + public static final String APPSYNC_RESOLVER = "request.headers.\"x-amzn-trace-id\""; } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java index 9932eb700..79d1a95fd 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java @@ -34,18 +34,19 @@ * {@code com.amazonaws.services.lambda.runtime.Context}</p> * * <ul> - * <li>FunctionName</li> - * <li>FunctionVersion</li> - * <li>InvokedFunctionArn</li> - * <li>MemoryLimitInMB</li> + * <li>function_name</li> + * <li>function_version</li> + * <li>function_arn</li> + * <li>function_memory_size</li> + * <li>function_request_id</li> * </ul> * * <p>By default {@code Logging} will also create keys for:</p> * * <ul> - * <li>coldStart - True if this is the first invocation of this Lambda execution environment; else False</li> + * <li>cold_start - True if this is the first invocation of this Lambda execution environment; else False</li> * <li>service - The value of the 'POWER_TOOLS_SERVICE_NAME' environment variable or 'service_undefined'</li> - * <li>samplingRate - The value of the 'POWERTOOLS_LOGGER_SAMPLE_RATE' environment variable or value of samplingRate field or 0. + * <li>sampling_rate - The value of the 'POWERTOOLS_LOGGER_SAMPLE_RATE' environment variable or value of sampling_rate field or 0. * Valid value is from 0.0 to 1.0. Value outside this range is silently ignored.</li> * </ul> * @@ -56,17 +57,36 @@ * <p>By default {@code Logging} will not log the event which has trigger the invoke of the Lambda function. * This can be enabled using {@code @Logging(logEvent = true)}.</p> * - * <p>By default {@code Logging} all debug logs will follow log4j2 configuration unless configured via - * POWERTOOLS_LOGGER_SAMPLE_RATE environment variable {@code @Logging(samplingRate = <0.0-1.0>)}.</p> - * - * <p>To append additional keys to each log entry you can use {@link LoggingUtils#appendKey(String, String)}</p> + * <p>To append additional keys to each log entry you can either use {@link org.slf4j.MDC#put(String, String)} + * or {@link software.amazon.lambda.powertools.logging.argument.StructuredArguments}</p> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Logging { + /** + * Set to true if you want to log the event received by the Lambda function handler.<br/> + * Can also be configured with the 'POWERTOOLS_LOGGER_LOG_EVENT' environment variable + */ boolean logEvent() default false; + /** + * Set to true if you want to log the response sent by the Lambda function handler.<br/> + * Can also be configured with the 'POWERTOOLS_LOGGER_LOG_RESPONSE' environment variable + */ + boolean logResponse() default false; + + /** + * Set to true if you want to log the exception thrown by the Lambda function handler. + * It is already logged by AWS Lambda but with no context information. Setting this to true + * will log the exception and all the powertools additional fields, for more context.<br/> + * Can also be configured with the 'POWERTOOLS_LOGGER_LOG_ERROR' environment variable + */ + boolean logError() default false; + + /** + * Sampling rate to change log level to DEBUG. (values must be >=0.0, <=1.0) + */ double samplingRate() default 0; /** @@ -82,4 +102,10 @@ * Set this attribute to true if you want all custom keys to be deleted on each request. */ boolean clearState() default false; + + /** + * Set to true if you want to flush the log buffer when an uncaught exception occurs. + * This ensures that buffered logs are output when errors happen. + */ + boolean flushBufferOnUncaughtError() default true; } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java deleted file mode 100644 index 6e11573cc..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.logging; - -import static java.util.Arrays.asList; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Map; -import org.apache.logging.log4j.ThreadContext; - -/** - * A class of helper functions to add additional functionality to Logging. - * <p> - * {@see Logging} - */ -public final class LoggingUtils { - private static ObjectMapper objectMapper; - - private LoggingUtils() { - } - - /** - * Appends an additional key and value to each log entry made. Duplicate values - * for the same key will be replaced with the latest. - * - * @param key The name of the key to be logged - * @param value The value to be logged - */ - public static void appendKey(String key, String value) { - ThreadContext.put(key, value); - } - - - /** - * Appends additional key and value to each log entry made. Duplicate values - * for the same key will be replaced with the latest. - * - * @param customKeys Map of custom keys values to be appended to logs - */ - public static void appendKeys(Map<String, String> customKeys) { - ThreadContext.putAll(customKeys); - } - - /** - * Remove an additional key from log entry. - * - * @param customKey The name of the key to be logged - */ - public static void removeKey(String customKey) { - ThreadContext.remove(customKey); - } - - - /** - * Removes additional keys from log entry. - * - * @param keys Map of custom keys values to be appended to logs - */ - public static void removeKeys(String... keys) { - ThreadContext.removeAll(asList(keys)); - } - - /** - * Sets correlation id attribute on the logs. - * - * @param value The value of the correlation id - */ - public static void setCorrelationId(String value) { - ThreadContext.put("correlation_id", value); - } - - /** - * Sets the instance of ObjectMapper object which is used for serialising event when - * {@code @Logging(logEvent = true)}. - * - * @param objectMapper Custom implementation of object mapper to be used for logging serialised event - */ - public static void defaultObjectMapper(ObjectMapper objectMapper) { - LoggingUtils.objectMapper = objectMapper; - } - - public static ObjectMapper objectMapper() { - if (null == objectMapper) { - objectMapper = new ObjectMapper(); - } - - return objectMapper; - } -} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/PowertoolsLogging.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/PowertoolsLogging.java new file mode 100644 index 000000000..f4c18af64 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/PowertoolsLogging.java @@ -0,0 +1,369 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging; + +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.getXrayTraceId; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isColdStart; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.serviceName; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LAMBDA_LOG_LEVEL; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_LOG_LEVEL; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_SAMPLING_RATE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_TRACE_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SAMPLING_RATE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SERVICE; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.event.Level; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.databind.JsonNode; + +import io.burt.jmespath.Expression; +import software.amazon.lambda.powertools.logging.internal.BufferManager; +import software.amazon.lambda.powertools.logging.internal.LoggingManager; +import software.amazon.lambda.powertools.logging.internal.LoggingManagerRegistry; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +/** + * PowertoolsLogging provides a logging backend-agnostic API for managing Powertools logging functionality. + * This class abstracts away the underlying logging framework (Log4j2, Logback) and provides a unified + * interface for Lambda context extraction, correlation ID handling, sampling rate configuration, + * log buffering operations, and other Lambda-specific logging features. + * + * <p>This class serves as a programmatic alternative to AspectJ-based {@code @Logging} annotation, + * allowing developers to integrate Powertools logging capabilities without AspectJ dependencies.</p> + * + * Key features: + * <ul> + * <li>Lambda context initialization with function metadata, trace ID, and service name</li> + * <li>Sampling rate configuration for DEBUG logging</li> + * <li>Backend-independent log buffer management (flush/clear operations)</li> + * <li>MDC state management for structured logging</li> + * </ul> + */ +public final class PowertoolsLogging { + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsLogging.class); + private static final ThreadLocal<Random> SAMPLER = ThreadLocal.withInitial(Random::new); + private static AtomicBoolean hasBeenInitialized = new AtomicBoolean(false); + + static { + initializeLogLevel(); + } + + private PowertoolsLogging() { + // Utility class + } + + private static void initializeLogLevel() { + if (POWERTOOLS_LOG_LEVEL != null) { + Level powertoolsLevel = getLevelFromString(POWERTOOLS_LOG_LEVEL); + if (LAMBDA_LOG_LEVEL != null) { + Level lambdaLevel = getLevelFromString(LAMBDA_LOG_LEVEL); + if (powertoolsLevel.toInt() < lambdaLevel.toInt()) { + LOG.warn( + "Current log level ({}) does not match AWS Lambda Advanced Logging Controls minimum log level ({}). This can lead to data loss, consider adjusting them.", + POWERTOOLS_LOG_LEVEL, LAMBDA_LOG_LEVEL); + } + } + setLogLevel(powertoolsLevel); + } else if (LAMBDA_LOG_LEVEL != null) { + setLogLevel(getLevelFromString(LAMBDA_LOG_LEVEL)); + } + } + + private static Level getLevelFromString(String level) { + if (Arrays.stream(Level.values()).anyMatch(slf4jLevel -> slf4jLevel.name().equalsIgnoreCase(level))) { + return Level.valueOf(level.toUpperCase(Locale.ROOT)); + } else { + // FATAL does not exist in slf4j + if ("FATAL".equalsIgnoreCase(level)) { + return Level.ERROR; + } + } + // default to INFO if incorrect value + return Level.INFO; + } + + private static void setLogLevel(Level logLevel) { + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + loggingManager.setLogLevel(logLevel); + } + + /** + * Flushes the log buffer for the current Lambda execution. + * This method will flush any buffered logs to the output stream. + * The operation is backend-independent and works with both Log4j2 and Logback. + */ + public static void flushBuffer() { + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof BufferManager) { + ((BufferManager) loggingManager).flushBuffer(); + } + } + + /** + * Clears the log buffer for the current Lambda execution. + * This method will discard any buffered logs without outputting them. + * The operation is backend-independent and works with both Log4j2 and Logback. + */ + public static void clearBuffer() { + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof BufferManager) { + ((BufferManager) loggingManager).clearBuffer(); + } + } + + /** + * Initializes Lambda logging context with standard Powertools fields. + * This method should be called at the beginning of your Lambda handler to set up + * logging context with Lambda function information, trace ID, and service name. + * + * <p>Important: Call {@link #clearState(boolean)} at the end of your handler or use + * {@link #withLogging(Context, Supplier)} to handle cleanup automatically.</p> + * + * @param context the Lambda context provided by AWS Lambda runtime + */ + public static void initializeLogging(Context context) { + initializeLogging(context, 0.0, null, null); + } + + /** + * Initializes Lambda logging context with sampling rate configuration. + * This method sets up logging context and optionally enables DEBUG logging + * based on the provided sampling rate. + * + * <p>Important: Call {@link #clearState(boolean)} at the end of your handler or use + * {@link #withLogging(Context, double, Supplier)} to handle cleanup automatically.</p> + * + * @param context the Lambda context provided by AWS Lambda runtime + * @param samplingRate sampling rate for DEBUG logging (0.0 to 1.0) + */ + public static void initializeLogging(Context context, double samplingRate) { + initializeLogging(context, samplingRate, null, null); + } + + /** + * Initializes Lambda logging context with correlation ID extraction. + * This method sets up logging context and extracts correlation ID from the event + * using the provided JSON path. + * + * <p>Important: Call {@link #clearState(boolean)} at the end of your handler or use + * {@link #withLogging(Context, String, Object, Supplier)} to handle cleanup automatically.</p> + * + * @param context the Lambda context provided by AWS Lambda runtime + * @param correlationIdPath JSON path to extract correlation ID from event + * @param event the Lambda event object + */ + public static void initializeLogging(Context context, String correlationIdPath, Object event) { + initializeLogging(context, 0.0, correlationIdPath, event); + } + + /** + * Initializes Lambda logging context with full configuration. + * This method sets up logging context with Lambda function information, + * configures sampling rate for DEBUG logging, and optionally extracts + * correlation ID from the event. + * + * <p>Important: Call {@link #clearState(boolean)} at the end of your handler or use + * {@link #withLogging(Context, double, String, Object, Supplier)} to handle cleanup automatically.</p> + * + * <p>This method is thread-safe.</p> + * + * @param context the Lambda context provided by AWS Lambda runtime + * @param samplingRate sampling rate for DEBUG logging (0.0 to 1.0) + * @param correlationIdPath JSON path to extract correlation ID from event (can be null) + * @param event the Lambda event object (required if correlationIdPath is provided) + */ + public static void initializeLogging(Context context, double samplingRate, String correlationIdPath, Object event) { + + addLambdaContextToLoggingContext(context); + setLogLevelBasedOnSamplingRate(samplingRate); + getXrayTraceId().ifPresent(xRayTraceId -> MDC.put(FUNCTION_TRACE_ID.getName(), xRayTraceId)); + + if (correlationIdPath != null && !correlationIdPath.isEmpty() && event != null) { + captureCorrelationId(correlationIdPath, event); + } + } + + // Synchronized since isColdStart() is a globally managed constant in LambdaHandlerProcessor + private static synchronized void addLambdaContextToLoggingContext(Context context) { + if (context != null) { + PowertoolsLoggedFields.setValuesFromLambdaContext(context).forEach(MDC::put); + } + + MDC.put(FUNCTION_COLD_START.getName(), isColdStart() ? "true" : "false"); + if (hasBeenInitialized.compareAndSet(false, true)) { + coldStartDone(); + } + MDC.put(SERVICE.getName(), serviceName()); + } + + private static void setLogLevelBasedOnSamplingRate(double samplingRate) { + double effectiveSamplingRate = getEffectiveSamplingRate(samplingRate); + + if (effectiveSamplingRate < 0 || effectiveSamplingRate > 1) { + LOG.warn("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", + effectiveSamplingRate); + return; + } + + MDC.put(SAMPLING_RATE.getName(), String.valueOf(effectiveSamplingRate)); + + if (effectiveSamplingRate == 0) { + return; + } + + float sample = SAMPLER.get().nextFloat(); + if (effectiveSamplingRate > sample) { + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + loggingManager.setLogLevel(Level.DEBUG); + LOG.debug( + "Changed log level to DEBUG based on Sampling configuration. Sampling Rate: {}, Sampler Value: {}.", + effectiveSamplingRate, sample); + } + } + + // The environment variable takes precedence over manually set sampling rate + private static double getEffectiveSamplingRate(double samplingRate) { + String envSampleRate = POWERTOOLS_SAMPLING_RATE; + if (envSampleRate != null) { + try { + return Double.parseDouble(envSampleRate); + } catch (NumberFormatException e) { + LOG.warn( + "Skipping sampling rate on environment variable configuration because of invalid value. Sampling rate: {}", + envSampleRate); + } + } + + return samplingRate; + } + + private static void captureCorrelationId(String correlationIdPath, Object event) { + try { + JsonNode jsonNode = JsonConfig.get().getObjectMapper().valueToTree(event); + Expression<JsonNode> jmesExpression = JsonConfig.get().getJmesPath().compile(correlationIdPath); + JsonNode node = jmesExpression.search(jsonNode); + + String asText = node.asText(); + if (asText != null && !asText.isEmpty()) { + MDC.put(PowertoolsLoggedFields.CORRELATION_ID.getName(), asText); + } else { + LOG.warn("Unable to extract any correlation id. Is your function expecting supported event type?"); + } + } catch (Exception e) { + LOG.warn("Failed to capture correlation id from event.", e); + } + } + + /** + * Clears MDC state and log buffer. + * + * @param clearMdcState whether to clear MDC state + */ + public static void clearState(boolean clearMdcState) { + if (clearMdcState) { + MDC.clear(); + } + clearBuffer(); + SAMPLER.remove(); + } + + /** + * Executes code with logging context initialized and automatically clears state. + * + * @param context the Lambda context provided by AWS Lambda runtime + * @param supplier the code to execute with logging context + * @param <T> the return type + * @return the result of the supplier execution + */ + public static <T> T withLogging(Context context, Supplier<T> supplier) { + initializeLogging(context); + try { + return supplier.get(); + } finally { + clearState(true); + } + } + + /** + * Executes code with logging context initialized with sampling rate and automatically clears state. + * + * @param context the Lambda context provided by AWS Lambda runtime + * @param samplingRate sampling rate for DEBUG logging (0.0 to 1.0) + * @param supplier the code to execute with logging context + * @param <T> the return type + * @return the result of the supplier execution + */ + public static <T> T withLogging(Context context, double samplingRate, Supplier<T> supplier) { + initializeLogging(context, samplingRate); + try { + return supplier.get(); + } finally { + clearState(true); + } + } + + /** + * Executes code with logging context initialized with correlation ID extraction and automatically clears state. + * + * @param context the Lambda context provided by AWS Lambda runtime + * @param correlationIdPath JSON path to extract correlation ID from event + * @param event the Lambda event object + * @param supplier the code to execute with logging context + * @param <T> the return type + * @return the result of the supplier execution + */ + public static <T> T withLogging(Context context, String correlationIdPath, Object event, Supplier<T> supplier) { + initializeLogging(context, correlationIdPath, event); + try { + return supplier.get(); + } finally { + clearState(true); + } + } + + /** + * Executes code with logging context initialized with full configuration and automatically clears state. + * + * @param context the Lambda context provided by AWS Lambda runtime + * @param samplingRate sampling rate for DEBUG logging (0.0 to 1.0) + * @param correlationIdPath JSON path to extract correlation ID from event (can be null) + * @param event the Lambda event object (required if correlationIdPath is provided) + * @param supplier the code to execute with logging context + * @param <T> the return type + * @return the result of the supplier execution + */ + public static <T> T withLogging(Context context, double samplingRate, String correlationIdPath, Object event, + Supplier<T> supplier) { + initializeLogging(context, samplingRate, correlationIdPath, event); + try { + return supplier.get(); + } finally { + clearState(true); + } + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/ArrayArgument.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/ArrayArgument.java new file mode 100644 index 000000000..cbedbdc0f --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/ArrayArgument.java @@ -0,0 +1,45 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.argument; + +import java.util.Objects; + +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * See {@link StructuredArguments#array(String, Object...)} + */ +class ArrayArgument implements StructuredArgument { + private final String key; + private final Object[] values; + + public ArrayArgument(String key, Object[] values) { + this.key = Objects.requireNonNull(key, "Key must not be null"); + this.values = new Object[values.length]; + System.arraycopy(values, 0, this.values, 0, values.length); + } + + @Override + public void writeTo(JsonSerializer serializer) { + serializer.writeFieldName(key); + serializer.writeObject(values); + } + + @Override + public String toString() { + return key + "=" + StructuredArguments.toString(values); + } + +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/JsonArgument.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/JsonArgument.java new file mode 100644 index 000000000..debea18c5 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/JsonArgument.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.argument; + +import java.util.Objects; + +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * See {@link StructuredArguments#json(String, String)} + */ +class JsonArgument implements StructuredArgument { + private final String key; + private final String rawJson; + + public JsonArgument(String key, String rawJson) { + this.key = Objects.requireNonNull(key, "key must not be null"); + this.rawJson = Objects.requireNonNull(rawJson, "rawJson must not be null"); + } + + @Override + public void writeTo(JsonSerializer serializer) { + serializer.writeFieldName(key); + serializer.writeRaw(rawJson); + } + + @Override + public String toString() { + return key + "=" + rawJson; + } + +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/KeyValueArgument.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/KeyValueArgument.java new file mode 100644 index 000000000..54648d99e --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/KeyValueArgument.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.argument; + +import java.util.Objects; + +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * See {@link StructuredArguments#entry(String, Object)} + */ +class KeyValueArgument implements StructuredArgument { + private final String key; + private final Object value; + + public KeyValueArgument(String key, Object value) { + this.key = Objects.requireNonNull(key, "Key must not be null"); + this.value = value; + } + + @Override + public void writeTo(JsonSerializer serializer) { + serializer.writeObjectField(key, value); + } + + @Override + public String toString() { + return key + "=" + StructuredArguments.toString(value); + } + + public String getKey() { + return key; + } + + public Object getValue() { + return value; + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/MapArgument.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/MapArgument.java new file mode 100644 index 000000000..1155fa93b --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/MapArgument.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.argument; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * See {@link StructuredArguments#entries(Map)} + */ +class MapArgument implements StructuredArgument { + private final Map<?, ?> map; + + public MapArgument(Map<?, ?> map) { + if (map != null) { + this.map = new HashMap<>(map); + } else { + this.map = null; + } + } + + @Override + public void writeTo(JsonSerializer serializer) { + if (map != null) { + for (Iterator<? extends Map.Entry<?, ?>> entries = map.entrySet().iterator(); entries.hasNext();) { + Map.Entry<?, ?> entry = entries.next(); + serializer.writeObjectField(String.valueOf(entry.getKey()), entry.getValue()); + // If the map has more than one entry, we need to print a (comma) separator to avoid breaking the JSON + if (entries.hasNext()) { + serializer.writeSeparator(); + } + } + } + } + + @Override + public String toString() { + return String.valueOf(map); + } + +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArgument.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArgument.java new file mode 100644 index 000000000..00cc651eb --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArgument.java @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.argument; + +import java.io.IOException; + +import org.slf4j.Logger; + +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * A wrapper for an argument passed to a log method (e.g. {@link Logger#info(String, Object...)}) + * that adds data to the JSON event. + */ +public interface StructuredArgument { + /** + * Writes the data associated with this argument to the given {@link JsonSerializer}. + * + * @param serializer + * the {@link JsonSerializer} to produce JSON content + * @throws IOException + * if an I/O error occurs + */ + void writeTo(JsonSerializer serializer) throws IOException; + + /** + * Writes the data associated with this argument to a {@link String} to be + * included in a log event's formatted message (via parameter substitution). + * <p> + * Note that this will only be included in the log event's formatted + * message if the message format includes a parameter for this argument (using {}). + * + * @return String representation of the data associated with this argument + */ + String toString(); + +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArguments.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArguments.java new file mode 100644 index 000000000..f56a42ea3 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArguments.java @@ -0,0 +1,193 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.argument; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; + +/** + * Factory for creating {@link StructuredArgument}s. + * Inspired from the StructuredArgument of logstash-logback-encoder. + */ +public class StructuredArguments { + + private static final Logger LOGGER = LoggerFactory.getLogger(StructuredArguments.class); + + /** + * Set of reserved keys that should not be used in structured arguments. + * When a reserved key is used, the method will return null. + */ + private static final Set<String> RESERVED_KEYS = Stream + .concat(PowertoolsLoggedFields.stringValues().stream(), + List.of("message", "level", "timestamp", "error").stream()) + .collect(Collectors.toSet()); + + private StructuredArguments() { + // nothing to do, use static methods only + } + + /** + * Checks if the provided key is a reserved key. + * If the key is reserved, logs a warning message. + * + * @param key the key to check + * @return true if the key is reserved, false otherwise + */ + private static boolean isReservedKey(String key) { + if (key != null && RESERVED_KEYS.contains(key)) { + LOGGER.warn( + "Attempted to use reserved key '{}' in structured argument. This key will be ignored.", key); + return true; + } + return false; + } + + /** + * Adds "key": "value" to the JSON structure and "key=value" to the formatted message. + * Returns null if the key is a reserved key. + * + * @param key the field name + * @param value the value associated with the key (can be any kind of object) + * @return a {@link StructuredArgument} populated with the data, or null if key is reserved + */ + public static StructuredArgument entry(String key, Object value) { + if (isReservedKey(key)) { + return null; + } + return new KeyValueArgument(key, value); + } + + /** + * Adds a "key": "value" to the JSON structure for each entry in the map + * and {@code map.toString()} to the formatted message. + * If the map contains any reserved keys, those entries will be filtered out. + * + * @param map {@link Map} holding the key/value pairs + * @return a {@link MapArgument} populated with the data, with reserved keys filtered out + */ + public static StructuredArgument entries(Map<?, ?> map) { + if (map == null) { + return null; + } + + // Create a new map without reserved keys + Map<Object, Object> filteredMap = new java.util.HashMap<>(); + for (Map.Entry<?, ?> entry : map.entrySet()) { + if (entry.getKey() != null) { + String keyStr = String.valueOf(entry.getKey()); + if (!isReservedKey(keyStr)) { + filteredMap.put(entry.getKey(), entry.getValue()); + } + } + } + + return new MapArgument(filteredMap); + } + + /** + * Adds a field to the JSON structure with key as the key and where value + * is a JSON array of objects AND a string version of the array to the formatted message: + * {@code "key": [value, value]} + * Returns null if the key is a reserved key. + * + * @param key the field name + * @param values elements of the array associated with the key + * @return an {@link ArrayArgument} populated with the data, or null if key is reserved + */ + public static StructuredArgument array(String key, Object... values) { + if (isReservedKey(key)) { + return null; + } + return new ArrayArgument(key, values); + } + + /** + * Adds the {@code rawJson} to the JSON structure and + * the {@code rawJson} to the formatted message. + * Returns null if the key is a reserved key. + * + * @param key the field name + * @param rawJson the raw JSON String + * @return a {@link JsonArgument} populated with the data, or null if key is reserved + */ + public static StructuredArgument json(String key, String rawJson) { + if (isReservedKey(key)) { + return null; + } + return new JsonArgument(key, rawJson); + } + + /** + * Format the argument into a string. + * + * This method mimics the slf4j behavior: + * array objects are formatted as array using {@link Arrays#toString}, + * non array object using {@link String#valueOf}. + * + * <p>See org.slf4j.helpers.MessageFormatter#deeplyAppendParameter(StringBuilder, Object, Map)} + * + * @param arg the argument to format + * @return formatted string version of the argument + */ + @SuppressWarnings("java:S106") + public static String toString(Object arg) { + + if (arg == null) { + return "null"; + } + + Class<?> argClass = arg.getClass(); + + try { + if (!argClass.isArray()) { + return String.valueOf(arg); + } else { + if (argClass == byte[].class) { + return Arrays.toString((byte[]) arg); + } else if (argClass == short[].class) { + return Arrays.toString((short[]) arg); + } else if (argClass == int[].class) { + return Arrays.toString((int[]) arg); + } else if (argClass == long[].class) { + return Arrays.toString((long[]) arg); + } else if (argClass == char[].class) { + return Arrays.toString((char[]) arg); + } else if (argClass == float[].class) { + return Arrays.toString((float[]) arg); + } else if (argClass == double[].class) { + return Arrays.toString((double[]) arg); + } else if (argClass == boolean[].class) { + return Arrays.toString((boolean[]) arg); + } else { + return Arrays.deepToString((Object[]) arg); + } + } + + } catch (Exception e) { + System.err.println("Failed toString() invocation on an object of type [" + argClass.getName() + "]"); + return "[FAILED toString()]"; + } + } + +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java deleted file mode 100644 index 17d09729f..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.logging.internal; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonRootName; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectWriter; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.Charset; -import java.util.LinkedHashMap; -import java.util.Map; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.ThreadContext; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; -import org.apache.logging.log4j.core.config.plugins.PluginElement; -import org.apache.logging.log4j.core.impl.Log4jLogEvent; -import org.apache.logging.log4j.core.impl.ThrowableProxy; -import org.apache.logging.log4j.core.jackson.XmlConstants; -import org.apache.logging.log4j.core.layout.AbstractStringLayout; -import org.apache.logging.log4j.core.lookup.StrSubstitutor; -import org.apache.logging.log4j.core.time.Instant; -import org.apache.logging.log4j.core.util.KeyValuePair; -import org.apache.logging.log4j.core.util.StringBuilderWriter; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.util.ReadOnlyStringMap; -import org.apache.logging.log4j.util.Strings; - -@Deprecated -abstract class AbstractJacksonLayoutCopy extends AbstractStringLayout { - - protected static final String DEFAULT_EOL = "\r\n"; - protected static final String COMPACT_EOL = Strings.EMPTY; - protected final String eol; - protected final ObjectWriter objectWriter; - protected final boolean compact; - protected final boolean complete; - protected final boolean includeNullDelimiter; - protected final ResolvableKeyValuePair[] additionalFields; - - @Deprecated - protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, - final Charset charset, - final boolean compact, final boolean complete, final boolean eventEol, - final Serializer headerSerializer, - final Serializer footerSerializer) { - this(config, objectWriter, charset, compact, complete, eventEol, headerSerializer, footerSerializer, false); - } - - @Deprecated - protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, - final Charset charset, - final boolean compact, final boolean complete, final boolean eventEol, - final Serializer headerSerializer, - final Serializer footerSerializer, final boolean includeNullDelimiter) { - this(config, objectWriter, charset, compact, complete, eventEol, null, headerSerializer, footerSerializer, - includeNullDelimiter, null); - } - - protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, - final Charset charset, - final boolean compact, final boolean complete, final boolean eventEol, - final String endOfLine, final Serializer headerSerializer, - final Serializer footerSerializer, final boolean includeNullDelimiter, - final KeyValuePair[] additionalFields) { - super(config, charset, headerSerializer, footerSerializer); - this.objectWriter = objectWriter; - this.compact = compact; - this.complete = complete; - this.eol = endOfLine != null ? endOfLine : compact && !eventEol ? COMPACT_EOL : DEFAULT_EOL; - this.includeNullDelimiter = includeNullDelimiter; - this.additionalFields = prepareAdditionalFields(config, additionalFields); - } - - protected static boolean valueNeedsLookup(final String value) { - return value != null && value.contains("${"); - } - - private static ResolvableKeyValuePair[] prepareAdditionalFields(final Configuration config, - final KeyValuePair[] additionalFields) { - if (additionalFields == null || additionalFields.length == 0) { - // No fields set - return ResolvableKeyValuePair.EMPTY_ARRAY; - } - - // Convert to specific class which already determines whether values needs lookup during serialization - final ResolvableKeyValuePair[] resolvableFields = new ResolvableKeyValuePair[additionalFields.length]; - - for (int i = 0; i < additionalFields.length; i++) { - final ResolvableKeyValuePair resolvable = - resolvableFields[i] = new ResolvableKeyValuePair(additionalFields[i]); - - // Validate - if (config == null && resolvable.valueNeedsLookup) { - throw new IllegalArgumentException( - "configuration needs to be set when there are additional fields with variables"); - } - } - - return resolvableFields; - } - - private static LogEvent convertMutableToLog4jEvent(final LogEvent event) { - return event instanceof Log4jLogEvent ? event : Log4jLogEvent.createMemento(event); - } - - /** - * Formats a {@link org.apache.logging.log4j.core.LogEvent}. - * - * @param event The LogEvent. - * @return The XML representation of the LogEvent. - */ - @Override - public String toSerializable(final LogEvent event) { - final StringBuilderWriter writer = new StringBuilderWriter(); - try { - toSerializable(event, writer); - return writer.toString(); - } catch (final IOException e) { - // Should this be an ISE or IAE? - LOGGER.error(e); - return Strings.EMPTY; - } - } - - protected Object wrapLogEvent(final LogEvent event) { - if (additionalFields.length > 0) { - // Construct map for serialization - note that we are intentionally using original LogEvent - final Map<String, String> additionalFieldsMap = resolveAdditionalFields(event); - // This class combines LogEvent with AdditionalFields during serialization - return new LogEventWithAdditionalFields(event, additionalFieldsMap); - } else if (event instanceof Message) { - // If the LogEvent implements the Messagee interface Jackson will not treat is as a LogEvent. - return new ReadOnlyLogEventWrapper(event); - } else { - // No additional fields, return original object - return event; - } - } - - private Map<String, String> resolveAdditionalFields(final LogEvent logEvent) { - // Note: LinkedHashMap retains order - final Map<String, String> additionalFieldsMap = new LinkedHashMap<>(additionalFields.length); - final StrSubstitutor strSubstitutor = configuration.getStrSubstitutor(); - - // Go over each field - for (final ResolvableKeyValuePair pair : additionalFields) { - if (pair.valueNeedsLookup) { - // Resolve value - additionalFieldsMap.put(pair.key, strSubstitutor.replace(logEvent, pair.value)); - } else { - // Plain text value - additionalFieldsMap.put(pair.key, pair.value); - } - } - - return additionalFieldsMap; - } - - public void toSerializable(final LogEvent event, final Writer writer) - throws JsonGenerationException, JsonMappingException, IOException { - objectWriter.writeValue(writer, wrapLogEvent(convertMutableToLog4jEvent(event))); - writer.write(eol); - if (includeNullDelimiter) { - writer.write('\0'); - } - markEvent(); - } - - public static abstract class Builder<B extends Builder<B>> extends AbstractStringLayout.Builder<B> { - - @PluginBuilderAttribute - private boolean eventEol; - - @PluginBuilderAttribute - private String endOfLine; - - @PluginBuilderAttribute - private boolean compact; - - @PluginBuilderAttribute - private boolean complete; - - @PluginBuilderAttribute - private boolean locationInfo; - - @PluginBuilderAttribute - private boolean properties; - - @PluginBuilderAttribute - private boolean includeStacktrace = true; - - @PluginBuilderAttribute - private boolean stacktraceAsString = false; - - @PluginBuilderAttribute - private boolean includeNullDelimiter = false; - - @PluginBuilderAttribute - private boolean includeTimeMillis = false; - - @PluginElement("AdditionalField") - private KeyValuePair[] additionalFields; - - protected String toStringOrNull(final byte[] header) { - return header == null ? null : new String(header, Charset.defaultCharset()); - } - - public boolean getEventEol() { - return eventEol; - } - - public B setEventEol(final boolean eventEol) { - this.eventEol = eventEol; - return asBuilder(); - } - - public String getEndOfLine() { - return endOfLine; - } - - public B setEndOfLine(final String endOfLine) { - this.endOfLine = endOfLine; - return asBuilder(); - } - - public boolean isCompact() { - return compact; - } - - public B setCompact(final boolean compact) { - this.compact = compact; - return asBuilder(); - } - - public boolean isComplete() { - return complete; - } - - public B setComplete(final boolean complete) { - this.complete = complete; - return asBuilder(); - } - - public boolean isLocationInfo() { - return locationInfo; - } - - public B setLocationInfo(final boolean locationInfo) { - this.locationInfo = locationInfo; - return asBuilder(); - } - - public boolean isProperties() { - return properties; - } - - public B setProperties(final boolean properties) { - this.properties = properties; - return asBuilder(); - } - - /** - * If "true", includes the stacktrace of any Throwable in the generated data, defaults to "true". - * - * @return If "true", includes the stacktrace of any Throwable in the generated data, defaults to "true". - */ - public boolean isIncludeStacktrace() { - return includeStacktrace; - } - - /** - * If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". - * - * @param includeStacktrace If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". - * @return this builder - */ - public B setIncludeStacktrace(final boolean includeStacktrace) { - this.includeStacktrace = includeStacktrace; - return asBuilder(); - } - - public boolean isStacktraceAsString() { - return stacktraceAsString; - } - - /** - * Whether to format the stacktrace as a string, and not a nested object (optional, defaults to false). - * - * @return this builder - */ - public B setStacktraceAsString(final boolean stacktraceAsString) { - this.stacktraceAsString = stacktraceAsString; - return asBuilder(); - } - - public boolean isIncludeNullDelimiter() { - return includeNullDelimiter; - } - - /** - * Whether to include NULL byte as delimiter after each event (optional, default to false). - * - * @return this builder - */ - public B setIncludeNullDelimiter(final boolean includeNullDelimiter) { - this.includeNullDelimiter = includeNullDelimiter; - return asBuilder(); - } - - public boolean isIncludeTimeMillis() { - return includeTimeMillis; - } - - /** - * Whether to include the timestamp (in addition to the Instant) (optional, default to false). - * - * @return this builder - */ - public B setIncludeTimeMillis(final boolean includeTimeMillis) { - this.includeTimeMillis = includeTimeMillis; - return asBuilder(); - } - - public KeyValuePair[] getAdditionalFields() { - return additionalFields; - } - - /** - * Additional fields to set on each log event. - * - * @return this builder - */ - public B setAdditionalFields(final KeyValuePair[] additionalFields) { - this.additionalFields = additionalFields; - return asBuilder(); - } - } - - @JsonRootName(XmlConstants.ELT_EVENT) - public static class LogEventWithAdditionalFields { - - private final Object logEvent; - private final Map<String, String> additionalFields; - - public LogEventWithAdditionalFields(final Object logEvent, final Map<String, String> additionalFields) { - this.logEvent = logEvent; - this.additionalFields = additionalFields; - } - - @JsonUnwrapped - public Object getLogEvent() { - return logEvent; - } - - @JsonAnyGetter - @SuppressWarnings("unused") - public Map<String, String> getAdditionalFields() { - return additionalFields; - } - } - - protected static class ResolvableKeyValuePair { - - /** - * The empty array. - */ - static final ResolvableKeyValuePair[] EMPTY_ARRAY = {}; - - final String key; - final String value; - final boolean valueNeedsLookup; - - ResolvableKeyValuePair(final KeyValuePair pair) { - this.key = pair.getKey(); - this.value = pair.getValue(); - this.valueNeedsLookup = AbstractJacksonLayoutCopy.valueNeedsLookup(this.value); - } - } - - private static class ReadOnlyLogEventWrapper implements LogEvent { - - @JsonIgnore - private final LogEvent event; - - public ReadOnlyLogEventWrapper(LogEvent event) { - this.event = event; - } - - @Override - public LogEvent toImmutable() { - return event.toImmutable(); - } - - @Override - public Map<String, String> getContextMap() { - return event.getContextMap(); - } - - @Override - public ReadOnlyStringMap getContextData() { - return event.getContextData(); - } - - @Override - public ThreadContext.ContextStack getContextStack() { - return event.getContextStack(); - } - - @Override - public String getLoggerFqcn() { - return event.getLoggerFqcn(); - } - - @Override - public Level getLevel() { - return event.getLevel(); - } - - @Override - public String getLoggerName() { - return event.getLoggerName(); - } - - @Override - public Marker getMarker() { - return event.getMarker(); - } - - @Override - public Message getMessage() { - return event.getMessage(); - } - - @Override - public long getTimeMillis() { - return event.getTimeMillis(); - } - - @Override - public Instant getInstant() { - return event.getInstant(); - } - - @Override - public StackTraceElement getSource() { - return event.getSource(); - } - - @Override - public String getThreadName() { - return event.getThreadName(); - } - - @Override - public long getThreadId() { - return event.getThreadId(); - } - - @Override - public int getThreadPriority() { - return event.getThreadPriority(); - } - - @Override - public Throwable getThrown() { - return event.getThrown(); - } - - @Override - public ThrowableProxy getThrownProxy() { - return event.getThrownProxy(); - } - - @Override - public boolean isEndOfBatch() { - return event.isEndOfBatch(); - } - - @Override - public void setEndOfBatch(boolean endOfBatch) { - - } - - @Override - public boolean isIncludeLocation() { - return event.isIncludeLocation(); - } - - @Override - public void setIncludeLocation(boolean locationRequired) { - - } - - @Override - public long getNanoTime() { - return event.getNanoTime(); - } - } -} \ No newline at end of file diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/BufferManager.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/BufferManager.java new file mode 100644 index 000000000..d81d1fd31 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/BufferManager.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +/** + * Interface for logging managers that support buffer operations. + * This extends the logging framework capabilities with buffer-specific functionality. + */ +public interface BufferManager { + /** + * Flushes the log buffer for the current Lambda execution. + * This method will flush any buffered logs to the target appender. + */ + void flushBuffer(); + + /** + * Clears the log buffer for the current Lambda execution. + * This method will discard any buffered logs without outputting them. + */ + void clearBuffer(); +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLoggingManager.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLoggingManager.java new file mode 100644 index 000000000..ed2c14c38 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLoggingManager.java @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import org.slf4j.Logger; +import org.slf4j.event.Level; + +/** + * When no LoggingManager is found, setting a default one with no action on logging implementation + * Powertools cannot change the log level based on the environment variable, will use the logger configuration + */ +public class DefaultLoggingManager implements LoggingManager { + + @Override + public void setLogLevel(Level logLevel) { + // do nothing + } + + @Override + public Level getLogLevel(Logger logger) { + return Level.ERROR; + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java deleted file mode 100644 index 6b568be30..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.logging.internal; - -import com.fasterxml.jackson.core.PrettyPrinter; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; -import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import java.util.HashSet; -import java.util.Set; -import org.apache.logging.log4j.core.impl.Log4jLogEvent; -import org.apache.logging.log4j.core.jackson.JsonConstants; -import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper; - -@Deprecated -abstract class JacksonFactoryCopy { - - abstract protected String getPropertyNameForTimeMillis(); - - abstract protected String getPropertyNameForInstant(); - - abstract protected String getPropertNameForContextMap(); - - abstract protected String getPropertNameForSource(); - - abstract protected String getPropertNameForNanoTime(); - - abstract protected PrettyPrinter newCompactPrinter(); - - abstract protected ObjectMapper newObjectMapper(); - - abstract protected PrettyPrinter newPrettyPrinter(); - - ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact) { - return newWriter(locationInfo, properties, compact, false); - } - - ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact, - final boolean includeMillis) { - final SimpleFilterProvider filters = new SimpleFilterProvider(); - final Set<String> except = new HashSet<>(3); - if (!locationInfo) { - except.add(this.getPropertNameForSource()); - } - if (!properties) { - except.add(this.getPropertNameForContextMap()); - } - if (includeMillis) { - except.add(getPropertyNameForInstant()); - } else { - except.add(getPropertyNameForTimeMillis()); - } - except.add(this.getPropertNameForNanoTime()); - filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except)); - final ObjectWriter writer = - this.newObjectMapper().writer(compact ? this.newCompactPrinter() : this.newPrettyPrinter()); - return writer.with(filters); - } - - static class JSON extends JacksonFactoryCopy { - - private final boolean encodeThreadContextAsList; - private final boolean includeStacktrace; - private final boolean stacktraceAsString; - private final boolean objectMessageAsJsonObject; - - public JSON(final boolean encodeThreadContextAsList, final boolean includeStacktrace, - final boolean stacktraceAsString, final boolean objectMessageAsJsonObject) { - this.encodeThreadContextAsList = encodeThreadContextAsList; - this.includeStacktrace = includeStacktrace; - this.stacktraceAsString = stacktraceAsString; - this.objectMessageAsJsonObject = objectMessageAsJsonObject; - } - - @Override - protected String getPropertNameForContextMap() { - return JsonConstants.ELT_CONTEXT_MAP; - } - - @Override - protected String getPropertyNameForTimeMillis() { - return JsonConstants.ELT_TIME_MILLIS; - } - - @Override - protected String getPropertyNameForInstant() { - return JsonConstants.ELT_INSTANT; - } - - @Override - protected String getPropertNameForSource() { - return JsonConstants.ELT_SOURCE; - } - - @Override - protected String getPropertNameForNanoTime() { - return JsonConstants.ELT_NANO_TIME; - } - - @Override - protected PrettyPrinter newCompactPrinter() { - return new MinimalPrettyPrinter(); - } - - @Override - protected ObjectMapper newObjectMapper() { - return new Log4jJsonObjectMapper(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, - objectMessageAsJsonObject); - } - - @Override - protected PrettyPrinter newPrettyPrinter() { - return new DefaultPrettyPrinter(); - } - - } - -} \ No newline at end of file diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JsonSerializer.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JsonSerializer.java new file mode 100644 index 000000000..c69789519 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JsonSerializer.java @@ -0,0 +1,604 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.BooleanNode; +import com.fasterxml.jackson.databind.node.MissingNode; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.NumericNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.POJONode; +import com.fasterxml.jackson.databind.node.TextNode; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +/** + * A simple JSON serializer. + * Used internally for json serialization, not to be used externally. + * We do not use Jackson as we need to serialize each fields of the log event individually. + * Mainly used by logback as log4j is using its own JsonWriter + */ +public class JsonSerializer implements AutoCloseable { + + private final StringBuilder builder; + + public JsonSerializer(StringBuilder builder) { + super(); + if (builder == null) { + throw new IllegalArgumentException("StringBuilder cannot be null"); + } + this.builder = builder; + } + + public void writeStartArray() { + builder.append('['); + } + + public void writeEndArray() { + builder.append(']'); + } + + public void writeStartObject() { + builder.append('{'); + } + + public void writeEndObject() { + builder.append('}'); + } + + public void writeSeparator() { + writeRaw(','); + } + + public void writeFieldName(String name) { + Objects.requireNonNull(name, "field name must not be null"); + writeString(name); + writeRaw(':'); + } + + public void writeString(String text) { + if (text == null) { + writeNull(); + } else { + // Escape special characters to avoid breaking JSON format + String escaped = text.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t") + .replace("\b", "\\b") + .replace("\f", "\\f"); + builder.append("\"").append(escaped).append("\""); + } + } + + public void writeRaw(String text) { + builder.append(text); + } + + public void writeRaw(char c) { + builder.append(c); + } + + public void writeNumber(short v) { + builder.append(v); + } + + public void writeNumber(int v) { + builder.append(v); + } + + public void writeNumber(long v) { + builder.append(v); + } + + public void writeNumber(BigInteger v) { + builder.append(v); + } + + public void writeNumber(double v) { + builder.append(v); + } + + public void writeNumber(float v) { + builder.append(v); + } + + public void writeNumber(BigDecimal v) { + builder.append(v.toPlainString()); + } + + public void writeBoolean(boolean state) { + builder.append(state); + } + + public void writeArray(final char[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + builder.append('\''); + builder.append(items[itemIndex]); + builder.append('\''); + } + writeEndArray(); + } + } + + public void writeArray(final boolean[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final boolean item = items[itemIndex]; + writeBoolean(item); + } + writeEndArray(); + } + } + + public void writeArray(final byte[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final byte item = items[itemIndex]; + writeNumber(item); + } + writeEndArray(); + } + } + + public void writeArray(final short[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final short item = items[itemIndex]; + writeNumber(item); + } + writeEndArray(); + } + } + + public void writeArray(final int[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final int item = items[itemIndex]; + writeNumber(item); + } + writeEndArray(); + } + } + + public void writeArray(final long[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final long item = items[itemIndex]; + writeNumber(item); + } + writeEndArray(); + } + } + + public void writeArray(final float[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final float item = items[itemIndex]; + writeNumber(item); + } + writeEndArray(); + } + } + + public void writeArray(final double[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final double item = items[itemIndex]; + writeNumber(item); + } + writeEndArray(); + } + } + + public void writeArray(final Object[] items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final Object item = items[itemIndex]; + writeObject(item); + } + writeEndArray(); + } + } + + public void writeNull() { + builder.append("null"); + } + + public void writeArray(final List<?> items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) { + if (itemIndex > 0) { + writeSeparator(); + } + final Object item = items.get(itemIndex); + writeObject(item); + } + writeEndArray(); + } + } + + public void writeArray(final Collection<?> items) { + if (items == null) { + writeNull(); + } else { + writeStartArray(); + Iterator<?> iterator = items.iterator(); + while (iterator.hasNext()) { + writeObject(iterator.next()); + if (iterator.hasNext()) { + writeSeparator(); + } + } + writeEndArray(); + } + } + + public void writeMap(final Map<?, ?> map) { + if (map == null) { + writeNull(); + } else { + writeStartObject(); + for (Iterator<? extends Map.Entry<?, ?>> entries = map.entrySet().iterator(); entries.hasNext();) { + Map.Entry<?, ?> entry = entries.next(); + writeObjectField(String.valueOf(entry.getKey()), entry.getValue()); + if (entries.hasNext()) { + builder.append(','); + } + } + writeEndObject(); + } + } + + public void writeObject(Object value) { + + // null + if (value == null) { + writeNull(); + } + + else if (value instanceof String) { + writeString((String) value); + } + + // number & boolean + else if (value instanceof Number) { + Number n = (Number) value; + if (n instanceof Integer) { + writeNumber(n.intValue()); + } else if (n instanceof Long) { + writeNumber(n.longValue()); + } else if (n instanceof Double) { + writeNumber(n.doubleValue()); + } else if (n instanceof Float) { + writeNumber(n.floatValue()); + } else if (n instanceof Short) { + writeNumber(n.shortValue()); + } else if (n instanceof Byte) { + writeNumber(n.byteValue()); + } else if (n instanceof BigInteger) { + writeNumber((BigInteger) n); + } else if (n instanceof BigDecimal) { + writeNumber((BigDecimal) n); + } else if (n instanceof AtomicInteger) { + writeNumber(((AtomicInteger) n).get()); + } else if (n instanceof AtomicLong) { + writeNumber(((AtomicLong) n).get()); + } + } else if (value instanceof Boolean) { + writeBoolean((Boolean) value); + } else if (value instanceof AtomicBoolean) { + writeBoolean(((AtomicBoolean) value).get()); + } + + // list & collection + else if (value instanceof List) { + final List<?> list = (List<?>) value; + writeArray(list); + } else if (value instanceof Collection) { + final Collection<?> collection = (Collection<?>) value; + writeArray(collection); + } + + // map + else if (value instanceof Map) { + final Map<?, ?> map = (Map<?, ?>) value; + writeMap(map); + } + + // arrays + else if (value instanceof char[]) { + final char[] charValues = (char[]) value; + writeArray(charValues); + } else if (value instanceof boolean[]) { + final boolean[] booleanValues = (boolean[]) value; + writeArray(booleanValues); + } else if (value instanceof byte[]) { + final byte[] byteValues = (byte[]) value; + writeArray(byteValues); + } else if (value instanceof short[]) { + final short[] shortValues = (short[]) value; + writeArray(shortValues); + } else if (value instanceof int[]) { + final int[] intValues = (int[]) value; + writeArray(intValues); + } else if (value instanceof long[]) { + final long[] longValues = (long[]) value; + writeArray(longValues); + } else if (value instanceof float[]) { + final float[] floatValues = (float[]) value; + writeArray(floatValues); + } else if (value instanceof double[]) { + final double[] doubleValues = (double[]) value; + writeArray(doubleValues); + } else if (value instanceof Object[]) { + final Object[] values = (Object[]) value; + writeArray(values); + } + + else if (value instanceof JsonNode) { + JsonNode node = (JsonNode) value; + + switch (node.getNodeType()) { + case NULL: + case MISSING: + writeNull(); + break; + + case STRING: + writeString(node.asText()); + break; + + case BOOLEAN: + writeBoolean(node.asBoolean()); + break; + + case NUMBER: + if (node.isInt()) { + writeNumber(node.intValue()); + break; + } + if (node.isLong()) { + writeNumber(node.longValue()); + break; + } + if (node.isShort()) { + writeNumber(node.shortValue()); + break; + } + if (node.isDouble()) { + writeNumber(node.doubleValue()); + break; + } + if (node.isFloat()) { + writeNumber(node.floatValue()); + break; + } + if (node.isBigDecimal()) { + writeNumber(node.decimalValue()); + break; + } + if (node.isBigInteger()) { + writeNumber(node.bigIntegerValue()); + break; + } + break; + case OBJECT: + case POJO: + writeStartObject(); + for (Iterator<Map.Entry<String, JsonNode>> entries = node.fields(); entries.hasNext();) { + Map.Entry<String, JsonNode> entry = entries.next(); + writeObjectField(entry.getKey(), entry.getValue()); + if (entries.hasNext()) { + builder.append(','); + } + } + writeEndObject(); + return; + + case ARRAY: + ArrayNode arrayNode = (ArrayNode) node; + writeStartArray(); + for (Iterator<JsonNode> elements = arrayNode.elements(); elements.hasNext();) { + writeObject(elements.next()); + if (elements.hasNext()) { + builder.append(','); + } + } + writeEndArray(); + return; + + default: + break; + } + } else { + try { + // default: try to write object as JSON + writeRaw(JsonConfig.get().getObjectMapper().writeValueAsString(value)); + } catch (Exception e) { + // last chance: toString + writeString(value.toString()); + } + } + } + + public void writeObjectField(String key, Object value) { + writeFieldName(key); + writeObject(value); + } + + public void writeBooleanField(String key, boolean value) { + writeFieldName(key); + writeBoolean(value); + } + + public void writeNullField(String key) { + writeFieldName(key); + writeNull(); + } + + public void writeNumberField(String key, int value) { + writeFieldName(key); + writeNumber(value); + } + + public void writeNumberField(String key, float value) { + writeFieldName(key); + writeNumber(value); + } + + public void writeNumberField(String key, short value) { + writeFieldName(key); + writeNumber(value); + } + + public void writeNumberField(String key, long value) { + writeFieldName(key); + writeNumber(value); + } + + public void writeNumberField(String key, BigInteger value) { + writeFieldName(key); + writeNumber(value); + } + + public void writeNumberField(String key, double value) { + writeFieldName(key); + writeNumber(value); + } + + public void writeNumberField(String key, BigDecimal value) { + writeFieldName(key); + writeNumber(value); + } + + public void writeStringField(String key, String value) { + writeFieldName(key); + writeString(value); + } + + public void writeTree(TreeNode rootNode) { + if (rootNode == null) { + writeNull(); + } else if (rootNode instanceof TextNode) { + writeString(((TextNode) rootNode).asText()); + } else if (rootNode instanceof BooleanNode) { + writeBoolean(((BooleanNode) rootNode).asBoolean()); + } else if (rootNode instanceof NumericNode) { + NumericNode numericNode = (NumericNode) rootNode; + if (numericNode.isInt()) { + writeNumber(numericNode.intValue()); + } else if (numericNode.isLong()) { + writeNumber(numericNode.longValue()); + } else if (numericNode.isShort()) { + writeNumber(numericNode.shortValue()); + } else if (numericNode.isDouble()) { + writeNumber(numericNode.doubleValue()); + } else if (numericNode.isFloat()) { + writeNumber(numericNode.floatValue()); + } else if (numericNode.isBigDecimal()) { + writeNumber(numericNode.decimalValue()); + } else if (numericNode.isBigInteger()) { + writeNumber(numericNode.bigIntegerValue()); + } + } else if (rootNode instanceof ArrayNode) { + ArrayNode arrayNode = (ArrayNode) rootNode; + writeObject(arrayNode); + } else if (rootNode instanceof ObjectNode) { + ObjectNode objectNode = (ObjectNode) rootNode; + writeObject(objectNode); + } else if (rootNode instanceof NullNode || rootNode instanceof MissingNode) { + writeNull(); + } else if (rootNode instanceof POJONode) { + writeObject(((POJONode) rootNode).getPojo()); + } + } + + @Override + public void close() { + // nothing to do + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/KeyBuffer.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/KeyBuffer.java new file mode 100644 index 000000000..3510a544d --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/KeyBuffer.java @@ -0,0 +1,95 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * Thread-safe buffer that stores events by key with size-based eviction. + * + * <p>Maintains separate queues per key. When buffer size exceeds maxBytes, + * oldest events are evicted FIFO. Events larger than maxBytes are rejected. + * + * @param <K> key type for buffering + * @param <T> event type to buffer + */ +public class KeyBuffer<K, T> { + + private final Map<K, Deque<T>> keyBufferCache = new ConcurrentHashMap<>(); + private final Map<K, Boolean> overflowTriggered = new ConcurrentHashMap<>(); + private final int maxBytes; + private final Function<T, Integer> sizeCalculator; + private final Runnable overflowWarningLogger; + + @SuppressWarnings("java:S106") // Using System.err to avoid circular dependency with logging implementation + public KeyBuffer(int maxBytes, Function<T, Integer> sizeCalculator) { + this(maxBytes, sizeCalculator, () -> System.err.println("WARN [" + KeyBuffer.class.getSimpleName() + + "] - Some logs are not displayed because they were evicted from the buffer. Increase buffer size to store more logs in the buffer.")); + } + + public KeyBuffer(int maxBytes, Function<T, Integer> sizeCalculator, Runnable overflowWarningLogger) { + this.maxBytes = maxBytes; + this.sizeCalculator = sizeCalculator; + this.overflowWarningLogger = overflowWarningLogger; + } + + public void add(K key, T event) { + int eventSize = sizeCalculator.apply(event); + // Immediately reject events larger than the whole buffer-size to avoid evicting all elements. + if (eventSize > maxBytes) { + overflowTriggered.put(key, true); + return; + } + + Deque<T> buffer = keyBufferCache.computeIfAbsent(key, k -> new ArrayDeque<>()); + synchronized (buffer) { + buffer.add(event); + while (getBufferSize(buffer) > maxBytes && !buffer.isEmpty()) { + overflowTriggered.put(key, true); + buffer.removeFirst(); + } + } + } + + public Deque<T> removeAll(K key) { + logOverflowWarningIfNeeded(key); + Deque<T> buffer = keyBufferCache.remove(key); + if (buffer != null) { + synchronized (buffer) { + return new ArrayDeque<>(buffer); + } + } + return buffer; + } + + public void clear(K key) { + keyBufferCache.remove(key); + overflowTriggered.remove(key); + } + + private void logOverflowWarningIfNeeded(K key) { + if (Boolean.TRUE.equals(overflowTriggered.remove(key))) { + overflowWarningLogger.run(); + } + } + + private int getBufferSize(Deque<T> buffer) { + return buffer.stream().mapToInt(sizeCalculator::apply).sum(); + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java deleted file mode 100644 index fd646ab50..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.logging.internal; - -import static java.time.Instant.ofEpochMilli; -import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonRootName; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import org.apache.logging.log4j.core.Layout; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.DefaultConfiguration; -import org.apache.logging.log4j.core.config.Node; -import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; -import org.apache.logging.log4j.core.jackson.XmlConstants; -import org.apache.logging.log4j.core.layout.PatternLayout; -import org.apache.logging.log4j.core.util.KeyValuePair; -import org.apache.logging.log4j.util.Strings; - -/*** - * Note: The LambdaJsonLayout should be considered to be deprecated. Please use JsonTemplateLayout instead. - */ -@Deprecated -@Plugin(name = "LambdaJsonLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true) -public final class LambdaJsonLayout extends AbstractJacksonLayoutCopy { - static final String CONTENT_TYPE = "application/json"; - private static final String DEFAULT_FOOTER = "]"; - private static final String DEFAULT_HEADER = "["; - - private LambdaJsonLayout(final Configuration config, final boolean locationInfo, final boolean properties, - final boolean encodeThreadContextAsList, - final boolean complete, final boolean compact, final boolean eventEol, - final String headerPattern, final String footerPattern, final Charset charset, - final boolean includeStacktrace, final boolean stacktraceAsString, - final boolean includeNullDelimiter, - final KeyValuePair[] additionalFields, final boolean objectMessageAsJsonObject) { - super(config, new JacksonFactoryCopy.JSON(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, - objectMessageAsJsonObject).newWriter( - locationInfo, properties, compact), - charset, compact, complete, eventEol, - null, - PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern) - .setDefaultPattern(DEFAULT_HEADER).build(), - PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern) - .setDefaultPattern(DEFAULT_FOOTER).build(), - includeNullDelimiter, - additionalFields); - } - - @PluginBuilderFactory - public static <B extends Builder<B>> B newBuilder() { - return new Builder<B>().asBuilder(); - } - - /** - * Creates a JSON Layout using the default settings. Useful for testing. - * - * @return A JSON Layout. - */ - public static LambdaJsonLayout createDefaultLayout() { - return new LambdaJsonLayout(new DefaultConfiguration(), false, false, false, false, false, false, - DEFAULT_HEADER, DEFAULT_FOOTER, StandardCharsets.UTF_8, true, false, false, null, false); - } - - /** - * Returns appropriate JSON header. - * - * @return a byte array containing the header, opening the JSON array. - */ - @Override - public byte[] getHeader() { - if (!this.complete) { - return null; - } - final StringBuilder buf = new StringBuilder(); - final String str = serializeToString(getHeaderSerializer()); - if (str != null) { - buf.append(str); - } - buf.append(this.eol); - return getBytes(buf.toString()); - } - - /** - * Returns appropriate JSON footer. - * - * @return a byte array containing the footer, closing the JSON array. - */ - @Override - public byte[] getFooter() { - if (!this.complete) { - return null; - } - final StringBuilder buf = new StringBuilder(); - buf.append(this.eol); - final String str = serializeToString(getFooterSerializer()); - if (str != null) { - buf.append(str); - } - buf.append(this.eol); - return getBytes(buf.toString()); - } - - @Override - public Map<String, String> getContentFormat() { - final Map<String, String> result = new HashMap<>(); - result.put("version", "2.0"); - return result; - } - - /** - * @return The content type. - */ - @Override - public String getContentType() { - return CONTENT_TYPE + "; charset=" + this.getCharset(); - } - - @Override - public Object wrapLogEvent(final LogEvent event) { - Map<String, Object> additionalFieldsMap = resolveAdditionalFields(event); - // This class combines LogEvent with AdditionalFields during serialization - return new LogEventWithAdditionalFields(event, additionalFieldsMap); - } - - @Override - public void toSerializable(final LogEvent event, final Writer writer) throws IOException { - if (complete && eventCount > 0) { - writer.append(", "); - } - super.toSerializable(event, writer); - } - - private Map<String, Object> resolveAdditionalFields(LogEvent logEvent) { - // Note: LinkedHashMap retains order - final Map<String, Object> additionalFieldsMap = new LinkedHashMap<>(additionalFields.length); - - // Go over MDC - logEvent.getContextData().forEach((key, value) -> - { - if (Strings.isNotBlank(key) && value != null) { - additionalFieldsMap.put(key, value); - } - }); - - return additionalFieldsMap; - } - - public static class Builder<B extends Builder<B>> extends AbstractJacksonLayoutCopy.Builder<B> - implements org.apache.logging.log4j.core.util.Builder<LambdaJsonLayout> { - - @PluginBuilderAttribute - private boolean propertiesAsList; - - @PluginBuilderAttribute - private boolean objectMessageAsJsonObject; - - public Builder() { - super(); - setCharset(StandardCharsets.UTF_8); - } - - @Override - public LambdaJsonLayout build() { - final boolean encodeThreadContextAsList = isProperties() && propertiesAsList; - final String headerPattern = toStringOrNull(getHeader()); - final String footerPattern = toStringOrNull(getFooter()); - return new LambdaJsonLayout(getConfiguration(), isLocationInfo(), isProperties(), encodeThreadContextAsList, - isComplete(), isCompact(), getEventEol(), headerPattern, footerPattern, getCharset(), - isIncludeStacktrace(), isStacktraceAsString(), isIncludeNullDelimiter(), - getAdditionalFields(), getObjectMessageAsJsonObject()); - } - - public boolean isPropertiesAsList() { - return propertiesAsList; - } - - public B setPropertiesAsList(final boolean propertiesAsList) { - this.propertiesAsList = propertiesAsList; - return asBuilder(); - } - - public boolean getObjectMessageAsJsonObject() { - return objectMessageAsJsonObject; - } - - public B setObjectMessageAsJsonObject(final boolean objectMessageAsJsonObject) { - this.objectMessageAsJsonObject = objectMessageAsJsonObject; - return asBuilder(); - } - } - - @JsonRootName(XmlConstants.ELT_EVENT) - public static class LogEventWithAdditionalFields { - - private final LogEvent logEvent; - private final Map<String, Object> additionalFields; - - public LogEventWithAdditionalFields(LogEvent logEvent, Map<String, Object> additionalFields) { - this.logEvent = logEvent; - this.additionalFields = additionalFields; - } - - @JsonUnwrapped - public Object getLogEvent() { - return logEvent; - } - - @JsonAnyGetter - public Map<String, Object> getAdditionalFields() { - return additionalFields; - } - - @JsonGetter("timestamp") - public String getTimestamp() { - return ISO_ZONED_DATE_TIME.format( - ZonedDateTime.from(ofEpochMilli(logEvent.getTimeMillis()).atZone(ZoneId.systemDefault()))); - } - } -} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java index 4a98735af..272c4bff9 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java @@ -15,258 +15,230 @@ package software.amazon.lambda.powertools.logging.internal; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Optional.empty; -import static java.util.Optional.ofNullable; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.extractContext; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.getXrayTraceId; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnStreamHandler; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; -import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKey; -import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKeys; -import static software.amazon.lambda.powertools.logging.LoggingUtils.objectMapper; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.extractContext; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnStreamHandler; +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_LOG_ERROR; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_LOG_EVENT; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_LOG_RESPONSE; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.core.JsonPointer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.util.Map; -import java.util.Optional; -import java.util.Random; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.ThreadContext; -import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.config.Configurator; -import org.apache.logging.log4j.core.util.IOUtils; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclarePrecedence; import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MarkerFactory; + +import com.amazonaws.services.lambda.runtime.Context; + import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.logging.PowertoolsLogging; +import software.amazon.lambda.powertools.utilities.JsonConfig; @Aspect @DeclarePrecedence("*, software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect") public final class LambdaLoggingAspect { - private static final Logger LOG = LogManager.getLogger(LambdaLoggingAspect.class); - private static final Random SAMPLER = new Random(); - - private static final String LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); - private static final String SAMPLING_RATE = System.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE"); - - private static Level LEVEL_AT_INITIALISATION; + private static final Logger LOG = LoggerFactory.getLogger(LambdaLoggingAspect.class); + private static final LoggingManager LOGGING_MANAGER; static { - if (null != LOG_LEVEL) { - resetLogLevels(Level.getLevel(LOG_LEVEL)); - } - - LEVEL_AT_INITIALISATION = LOG.getLevel(); - } - - private static void resetLogLevels(Level logLevel) { - LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - Configurator.setAllLevels(LogManager.getRootLogger().getName(), logLevel); - ctx.updateLoggers(); + LOGGING_MANAGER = LoggingManagerRegistry.getLoggingManager(); } - @SuppressWarnings({"EmptyMethod"}) + @SuppressWarnings({ "EmptyMethod" }) @Pointcut("@annotation(logging)") public void callAt(Logging logging) { + // Pointcut method - body intentionally empty } + /** + * Main method of the aspect + */ @Around(value = "callAt(logging) && execution(@Logging * *.*(..))", argNames = "pjp,logging") public Object around(ProceedingJoinPoint pjp, - Logging logging) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); + Logging logging) throws Throwable { - setLogLevelBasedOnSamplingRate(pjp, logging); + boolean isOnRequestHandler = placedOnRequestHandler(pjp); + boolean isOnRequestStreamHandler = placedOnStreamHandler(pjp); - Context extractedContext = extractContext(pjp); + // Initialize logging using PowertoolsLogging + Context context = extractContext(pjp); + Object[] proceedArgs = pjp.getArgs(); - if (null != extractedContext) { - appendKeys(DefaultLambdaFields.values(extractedContext)); - appendKey("coldStart", isColdStart() ? "true" : "false"); - appendKey("service", serviceName()); + if (isHandlerMethod(pjp) && context != null) { + Object event = extractEventForCorrelationId(logging, isOnRequestHandler, isOnRequestStreamHandler, + proceedArgs); + PowertoolsLogging.initializeLogging(context, logging.samplingRate(), + logging.correlationIdPath().isEmpty() ? null : logging.correlationIdPath(), event); } - getXrayTraceId().ifPresent(xRayTraceId -> appendKey("xray_trace_id", xRayTraceId)); - - if (logging.logEvent()) { - proceedArgs = logEvent(pjp); + logEvent(pjp, logging, isOnRequestHandler, isOnRequestStreamHandler, proceedArgs); + + @SuppressWarnings("PMD.CloseResource") // Lambda-owned stream, not ours to close + OutputStream backupOutputStream = null; + if (isOnRequestStreamHandler) { + // To log the result of a RequestStreamHandler (OutputStream), we need to do the following: + // 1. backup a reference to the OutputStream provided by Lambda + // 2. create a temporary OutputStream and pass it to the handler method + // 3. retrieve this temporary stream to log it (if enabled) + // 4. write it back to the OutputStream provided by Lambda + backupOutputStream = prepareOutputStreamForLogging(logging, proceedArgs); } - if (!logging.correlationIdPath().isEmpty()) { - proceedArgs = captureCorrelationId(logging.correlationIdPath(), pjp); + Object lambdaFunctionResponse; + try { + lambdaFunctionResponse = pjp.proceed(proceedArgs); + } catch (Throwable t) { // NOPMD - AspectJ proceed() throws Throwable + handleException(pjp, logging, t); + throw t; + } finally { + PowertoolsLogging.clearState(logging.clearState()); } - Object proceed = pjp.proceed(proceedArgs); + logResponse(pjp, logging, lambdaFunctionResponse, isOnRequestHandler, isOnRequestStreamHandler, + backupOutputStream, proceedArgs); - if (logging.clearState()) { - ThreadContext.clearMap(); - } - - coldStartDone(); - return proceed; + return lambdaFunctionResponse; } - private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, - final Logging logging) { - double samplingRate = samplingRate(logging); - - if (isHandlerMethod(pjp)) { - - if (samplingRate < 0 || samplingRate > 1) { - LOG.debug("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", - samplingRate); - return; - } - - appendKey("samplingRate", String.valueOf(samplingRate)); + private void logEvent(ProceedingJoinPoint pjp, Logging logging, + boolean isOnRequestHandler, boolean isOnRequestStreamHandler, Object[] proceedArgs) { - if (samplingRate == 0) { - return; + if (logging.logEvent() || POWERTOOLS_LOG_EVENT) { + if (isOnRequestHandler) { + logRequestHandlerEvent(pjp, proceedArgs[0]); + } else if (isOnRequestStreamHandler) { + logRequestStreamHandlerEvent(pjp, proceedArgs); } + } + } - float sample = SAMPLER.nextFloat(); - - if (samplingRate > sample) { - resetLogLevels(Level.DEBUG); - - LOG.debug("Changed log level to DEBUG based on Sampling configuration. " + - "Sampling Rate: {}, Sampler Value: {}.", samplingRate, sample); - } else if (LEVEL_AT_INITIALISATION != LOG.getLevel()) { - resetLogLevels(LEVEL_AT_INITIALISATION); - } + @SuppressWarnings("java:S3457") + private void logRequestHandlerEvent(final ProceedingJoinPoint pjp, final Object event) { + Logger log = logger(pjp); + if (log.isInfoEnabled()) { + log.info("Handler Event", entry("event", event)); } } - private double samplingRate(final Logging logging) { - if (null != SAMPLING_RATE) { + @SuppressWarnings("java:S3457") + private void logRequestStreamHandlerEvent(final ProceedingJoinPoint pjp, Object[] args) { + Logger log = logger(pjp); + if (log.isInfoEnabled()) { try { - return Double.parseDouble(SAMPLING_RATE); - } catch (NumberFormatException e) { - LOG.debug("Skipping sampling rate on environment variable configuration because of invalid " + - "value. Sampling rate: {}", SAMPLING_RATE); + byte[] bytes = bytesFromInputStreamSafely((InputStream) args[0]); + args[0] = new ByteArrayInputStream(bytes); + // do not log asJson as it can be something else (String, XML...) + log.info("Handler Event", entry("event", new String(bytes, UTF_8))); + } catch (IOException e) { + LOG.warn("Failed to log event from supplied input stream.", e); } } - return logging.samplingRate(); } - private Object[] logEvent(final ProceedingJoinPoint pjp) { - Object[] args = pjp.getArgs(); - - if (isHandlerMethod(pjp)) { - if (placedOnRequestHandler(pjp)) { - Logger log = logger(pjp); - asJson(pjp, pjp.getArgs()[0]) - .ifPresent(log::info); - } - - if (placedOnStreamHandler(pjp)) { - args = logFromInputStream(pjp); - } + @SuppressWarnings("java:S3457") + private void logRequestHandlerResponse(final ProceedingJoinPoint pjp, final Object response) { + Logger log = logger(pjp); + if (log.isInfoEnabled()) { + log.info("Handler Response", entry("response", response)); } - - return args; } - private Object[] captureCorrelationId(final String correlationIdPath, - final ProceedingJoinPoint pjp) { - Object[] args = pjp.getArgs(); - if (isHandlerMethod(pjp)) { - if (placedOnRequestHandler(pjp)) { - Object arg = pjp.getArgs()[0]; - JsonNode jsonNode = objectMapper().valueToTree(arg); - - setCorrelationIdFromNode(correlationIdPath, pjp, jsonNode); - - return args; - } - - if (placedOnStreamHandler(pjp)) { - try { - byte[] bytes = bytesFromInputStreamSafely((InputStream) pjp.getArgs()[0]); - JsonNode jsonNode = objectMapper().readTree(bytes); - args[0] = new ByteArrayInputStream(bytes); - - setCorrelationIdFromNode(correlationIdPath, pjp, jsonNode); + @SuppressWarnings("java:S3457") + private void logRequestStreamHandlerResponse(final ProceedingJoinPoint pjp, final byte[] bytes) { + Logger log = logger(pjp); + if (log.isInfoEnabled()) { + // we do not log with asJson as it can be something else (String, XML, ...) + log.info("Handler Response", entry("response", new String(bytes, UTF_8))); + } + } - return args; - } catch (IOException e) { - Logger log = logger(pjp); - log.warn("Failed to capture correlation id on event from supplied input stream.", e); - } + private byte[] bytesFromInputStreamSafely(final InputStream inputStream) throws IOException { + try (ByteArrayOutputStream out = new ByteArrayOutputStream(); + InputStreamReader reader = new InputStreamReader(inputStream, UTF_8); + OutputStreamWriter writer = new OutputStreamWriter(out, UTF_8)) { + int n; + char[] buffer = new char[4096]; + while (-1 != (n = reader.read(buffer))) { + writer.write(buffer, 0, n); } + writer.flush(); + return out.toByteArray(); } - - return args; } - private void setCorrelationIdFromNode(String correlationIdPath, ProceedingJoinPoint pjp, JsonNode jsonNode) { - JsonNode node = jsonNode.at(JsonPointer.compile(correlationIdPath)); - - String asText = node.asText(); - if (null != asText && !asText.isEmpty()) { - LoggingUtils.setCorrelationId(asText); - } else { - logger(pjp).debug("Unable to extract any correlation id. Is your function expecting supported event type?"); + private OutputStream prepareOutputStreamForLogging(Logging logging, + Object[] proceedArgs) { + if (logging.logResponse() || POWERTOOLS_LOG_RESPONSE) { + OutputStream backupOutputStream = (OutputStream) proceedArgs[1]; + proceedArgs[1] = new ByteArrayOutputStream(); + return backupOutputStream; } + return null; } - private Object[] logFromInputStream(final ProceedingJoinPoint pjp) { - Object[] args = pjp.getArgs(); - - try { - byte[] bytes = bytesFromInputStreamSafely((InputStream) pjp.getArgs()[0]); - args[0] = new ByteArrayInputStream(bytes); - Logger log = logger(pjp); - - asJson(pjp, objectMapper().readValue(bytes, Map.class)) - .ifPresent(log::info); - - } catch (IOException e) { - Logger log = logger(pjp); - log.debug("Failed to log event from supplied input stream.", e); + private void handleException(ProceedingJoinPoint pjp, Logging logging, Throwable t) { + if (LOGGING_MANAGER instanceof BufferManager) { + if (logging.flushBufferOnUncaughtError()) { + ((BufferManager) LOGGING_MANAGER).flushBuffer(); + } else { + ((BufferManager) LOGGING_MANAGER).clearBuffer(); + } + } + if (logging.logError() || POWERTOOLS_LOG_ERROR) { + logger(pjp).error(MarkerFactory.getMarker("FATAL"), "Exception in Lambda Handler", t); } - - return args; } - private byte[] bytesFromInputStreamSafely(final InputStream inputStream) throws IOException { - try (ByteArrayOutputStream out = new ByteArrayOutputStream(); - InputStreamReader reader = new InputStreamReader(inputStream, UTF_8)) { - OutputStreamWriter writer = new OutputStreamWriter(out, UTF_8); - - IOUtils.copy(reader, writer); - writer.flush(); - return out.toByteArray(); + private void logResponse(ProceedingJoinPoint pjp, Logging logging, Object lambdaFunctionResponse, + boolean isOnRequestHandler, boolean isOnRequestStreamHandler, + OutputStream backupOutputStream, Object[] proceedArgs) throws IOException { + if (logging.logResponse() || POWERTOOLS_LOG_RESPONSE) { + if (isOnRequestHandler) { + logRequestHandlerResponse(pjp, lambdaFunctionResponse); + } else if (isOnRequestStreamHandler && backupOutputStream != null) { + byte[] bytes = ((ByteArrayOutputStream) proceedArgs[1]).toByteArray(); + logRequestStreamHandlerResponse(pjp, bytes); + backupOutputStream.write(bytes); + } } } - private Optional<String> asJson(final ProceedingJoinPoint pjp, - final Object target) { - try { - return ofNullable(objectMapper().writeValueAsString(target)); - } catch (JsonProcessingException e) { - logger(pjp).error("Failed logging event of type {}", target.getClass(), e); - return empty(); + private Object extractEventForCorrelationId(Logging logging, boolean isOnRequestHandler, + boolean isOnRequestStreamHandler, Object[] proceedArgs) { + if (logging.correlationIdPath().isEmpty()) { + return null; + } + + if (isOnRequestHandler && proceedArgs.length > 0) { + return proceedArgs[0]; + } else if (isOnRequestStreamHandler && proceedArgs.length > 0) { + try { + byte[] bytes = bytesFromInputStreamSafely((InputStream) proceedArgs[0]); + // Parse JSON string to Object for correlation ID extraction + Object event = JsonConfig.get().getObjectMapper().readTree(bytes); + proceedArgs[0] = new ByteArrayInputStream(bytes); // Restore stream + return event; + } catch (IOException e) { + LOG.warn("Failed to read event from input stream for correlation ID extraction.", e); + } } + return null; } private Logger logger(final ProceedingJoinPoint pjp) { - return LogManager.getLogger(pjp.getSignature().getDeclaringType()); + return LoggerFactory.getLogger(pjp.getSignature().getDeclaringType()); } } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java new file mode 100644 index 000000000..511b12ace --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +public final class LoggingConstants { + @SuppressWarnings({"java:S1104", "java:S1444", "java:S3008"}) + public static String LAMBDA_LOG_LEVEL = System.getenv("AWS_LAMBDA_LOG_LEVEL"); /* not final for test purpose */ + + @SuppressWarnings({"java:S1104", "java:S1444", "java:S3008"}) + public static String POWERTOOLS_LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); /* not final for test purpose */ + + @SuppressWarnings({"java:S1104", "java:S1444", "java:S3008"}) + public static String POWERTOOLS_SAMPLING_RATE = System.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE"); /* not final for test purpose */ + + static boolean POWERTOOLS_LOG_EVENT = "true".equals(System.getenv("POWERTOOLS_LOGGER_LOG_EVENT")); /* not final for test purpose */ + + static boolean POWERTOOLS_LOG_RESPONSE = "true".equals(System.getenv("POWERTOOLS_LOGGER_LOG_RESPONSE")); /* not final for test purpose */ + + static boolean POWERTOOLS_LOG_ERROR = "true".equals(System.getenv("POWERTOOLS_LOGGER_LOG_ERROR")); /* not final for test purpose */ + + private LoggingConstants() { + // constants + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManager.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManager.java new file mode 100644 index 000000000..51d05b25d --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManager.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import org.slf4j.Logger; +import org.slf4j.event.Level; + +/** + * Due to limitations of SLF4J, we need to rely on implementations for some operations: + * <ul> + * <li>Accessing to all loggers and change their Level</li> + * <li>Retrieving the log Level of a Logger</li> + * </ul> + * + * <p> + * This interface is used for these operations and implementations are provided in submodules and loaded thanks to a {@link java.util.ServiceLoader} + * (define a file named <code>software.amazon.lambda.powertools.logging.internal.LoggingManager</code> + * in <code>src/main/resources/META-INF/services</code> with the qualified name of the implementation). + */ +public interface LoggingManager { + /** + * Change the log Level of all loggers (named and root) + * + * @param logLevel the log Level (slf4j) to apply + */ + void setLogLevel(Level logLevel); + + /** + * Retrieve the log Level of a specific logger + * + * @param logger the logger (slf4j) for which to retrieve the log Level + * @return the Level (slf4j) of this logger. + * Note that SLF4J only support ERROR, WARN, INFO, DEBUG, TRACE while some frameworks may support others (OFF, FATAL, ...) + */ + Level getLogLevel(Logger logger); +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistry.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistry.java new file mode 100644 index 000000000..463981903 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistry.java @@ -0,0 +1,99 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import java.io.PrintStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +/** + * Thread-safe singleton registry for LoggingManager instances. + * Handles lazy loading and caching of the LoggingManager implementation. + */ +public final class LoggingManagerRegistry { + + // Used with double-checked locking within getLoggingManger() + @SuppressWarnings({ "java:S3077", "PMD.AvoidUsingVolatile" }) + private static volatile LoggingManager instance; + + private LoggingManagerRegistry() { + // Utility class + } + + /** + * Gets the LoggingManager instance, loading it lazily on first access. + * + * @return the LoggingManager instance + */ + public static LoggingManager getLoggingManager() { + LoggingManager manager = instance; + if (manager == null) { + synchronized (LoggingManagerRegistry.class) { + manager = instance; + if (manager == null) { + manager = loadLoggingManager(); + instance = manager; + } + } + } + return manager; + } + + @SuppressWarnings("java:S106") // S106: System.err is used rather than logger to make sure message is printed + private static LoggingManager loadLoggingManager() { + ServiceLoader<LoggingManager> loggingManagers; + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager == null) { + loggingManagers = ServiceLoader.load(LoggingManager.class); + } else { + final PrivilegedAction<ServiceLoader<LoggingManager>> action = () -> ServiceLoader + .load(LoggingManager.class); + loggingManagers = AccessController.doPrivileged(action); + } + + List<LoggingManager> loggingManagerList = new ArrayList<>(); + for (LoggingManager lm : loggingManagers) { + loggingManagerList.add(lm); + } + return selectLoggingManager(loggingManagerList, System.err); + } + + static LoggingManager selectLoggingManager(List<LoggingManager> loggingManagerList, PrintStream printStream) { + LoggingManager loggingManager; + if (loggingManagerList.isEmpty()) { + printStream.println("ERROR. No LoggingManager was found on the classpath"); + printStream.println("ERROR. Applying default LoggingManager: POWERTOOLS_LOG_LEVEL variable is ignored"); + printStream.println( + "ERROR. Make sure to add either powertools-logging-log4j or powertools-logging-logback to your dependencies"); + loggingManager = new DefaultLoggingManager(); + } else { + if (loggingManagerList.size() > 1) { + printStream.println("WARN. Multiple LoggingManagers were found on the classpath"); + for (LoggingManager manager : loggingManagerList) { + printStream.println("WARN. Found LoggingManager: [" + manager + "]"); + } + printStream.println( + "WARN. Make sure to have only one of powertools-logging-log4j OR powertools-logging-logback in your dependencies"); + printStream.println("WARN. Using the first LoggingManager found on the classpath: [" + + loggingManagerList.get(0) + "]"); + } + loggingManager = loggingManagerList.get(0); + } + return loggingManager; + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLambdaFields.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java similarity index 58% rename from powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLambdaFields.java rename to powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java index 2461ae771..2545396d2 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLambdaFields.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java @@ -16,22 +16,38 @@ import com.amazonaws.services.lambda.runtime.Context; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; -enum DefaultLambdaFields { - FUNCTION_NAME("functionName"), - FUNCTION_VERSION("functionVersion"), - FUNCTION_ARN("functionArn"), - FUNCTION_MEMORY_SIZE("functionMemorySize"), - FUNCTION_REQUEST_ID("function_request_id"); +/** + * Fields added in the logs by Powertools. + * Same as <a href="https://docs.powertools.aws.dev/lambda/python/latest/core/logger/#standard-structured-keys">python</a> + */ +public enum PowertoolsLoggedFields { + FUNCTION_NAME("function_name"), + FUNCTION_VERSION("function_version"), + FUNCTION_ARN("function_arn"), + FUNCTION_MEMORY_SIZE("function_memory_size"), + FUNCTION_REQUEST_ID("function_request_id"), + FUNCTION_COLD_START("cold_start"), + FUNCTION_TRACE_ID("xray_trace_id"), + SAMPLING_RATE("sampling_rate"), + CORRELATION_ID("correlation_id"), + SERVICE("service"); private final String name; - DefaultLambdaFields(String name) { + PowertoolsLoggedFields(String name) { this.name = name; } - static Map<String, String> values(Context context) { + public static List<String> stringValues() { + return Stream.of(values()).map(PowertoolsLoggedFields::getName).collect(Collectors.toList()); + } + + public static Map<String, String> setValuesFromLambdaContext(Context context) { Map<String, String> hashMap = new HashMap<>(); hashMap.put(FUNCTION_NAME.name, context.getFunctionName()); diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java deleted file mode 100644 index dc9816932..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.logging.internal; - -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.layout.template.json.resolver.EventResolver; -import org.apache.logging.log4j.layout.template.json.util.JsonWriter; -import org.apache.logging.log4j.util.ReadOnlyStringMap; - -final class PowertoolsResolver implements EventResolver { - - private final EventResolver internalResolver; - - PowertoolsResolver() { - internalResolver = new EventResolver() { - @Override - public boolean isResolvable(LogEvent value) { - ReadOnlyStringMap contextData = value.getContextData(); - return null != contextData && !contextData.isEmpty(); - } - - @Override - public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { - StringBuilder stringBuilder = jsonWriter.getStringBuilder(); - // remove dummy field to kick inn powertools resolver - stringBuilder.setLength(stringBuilder.length() - 4); - - // Inject all the context information. - ReadOnlyStringMap contextData = logEvent.getContextData(); - contextData.forEach((key, value) -> - { - jsonWriter.writeSeparator(); - jsonWriter.writeString(key); - stringBuilder.append(':'); - jsonWriter.writeValue(value); - }); - } - }; - } - - static String getName() { - return "powertools"; - } - - @Override - public void resolve(LogEvent value, JsonWriter jsonWriter) { - internalResolver.resolve(value, jsonWriter); - } - - @Override - public boolean isResolvable(LogEvent value) { - return internalResolver.isResolvable(value); - } -} diff --git a/powertools-logging/src/main/resources/LambdaEcsLayout.json b/powertools-logging/src/main/resources/LambdaEcsLayout.json deleted file mode 100644 index 4ab9c7ce2..000000000 --- a/powertools-logging/src/main/resources/LambdaEcsLayout.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "@timestamp": { - "$resolver": "timestamp", - "pattern": { - "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", - "timeZone": "UTC" - } - }, - "ecs.version": "1.2.0", - "log.level": { - "$resolver": "level", - "field": "name" - }, - "message": { - "$resolver": "message", - "stringified": true - }, - "process.thread.name": { - "$resolver": "thread", - "field": "name" - }, - "log.logger": { - "$resolver": "logger", - "field": "name" - }, - "labels": { - "$resolver": "mdc", - "flatten": true, - "stringified": true - }, - "tags": { - "$resolver": "ndc" - }, - "error.type": { - "$resolver": "exception", - "field": "className" - }, - "error.message": { - "$resolver": "exception", - "field": "message" - }, - "error.stack_trace": { - "$resolver": "exception", - "field": "stackTrace", - "stackTrace": { - "stringified": true - } - }, - "": { - "$resolver": "powertools" - } -} \ No newline at end of file diff --git a/powertools-logging/src/main/resources/LambdaJsonLayout.json b/powertools-logging/src/main/resources/LambdaJsonLayout.json deleted file mode 100644 index dfc1fc78f..000000000 --- a/powertools-logging/src/main/resources/LambdaJsonLayout.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "timestamp": { - "$resolver": "timestamp" - }, - "instant": { - "epochSecond": { - "$resolver": "timestamp", - "epoch": { - "unit": "secs", - "rounded": true - } - }, - "nanoOfSecond": { - "$resolver": "timestamp", - "epoch": { - "unit": "secs.nanos" - } - } - }, - "thread": { - "$resolver": "thread", - "field": "name" - }, - "level": { - "$resolver": "level", - "field": "name" - }, - "loggerName": { - "$resolver": "logger", - "field": "name" - }, - "message": { - "$resolver": "message", - "stringified": true - }, - "thrown": { - "message": { - "$resolver": "exception", - "field": "message" - }, - "name": { - "$resolver": "exception", - "field": "className" - }, - "extendedStackTrace": { - "$resolver": "exception", - "field": "stackTrace" - } - }, - "contextStack": { - "$resolver": "ndc" - }, - "endOfBatch": { - "$resolver": "endOfBatch" - }, - "loggerFqcn": { - "$resolver": "logger", - "field": "fqcn" - }, - "threadId": { - "$resolver": "thread", - "field": "id" - }, - "threadPriority": { - "$resolver": "thread", - "field": "priority" - }, - "source": { - "class": { - "$resolver": "source", - "field": "className" - }, - "method": { - "$resolver": "source", - "field": "methodName" - }, - "file": { - "$resolver": "source", - "field": "fileName" - }, - "line": { - "$resolver": "source", - "field": "lineNumber" - } - }, - "": { - "$resolver": "powertools" - } -} diff --git a/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/jni-config.json b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/jni-config.json new file mode 100644 index 000000000..c8b081385 --- /dev/null +++ b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/jni-config.json @@ -0,0 +1,10 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/reflect-config.json b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/reflect-config.json new file mode 100644 index 000000000..9e665e87a --- /dev/null +++ b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/reflect-config.json @@ -0,0 +1,282 @@ +[ +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.KeyDeserializers;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiators;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers;" +}, +{ + "name":"[Lsoftware.amazon.lambda.powertools.logging.model.Product;" +}, +{ + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.Context" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getHeaders","parameterTypes":[] }, {"name":"getHttpMethod","parameterTypes":[] }, {"name":"getIsBase64Encoded","parameterTypes":[] }, {"name":"getMultiValueHeaders","parameterTypes":[] }, {"name":"getMultiValueQueryStringParameters","parameterTypes":[] }, {"name":"getPath","parameterTypes":[] }, {"name":"getPathParameters","parameterTypes":[] }, {"name":"getQueryStringParameters","parameterTypes":[] }, {"name":"getRequestContext","parameterTypes":[] }, {"name":"getResource","parameterTypes":[] }, {"name":"getStageVariables","parameterTypes":[] }, {"name":"getVersion","parameterTypes":[] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setIsBase64Encoded","parameterTypes":["java.lang.Boolean"] }, {"name":"setMultiValueHeaders","parameterTypes":["java.util.Map"] }, {"name":"setMultiValueQueryStringParameters","parameterTypes":["java.util.Map"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setPathParameters","parameterTypes":["java.util.Map"] }, {"name":"setQueryStringParameters","parameterTypes":["java.util.Map"] }, {"name":"setRequestContext","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext"] }, {"name":"setResource","parameterTypes":["java.lang.String"] }, {"name":"setStageVariables","parameterTypes":["java.util.Map"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getAccountId","parameterTypes":[] }, {"name":"getApiId","parameterTypes":[] }, {"name":"getAuthorizer","parameterTypes":[] }, {"name":"getDomainName","parameterTypes":[] }, {"name":"getDomainPrefix","parameterTypes":[] }, {"name":"getExtendedRequestId","parameterTypes":[] }, {"name":"getHttpMethod","parameterTypes":[] }, {"name":"getIdentity","parameterTypes":[] }, {"name":"getOperationName","parameterTypes":[] }, {"name":"getPath","parameterTypes":[] }, {"name":"getProtocol","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }, {"name":"getRequestTime","parameterTypes":[] }, {"name":"getRequestTimeEpoch","parameterTypes":[] }, {"name":"getResourceId","parameterTypes":[] }, {"name":"getResourcePath","parameterTypes":[] }, {"name":"getStage","parameterTypes":[] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setResourcePath","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getCookies","parameterTypes":[] }, {"name":"getHeaders","parameterTypes":[] }, {"name":"getIsBase64Encoded","parameterTypes":[] }, {"name":"getPathParameters","parameterTypes":[] }, {"name":"getQueryStringParameters","parameterTypes":[] }, {"name":"getRawPath","parameterTypes":[] }, {"name":"getRawQueryString","parameterTypes":[] }, {"name":"getRequestContext","parameterTypes":[] }, {"name":"getRouteKey","parameterTypes":[] }, {"name":"getStageVariables","parameterTypes":[] }, {"name":"getVersion","parameterTypes":[] }, {"name":"setCookies","parameterTypes":["java.util.List"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setIsBase64Encoded","parameterTypes":["boolean"] }, {"name":"setRawPath","parameterTypes":["java.lang.String"] }, {"name":"setRawQueryString","parameterTypes":["java.lang.String"] }, {"name":"setRequestContext","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext"] }, {"name":"setRouteKey","parameterTypes":["java.lang.String"] }, {"name":"setVersion","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getAccountId","parameterTypes":[] }, {"name":"getApiId","parameterTypes":[] }, {"name":"getAuthorizer","parameterTypes":[] }, {"name":"getDomainName","parameterTypes":[] }, {"name":"getDomainPrefix","parameterTypes":[] }, {"name":"getHttp","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }, {"name":"getRouteKey","parameterTypes":[] }, {"name":"getStage","parameterTypes":[] }, {"name":"getTime","parameterTypes":[] }, {"name":"getTimeEpoch","parameterTypes":[] }, {"name":"setAccountId","parameterTypes":["java.lang.String"] }, {"name":"setApiId","parameterTypes":["java.lang.String"] }, {"name":"setDomainName","parameterTypes":["java.lang.String"] }, {"name":"setDomainPrefix","parameterTypes":["java.lang.String"] }, {"name":"setHttp","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Http"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setRouteKey","parameterTypes":["java.lang.String"] }, {"name":"setStage","parameterTypes":["java.lang.String"] }, {"name":"setTime","parameterTypes":["java.lang.String"] }, {"name":"setTimeEpoch","parameterTypes":["long"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Authorizer", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Authorizer$JWT", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$CognitoIdentity", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Http", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getMethod","parameterTypes":[] }, {"name":"getPath","parameterTypes":[] }, {"name":"getProtocol","parameterTypes":[] }, {"name":"getSourceIp","parameterTypes":[] }, {"name":"getUserAgent","parameterTypes":[] }, {"name":"setMethod","parameterTypes":["java.lang.String"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setProtocol","parameterTypes":["java.lang.String"] }, {"name":"setSourceIp","parameterTypes":["java.lang.String"] }, {"name":"setUserAgent","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$IAM", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getHeaders","parameterTypes":[] }, {"name":"getHttpMethod","parameterTypes":[] }, {"name":"getIsBase64Encoded","parameterTypes":[] }, {"name":"getMultiValueHeaders","parameterTypes":[] }, {"name":"getMultiValueQueryStringParameters","parameterTypes":[] }, {"name":"getPath","parameterTypes":[] }, {"name":"getQueryStringParameters","parameterTypes":[] }, {"name":"getRequestContext","parameterTypes":[] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setIsBase64Encoded","parameterTypes":["boolean"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setQueryStringParameters","parameterTypes":["java.util.Map"] }, {"name":"setRequestContext","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$RequestContext"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$Elb", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getTargetGroupArn","parameterTypes":[] }, {"name":"setTargetGroupArn","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$RequestContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getElb","parameterTypes":[] }, {"name":"setElb","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$Elb"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.tests.EventArgumentsProvider", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.tests.annotations.Event", + "queryAllPublicMethods":true +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"double", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.io.IOException" +}, +{ + "name":"java.io.InputStream" +}, +{ + "name":"java.io.OutputStream" +}, +{ + "name":"java.io.Serializable", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"java.lang.Boolean" +}, +{ + "name":"java.lang.Cloneable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Object" +}, +{ + "name":"java.lang.ProcessEnvironment", + "fields":[{"name":"theCaseInsensitiveEnvironment"}, {"name":"theEnvironment"}] +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.util.AbstractMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.Collections$SingletonMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] +}, +{ + "name":"java.util.Map", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.function.Consumer", + "queryAllPublicMethods":true +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.joda.time.DateTime" +}, +{ + "name":"org.slf4j.ILoggerFactory", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.Logger", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.helpers.AbstractLogger", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.helpers.LegacyAbstractLogger", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.spi.SLF4JServiceProvider", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"isColdStart"}, {"name":"serviceName"}] +}, +{ + "name":"software.amazon.lambda.powertools.logging.argument.StructuredArgumentsTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"arrayArgument","parameterTypes":[] }, {"name":"emptyMapArgument","parameterTypes":[] }, {"name":"jsonArgument","parameterTypes":[] }, {"name":"keyValueArgument","parameterTypes":[] }, {"name":"mapArgument","parameterTypes":[] }, {"name":"reservedKeywordArgumentIgnored","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.BufferManager", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect", + "fields":[{"name":"LEVEL_AT_INITIALISATION"}], + "methods":[{"name":"setLogLevels","parameterTypes":["org.slf4j.event.Level"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.LoggingConstants", + "fields":[{"name":"LAMBDA_LOG_LEVEL"}, {"name":"POWERTOOLS_LOG_EVENT"}, {"name":"POWERTOOLS_LOG_LEVEL"}, {"name":"POWERTOOLS_SAMPLING_RATE"}] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.LoggingManager", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.TestLoggingManager", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.model.Basket", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getProducts","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.model.Product", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getId","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPrice","parameterTypes":[] }] +} +] diff --git a/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/resource-config.json b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/resource-config.json new file mode 100644 index 000000000..832be3d72 --- /dev/null +++ b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/resource-config.json @@ -0,0 +1,17 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.configuration.Configuration\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.presentation.Representation\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager\\E" + }]}, + "bundles":[] +} diff --git a/powertools-logging/src/main/resources/log4j2.component.properties b/powertools-logging/src/main/resources/log4j2.component.properties deleted file mode 100644 index 3c392dd13..000000000 --- a/powertools-logging/src/main/resources/log4j2.component.properties +++ /dev/null @@ -1,2 +0,0 @@ -log4j.layout.jsonTemplate.timestampFormatPattern=yyyy-MM-dd'T'HH:mm:ss.SSSZz -#log4j.layout.jsonTemplate.timeZone= \ No newline at end of file diff --git a/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java b/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java deleted file mode 100644 index 9b0c6165a..000000000 --- a/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.apache.logging.log4j.core.layout; - -import static java.util.Collections.emptyMap; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Map; -import org.apache.logging.log4j.Level; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabled; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolSamplingEnabled; -import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; - -class LambdaJsonLayoutTest { - - private RequestHandler<Object, Object> handler = new PowerLogToolEnabled(); - - @Mock - private Context context; - - @BeforeEach - void setUp() throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - openMocks(this); - setupContext(); - //Make sure file is cleaned up before running full stack logging regression - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); - resetLogLevel(Level.INFO); - } - - @Test - void shouldLogInStructuredFormat() throws IOException { - handler.handleRequest("test", context); - - assertThat(Files.lines(Paths.get("target/logfile.json"))) - .hasSize(1) - .allSatisfy(line -> assertThat(parseToMap(line)) - .containsEntry("functionName", "testFunction") - .containsEntry("functionVersion", "1") - .containsEntry("functionMemorySize", "10") - .containsEntry("functionArn", "testArn") - .containsKey("timestamp") - .containsKey("message") - .containsKey("service")); - } - - @Test - void shouldModifyLogLevelBasedOnEnvVariable() - throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { - resetLogLevel(Level.DEBUG); - - handler.handleRequest("test", context); - - assertThat(Files.lines(Paths.get("target/logfile.json"))) - .hasSize(2) - .satisfies(line -> - { - assertThat(parseToMap(line.get(0))) - .containsEntry("level", "INFO") - .containsEntry("message", "Test event"); - - assertThat(parseToMap(line.get(1))) - .containsEntry("level", "DEBUG") - .containsEntry("message", "Test debug event"); - }); - } - - @Test - void shouldModifyLogLevelBasedOnSamplingRule() throws IOException { - handler = new PowerLogToolSamplingEnabled(); - - handler.handleRequest("test", context); - - assertThat(Files.lines(Paths.get("target/logfile.json"))) - .hasSize(3) - .satisfies(line -> - { - assertThat(parseToMap(line.get(0))) - .containsEntry("level", "DEBUG") - .containsEntry("loggerName", LambdaLoggingAspect.class.getCanonicalName()); - - assertThat(parseToMap(line.get(1))) - .containsEntry("level", "INFO") - .containsEntry("message", "Test event"); - - assertThat(parseToMap(line.get(2))) - .containsEntry("level", "DEBUG") - .containsEntry("message", "Test debug event"); - }); - } - - private void resetLogLevel(Level level) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); - resetLogLevels.setAccessible(true); - resetLogLevels.invoke(null, level); - writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); - } - - private Map<String, Object> parseToMap(String stringAsJson) { - try { - return new ObjectMapper().readValue(stringAsJson, Map.class); - } catch (JsonProcessingException e) { - fail("Failed parsing logger line " + stringAsJson); - return emptyMap(); - } - } - - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - } -} \ No newline at end of file diff --git a/powertools-logging/src/test/java/org/slf4j/test/OutputChoice.java b/powertools-logging/src/test/java/org/slf4j/test/OutputChoice.java new file mode 100644 index 000000000..9a6d56b81 --- /dev/null +++ b/powertools-logging/src/test/java/org/slf4j/test/OutputChoice.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * <p> + * 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: + * <p> + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * <p> + * 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. + */ + +package org.slf4j.test; + +import java.io.PrintStream; + +/** + * This class encapsulates the user's choice of output target. + * + * @see <a href="https://www.slf4j.org/xref/org/slf4j/simple/OutputChoice.html">...</a> + */ +class OutputChoice { + + final OutputChoiceType outputChoiceType; + final PrintStream targetPrintStream; + OutputChoice(OutputChoiceType outputChoiceType) { + if (outputChoiceType == OutputChoiceType.FILE) { + throw new IllegalArgumentException(); + } + this.outputChoiceType = outputChoiceType; + if (outputChoiceType == OutputChoiceType.CACHED_SYS_OUT) { + this.targetPrintStream = System.out; + } else if (outputChoiceType == OutputChoiceType.CACHED_SYS_ERR) { + this.targetPrintStream = System.err; + } else { + this.targetPrintStream = null; + } + } + + OutputChoice(PrintStream printStream) { + this.outputChoiceType = OutputChoiceType.FILE; + this.targetPrintStream = printStream; + } + + PrintStream getTargetPrintStream() { + switch (outputChoiceType) { + case SYS_OUT: + return System.out; + case SYS_ERR: + return System.err; + case CACHED_SYS_ERR: + case CACHED_SYS_OUT: + case FILE: + return targetPrintStream; + default: + throw new IllegalArgumentException(); + } + + } + + enum OutputChoiceType { + SYS_OUT, CACHED_SYS_OUT, SYS_ERR, CACHED_SYS_ERR, FILE; + } + +} diff --git a/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java b/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java new file mode 100644 index 000000000..acc635e75 --- /dev/null +++ b/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java @@ -0,0 +1,464 @@ +/** + * Copyright (c) 2004-2022 QOS.ch Sarl (Switzerland) + * All rights reserved. + * <p> + * 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: + * <p> + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * <p> + * 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. + */ + +package org.slf4j.test; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.Marker; +import org.slf4j.event.Level; +import org.slf4j.event.LoggingEvent; +import org.slf4j.helpers.LegacyAbstractLogger; +import org.slf4j.helpers.MessageFormatter; +import org.slf4j.helpers.NormalizedParameters; +import org.slf4j.spi.LocationAwareLogger; + +/** + * <p> + * Simple implementation of {@link Logger} that sends all enabled log messages, + * for all defined loggers, to the console ({@code System.err}). The following + * system properties are supported to configure the behavior of this logger: + * + * + * <ul> + * <li><code>org.slf4j.simpleLogger.logFile</code> - The output target which can + * be the <em>path</em> to a file, or the special values "System.out" and + * "System.err". Default is "System.err".</li> + * + * <li><code>org.slf4j.simpleLogger.cacheOutputStream</code> - If the output + * target is set to "System.out" or "System.err" (see preceding entry), by + * default, logs will be output to the latest value referenced by + * <code>System.out/err</code> variables. By setting this parameter to true, the + * output stream will be cached, i.e. assigned once at initialization time and + * re-used independently of the current value referenced by + * <code>System.out/err</code>.</li> + * + * <li><code>org.slf4j.simpleLogger.defaultLogLevel</code> - Default log level + * for all instances of SimpleLogger. Must be one of ("trace", "debug", "info", + * "warn", "error" or "off"). If not specified, defaults to "info".</li> + * + * <li><code>org.slf4j.simpleLogger.log.<em>a.b.c</em></code> - Logging detail + * level for a SimpleLogger instance named "a.b.c". Right-side value must be one + * of "trace", "debug", "info", "warn", "error" or "off". When a SimpleLogger + * named "a.b.c" is initialized, its level is assigned from this property. If + * unspecified, the level of nearest parent logger will be used, and if none is + * set, then the value specified by + * <code>org.slf4j.simpleLogger.defaultLogLevel</code> will be used.</li> + * + * <li><code>org.slf4j.simpleLogger.showDateTime</code> - Set to + * <code>true</code> if you want the current date and time to be included in + * output messages. Default is <code>false</code></li> + * + * <li><code>org.slf4j.simpleLogger.dateTimeFormat</code> - The date and time + * format to be used in the output messages. The pattern describing the date and + * time format is defined by <a href= + * "http://docs.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html"> + * <code>SimpleDateFormat</code></a>. If the format is not specified or is + * invalid, the number of milliseconds since start up will be output.</li> + * + * <li><code>org.slf4j.simpleLogger.showThreadName</code> -Set to + * <code>true</code> if you want to output the current thread name. Defaults to + * <code>true</code>.</li> + * + * <li>(since version 1.7.33 and 2.0.0-alpha6) <code>org.slf4j.simpleLogger.showThreadId</code> - + * If you would like to output the current thread id, then set to + * <code>true</code>. Defaults to <code>false</code>.</li> + * + * <li><code>org.slf4j.simpleLogger.showLogName</code> - Set to + * <code>true</code> if you want the Logger instance name to be included in + * output messages. Defaults to <code>true</code>.</li> + * + * <li><code>org.slf4j.simpleLogger.showShortLogName</code> - Set to + * <code>true</code> if you want the last component of the name to be included + * in output messages. Defaults to <code>false</code>.</li> + * + * <li><code>org.slf4j.simpleLogger.levelInBrackets</code> - Should the level + * string be output in brackets? Defaults to <code>false</code>.</li> + * + * <li><code>org.slf4j.simpleLogger.warnLevelString</code> - The string value + * output for the warn level. Defaults to <code>WARN</code>.</li> + * + * </ul> + * + * <p> + * In addition to looking for system properties with the names specified above, + * this implementation also checks for a class loader resource named + * <code>"simplelogger.properties"</code>, and includes any matching definitions + * from this resource (if it exists). + * + * + * <p> + * With no configuration, the default output includes the relative time in + * milliseconds, thread name, the level, logger name, and the message followed + * by the line separator for the host. In log4j terms it amounts to the "%r [%t] + * %level %logger - %m%n" pattern. + * + * <p> + * Sample output follows. + * + * + * <pre> + * 176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse order. + * 225 [main] INFO examples.SortAlgo - Entered the sort method. + * 304 [main] INFO examples.SortAlgo - Dump of integer array: + * 317 [main] INFO examples.SortAlgo - Element [0] = 0 + * 331 [main] INFO examples.SortAlgo - Element [1] = 1 + * 343 [main] INFO examples.Sort - The next log statement should be an error message. + * 346 [main] ERROR examples.SortAlgo - Tried to dump an uninitialized array. + * at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58) + * at org.log4j.examples.Sort.main(Sort.java:64) + * 467 [main] INFO examples.Sort - Exiting main method. + * </pre> + * + * <p> + * This implementation is heavily inspired by + * <a href="http://commons.apache.org/logging/">Apache Commons Logging</a>'s + * SimpleLog. + * + * + * @author Ceki Gülcü + * @author Scott Sanders + * @author Rod Waldhoff + * @author Robert Burrell Donkin + * @author Cédrik LIME + */ +public class TestLogger extends LegacyAbstractLogger { + + /** + * All system properties used by <code>SimpleLogger</code> start with this + * prefix + */ + public static final String SYSTEM_PREFIX = "org.slf4j.simpleLogger."; + public static final String LOG_KEY_PREFIX = TestLogger.SYSTEM_PREFIX + "log."; + public static final String CACHE_OUTPUT_STREAM_STRING_KEY = TestLogger.SYSTEM_PREFIX + "cacheOutputStream"; + public static final String WARN_LEVEL_STRING_KEY = TestLogger.SYSTEM_PREFIX + "warnLevelString"; + public static final String LEVEL_IN_BRACKETS_KEY = TestLogger.SYSTEM_PREFIX + "levelInBrackets"; + public static final String LOG_FILE_KEY = TestLogger.SYSTEM_PREFIX + "logFile"; + public static final String SHOW_SHORT_LOG_NAME_KEY = TestLogger.SYSTEM_PREFIX + "showShortLogName"; + public static final String SHOW_LOG_NAME_KEY = TestLogger.SYSTEM_PREFIX + "showLogName"; + public static final String SHOW_THREAD_NAME_KEY = TestLogger.SYSTEM_PREFIX + "showThreadName"; + public static final String SHOW_THREAD_ID_KEY = TestLogger.SYSTEM_PREFIX + "showThreadId"; + public static final String DATE_TIME_FORMAT_KEY = TestLogger.SYSTEM_PREFIX + "dateTimeFormat"; + public static final String SHOW_DATE_TIME_KEY = TestLogger.SYSTEM_PREFIX + "showDateTime"; + public static final String DEFAULT_LOG_LEVEL_KEY = TestLogger.SYSTEM_PREFIX + "defaultLogLevel"; + protected static final int LOG_LEVEL_TRACE = LocationAwareLogger.TRACE_INT; + protected static final int LOG_LEVEL_DEBUG = LocationAwareLogger.DEBUG_INT; + protected static final int LOG_LEVEL_INFO = LocationAwareLogger.INFO_INT; + protected static final int LOG_LEVEL_WARN = LocationAwareLogger.WARN_INT; + protected static final int LOG_LEVEL_ERROR = LocationAwareLogger.ERROR_INT; + // The OFF level can only be used in configuration files to disable logging. + // It has + // no printing method associated with it in o.s.Logger interface. + protected static final int LOG_LEVEL_OFF = LOG_LEVEL_ERROR + 10; + static final String TID_PREFIX = "tid="; + static final TestLoggerConfiguration CONFIG_PARAMS = new TestLoggerConfiguration(); + private static final long serialVersionUID = -632788891211436180L; + private static final long START_TIME = System.currentTimeMillis(); + static char SP = ' '; + private static boolean INITIALIZED = false; + /** The current log level */ + protected int currentLogLevel = LOG_LEVEL_INFO; + /** The short name of this simple log instance */ + private transient String shortLogName = null; + + // used for test purpose + private Object[] arguments; + + /** + * Package access allows only {@link TestLoggerFactory} to instantiate + * SimpleLogger instances. + */ + TestLogger(String name) { + this.name = name; + + String levelString = recursivelyComputeLevelString(); + if (levelString != null) { + this.currentLogLevel = TestLoggerConfiguration.stringToLevel(levelString); + } else { + this.currentLogLevel = CONFIG_PARAMS.defaultLogLevel; + } + } + + static void lazyInit() { + if (INITIALIZED) { + return; + } + INITIALIZED = true; + init(); + } + + // external software might be invoking this method directly. Do not rename + // or change its semantics. + static void init() { + CONFIG_PARAMS.init(); + } + + public int getLogLevel() { + return currentLogLevel; + } + + public void setLogLevel(String levelString) { + this.currentLogLevel = TestLoggerConfiguration.stringToLevel(levelString); + } + + String recursivelyComputeLevelString() { + String tempName = name; + String levelString = null; + int indexOfLastDot = tempName.length(); + while ((levelString == null) && (indexOfLastDot > -1)) { + tempName = tempName.substring(0, indexOfLastDot); + levelString = CONFIG_PARAMS.getStringProperty(TestLogger.LOG_KEY_PREFIX + tempName, null); + indexOfLastDot = String.valueOf(tempName).lastIndexOf("."); + } + return levelString; + } + + /** + * To avoid intermingling of log messages and associated stack traces, the two + * operations are done in a synchronized block. + * + * @param buf + * @param t + */ + void write(StringBuilder buf, Throwable t) { + PrintStream targetStream = CONFIG_PARAMS.outputChoice.getTargetPrintStream(); + + synchronized (CONFIG_PARAMS) { + targetStream.println(buf.toString()); + writeThrowable(t, targetStream); + targetStream.flush(); + } + + } + + protected void writeThrowable(Throwable t, PrintStream targetStream) { + if (t != null) { + t.printStackTrace(targetStream); + } + } + + private String getFormattedDate() { + Date now = new Date(); + String dateText; + synchronized (CONFIG_PARAMS.dateFormatter) { + dateText = CONFIG_PARAMS.dateFormatter.format(now); + } + return dateText; + } + + private String computeShortName() { + return name.substring(name.lastIndexOf(".") + 1); + } + + // /** + // * For formatted messages, first substitute arguments and then log. + // * + // * @param level + // * @param format + // * @param arg1 + // * @param arg2 + // */ + // private void formatAndLog(int level, String format, Object arg1, Object arg2) { + // if (!isLevelEnabled(level)) { + // return; + // } + // FormattingTuple tp = MessageFormatter.format(format, arg1, arg2); + // log(level, tp.getMessage(), tp.getThrowable()); + // } + + // /** + // * For formatted messages, first substitute arguments and then log. + // * + // * @param level + // * @param format + // * @param arguments + // * a list of 3 ore more arguments + // */ + // private void formatAndLog(int level, String format, Object... arguments) { + // if (!isLevelEnabled(level)) { + // return; + // } + // FormattingTuple tp = MessageFormatter.arrayFormat(format, arguments); + // log(level, tp.getMessage(), tp.getThrowable()); + // } + + /** + * Is the given log level currently enabled? + * + * @param logLevel is this level enabled? + * @return whether the logger is enabled for the given level + */ + protected boolean isLevelEnabled(int logLevel) { + // log level are numerically ordered so can use simple numeric + // comparison + return (logLevel >= currentLogLevel); + } + + /** Are {@code trace} messages currently enabled? */ + public boolean isTraceEnabled() { + return isLevelEnabled(LOG_LEVEL_TRACE); + } + + /** Are {@code debug} messages currently enabled? */ + public boolean isDebugEnabled() { + return isLevelEnabled(LOG_LEVEL_DEBUG); + } + + /** Are {@code info} messages currently enabled? */ + public boolean isInfoEnabled() { + return isLevelEnabled(LOG_LEVEL_INFO); + } + + /** Are {@code warn} messages currently enabled? */ + public boolean isWarnEnabled() { + return isLevelEnabled(LOG_LEVEL_WARN); + } + + /** Are {@code error} messages currently enabled? */ + public boolean isErrorEnabled() { + return isLevelEnabled(LOG_LEVEL_ERROR); + } + + /** + * SimpleLogger's implementation of + * {@link org.slf4j.helpers.AbstractLogger#handleNormalizedLoggingCall(Level, Marker, String, Object[], Throwable) AbstractLogger#handleNormalizedLoggingCall} + * } + * + * @param level the SLF4J level for this event + * @param marker The marker to be used for this event, may be null. + * @param messagePattern The message pattern which will be parsed and formatted + * @param arguments the array of arguments to be formatted, may be null + * @param throwable The exception whose stack trace should be logged, may be null + */ + @Override + protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, + Throwable throwable) { + + List<Marker> markers = null; + + if (marker != null) { + markers = new ArrayList<>(); + markers.add(marker); + } + + innerHandleNormalizedLoggingCall(level, markers, messagePattern, arguments, throwable); + } + + private void innerHandleNormalizedLoggingCall(Level level, List<Marker> markers, String messagePattern, + Object[] arguments, Throwable t) { + + this.arguments = arguments; + + StringBuilder buf = new StringBuilder(32); + + // Append date-time if so configured + if (CONFIG_PARAMS.showDateTime) { + if (CONFIG_PARAMS.dateFormatter != null) { + buf.append(getFormattedDate()); + buf.append(SP); + } else { + buf.append(System.currentTimeMillis() - START_TIME); + buf.append(SP); + } + } + + // Append current thread name if so configured + if (CONFIG_PARAMS.showThreadName) { + buf.append('['); + buf.append(Thread.currentThread().getName()); + buf.append("] "); + } + + if (CONFIG_PARAMS.showThreadId) { + buf.append(TID_PREFIX); + buf.append(Thread.currentThread().getId()); + buf.append(SP); + } + + if (CONFIG_PARAMS.levelInBrackets) { + buf.append('['); + } + + // Append a readable representation of the log level + String levelStr = level.name(); + buf.append(levelStr); + if (CONFIG_PARAMS.levelInBrackets) { + buf.append(']'); + } + buf.append(SP); + + // Append the name of the log instance if so configured + if (CONFIG_PARAMS.showShortLogName) { + if (shortLogName == null) { + shortLogName = computeShortName(); + } + buf.append(String.valueOf(shortLogName)).append(" - "); + } else if (CONFIG_PARAMS.showLogName) { + buf.append(String.valueOf(name)).append(" - "); + } + + if (markers != null) { + buf.append(SP); + for (Marker marker : markers) { + buf.append(marker.getName()).append(SP); + } + } + + String formattedMessage = MessageFormatter.basicArrayFormat(messagePattern, arguments); + + // Append the message + buf.append(formattedMessage); + + write(buf, t); + } + + public void log(LoggingEvent event) { + int levelInt = event.getLevel().toInt(); + + if (!isLevelEnabled(levelInt)) { + return; + } + + NormalizedParameters np = NormalizedParameters.normalize(event); + + innerHandleNormalizedLoggingCall(event.getLevel(), event.getMarkers(), np.getMessage(), np.getArguments(), + event.getThrowable()); + } + + @Override + protected String getFullyQualifiedCallerName() { + return null; + } + + public Object[] getArguments() { + return arguments; + } + + public void clearArguments() { + arguments = null; + } +} diff --git a/powertools-logging/src/test/java/org/slf4j/test/TestLoggerConfiguration.java b/powertools-logging/src/test/java/org/slf4j/test/TestLoggerConfiguration.java new file mode 100644 index 000000000..7601dbfde --- /dev/null +++ b/powertools-logging/src/test/java/org/slf4j/test/TestLoggerConfiguration.java @@ -0,0 +1,204 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * <p> + * 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: + * <p> + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * <p> + * 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. + */ + +package org.slf4j.test; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Properties; +import org.slf4j.helpers.Util; + +/** + * This class holds configuration values for {@link TestLogger}. The + * values are computed at runtime. See {@link TestLogger} documentation for + * more information. + * + * + * @author Ceki Gülcü + * @author Scott Sanders + * @author Rod Waldhoff + * @author Robert Burrell Donkin + * @author Cédrik LIME + * + * @since 1.7.25 + */ +public class TestLoggerConfiguration { + + final static boolean SHOW_LOG_NAME_DEFAULT = true; + private static final String CONFIGURATION_FILE = "testlogger.properties"; + private static final boolean SHOW_DATE_TIME_DEFAULT = false; + private static final String DATE_TIME_FORMAT_STR_DEFAULT = null; + private static final boolean SHOW_THREAD_NAME_DEFAULT = true; + /** + * See https://jira.qos.ch/browse/SLF4J-499 + * @since 1.7.33 and 2.0.0-alpha6 + */ + private static final boolean SHOW_THREAD_ID_DEFAULT = false; + private static final boolean SHOW_SHORT_LOG_NAME_DEFAULT = false; + private static final boolean LEVEL_IN_BRACKETS_DEFAULT = false; + private static final String LOG_FILE_DEFAULT = "System.err"; + private static final boolean CACHE_OUTPUT_STREAM_DEFAULT = false; + private static final String WARN_LEVELS_STRING_DEFAULT = "WARN"; + static int DEFAULT_LOG_LEVEL_DEFAULT = TestLogger.LOG_LEVEL_INFO; + private static String dateTimeFormatStr = DATE_TIME_FORMAT_STR_DEFAULT; + private final Properties properties = new Properties(); + int defaultLogLevel = DEFAULT_LOG_LEVEL_DEFAULT; + boolean showDateTime = SHOW_DATE_TIME_DEFAULT; + DateFormat dateFormatter = null; + boolean showThreadName = SHOW_THREAD_NAME_DEFAULT; + boolean showThreadId = SHOW_THREAD_ID_DEFAULT; + boolean showLogName = SHOW_LOG_NAME_DEFAULT; + boolean showShortLogName = SHOW_SHORT_LOG_NAME_DEFAULT; + boolean levelInBrackets = LEVEL_IN_BRACKETS_DEFAULT; + OutputChoice outputChoice = null; + String warnLevelString = WARN_LEVELS_STRING_DEFAULT; + private String logFile = LOG_FILE_DEFAULT; + private boolean cacheOutputStream = CACHE_OUTPUT_STREAM_DEFAULT; + + static int stringToLevel(String levelStr) { + if ("trace".equalsIgnoreCase(levelStr)) { + return TestLogger.LOG_LEVEL_TRACE; + } else if ("debug".equalsIgnoreCase(levelStr)) { + return TestLogger.LOG_LEVEL_DEBUG; + } else if ("info".equalsIgnoreCase(levelStr)) { + return TestLogger.LOG_LEVEL_INFO; + } else if ("warn".equalsIgnoreCase(levelStr)) { + return TestLogger.LOG_LEVEL_WARN; + } else if ("error".equalsIgnoreCase(levelStr)) { + return TestLogger.LOG_LEVEL_ERROR; + } else if ("off".equalsIgnoreCase(levelStr)) { + return TestLogger.LOG_LEVEL_OFF; + } + // assume INFO by default + return TestLogger.LOG_LEVEL_INFO; + } + + private static OutputChoice computeOutputChoice(String logFile, boolean cacheOutputStream) { + if ("System.err".equalsIgnoreCase(logFile)) { + if (cacheOutputStream) { + return new OutputChoice(OutputChoice.OutputChoiceType.CACHED_SYS_ERR); + } else { + return new OutputChoice(OutputChoice.OutputChoiceType.SYS_ERR); + } + } else if ("System.out".equalsIgnoreCase(logFile)) { + if (cacheOutputStream) { + return new OutputChoice(OutputChoice.OutputChoiceType.CACHED_SYS_OUT); + } else { + return new OutputChoice(OutputChoice.OutputChoiceType.SYS_OUT); + } + } else { + try { + FileOutputStream fos = new FileOutputStream(logFile); + PrintStream printStream = new PrintStream(fos); + return new OutputChoice(printStream); + } catch (FileNotFoundException e) { + Util.report("Could not open [" + logFile + "]. Defaulting to System.err", e); + return new OutputChoice(OutputChoice.OutputChoiceType.SYS_ERR); + } + } + } + + void init() { + loadProperties(); + + String defaultLogLevelString = getStringProperty(TestLogger.DEFAULT_LOG_LEVEL_KEY, null); + if (defaultLogLevelString != null) { + defaultLogLevel = stringToLevel(defaultLogLevelString); + } + + showLogName = getBooleanProperty(TestLogger.SHOW_LOG_NAME_KEY, TestLoggerConfiguration.SHOW_LOG_NAME_DEFAULT); + showShortLogName = getBooleanProperty(TestLogger.SHOW_SHORT_LOG_NAME_KEY, SHOW_SHORT_LOG_NAME_DEFAULT); + showDateTime = getBooleanProperty(TestLogger.SHOW_DATE_TIME_KEY, SHOW_DATE_TIME_DEFAULT); + showThreadName = getBooleanProperty(TestLogger.SHOW_THREAD_NAME_KEY, SHOW_THREAD_NAME_DEFAULT); + showThreadId = getBooleanProperty(TestLogger.SHOW_THREAD_ID_KEY, SHOW_THREAD_ID_DEFAULT); + dateTimeFormatStr = getStringProperty(TestLogger.DATE_TIME_FORMAT_KEY, DATE_TIME_FORMAT_STR_DEFAULT); + levelInBrackets = getBooleanProperty(TestLogger.LEVEL_IN_BRACKETS_KEY, LEVEL_IN_BRACKETS_DEFAULT); + warnLevelString = getStringProperty(TestLogger.WARN_LEVEL_STRING_KEY, WARN_LEVELS_STRING_DEFAULT); + + logFile = getStringProperty(TestLogger.LOG_FILE_KEY, logFile); + + cacheOutputStream = getBooleanProperty(TestLogger.CACHE_OUTPUT_STREAM_STRING_KEY, CACHE_OUTPUT_STREAM_DEFAULT); + outputChoice = computeOutputChoice(logFile, cacheOutputStream); + + if (dateTimeFormatStr != null) { + try { + dateFormatter = new SimpleDateFormat(dateTimeFormatStr); + } catch (IllegalArgumentException e) { + Util.report("Bad date format in " + CONFIGURATION_FILE + "; will output relative time", e); + } + } + } + + private void loadProperties() { + // Add props from the resource testlogger.properties + InputStream in = AccessController.doPrivileged((PrivilegedAction<InputStream>) () -> { + ClassLoader threadCL = Thread.currentThread().getContextClassLoader(); + if (threadCL != null) { + return threadCL.getResourceAsStream(CONFIGURATION_FILE); + } else { + return ClassLoader.getSystemResourceAsStream(CONFIGURATION_FILE); + } + }); + if (null != in) { + try { + properties.load(in); + } catch (java.io.IOException e) { + // ignored + } finally { + try { + in.close(); + } catch (java.io.IOException e) { + // ignored + } + } + } + } + + String getStringProperty(String name, String defaultValue) { + String prop = getStringProperty(name); + return (prop == null) ? defaultValue : prop; + } + + boolean getBooleanProperty(String name, boolean defaultValue) { + String prop = getStringProperty(name); + return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop); + } + + String getStringProperty(String name) { + String prop = null; + try { + prop = System.getProperty(name); + } catch (SecurityException e) { + ; // Ignore + } + return (prop == null) ? properties.getProperty(name) : prop; + } + +} diff --git a/powertools-logging/src/test/java/org/slf4j/test/TestLoggerFactory.java b/powertools-logging/src/test/java/org/slf4j/test/TestLoggerFactory.java new file mode 100644 index 000000000..d597b5706 --- /dev/null +++ b/powertools-logging/src/test/java/org/slf4j/test/TestLoggerFactory.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * <p> + * 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: + * <p> + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * <p> + * 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. + */ + +package org.slf4j.test; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; + +/** + * An implementation of {@link ILoggerFactory} which always returns + * {@link TestLogger} instances. + * + * @author Ceki Gülcü + */ +public class TestLoggerFactory implements ILoggerFactory { + + ConcurrentMap<String, Logger> loggerMap; + + public TestLoggerFactory() { + loggerMap = new ConcurrentHashMap<>(); + TestLogger.lazyInit(); + } + + public Map<String, Logger> getLoggers() { + return loggerMap; + } + + /** + * Return an appropriate {@link TestLogger} instance by name. + */ + public Logger getLogger(String name) { + Logger simpleLogger = loggerMap.get(name); + if (simpleLogger != null) { + return simpleLogger; + } else { + Logger newInstance = new TestLogger(name); + Logger oldInstance = loggerMap.putIfAbsent(name, newInstance); + return oldInstance == null ? newInstance : oldInstance; + } + } + + /** + * Clear the internal logger cache. + * + * This method is intended to be called by classes (in the same package) for + * testing purposes. This method is internal. It can be modified, renamed or + * removed at any time without notice. + * + * You are strongly discouraged from calling this method in production code. + */ + void reset() { + loggerMap.clear(); + } +} diff --git a/powertools-logging/src/test/java/org/slf4j/test/TestServiceProvider.java b/powertools-logging/src/test/java/org/slf4j/test/TestServiceProvider.java new file mode 100644 index 000000000..357360d1e --- /dev/null +++ b/powertools-logging/src/test/java/org/slf4j/test/TestServiceProvider.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * <p> + * 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: + * <p> + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * <p> + * 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. + */ + +package org.slf4j.test; + +import org.slf4j.ILoggerFactory; +import org.slf4j.IMarkerFactory; +import org.slf4j.helpers.BasicMDCAdapter; +import org.slf4j.helpers.BasicMarkerFactory; +import org.slf4j.spi.MDCAdapter; +import org.slf4j.spi.SLF4JServiceProvider; + +/** + * Copy of the org.slf4j.simple.SimpleServiceProvider, replacing the NoOpMDCAdapter with a BasicMDCAdapter to test the MDC + */ +public class TestServiceProvider implements SLF4JServiceProvider { + /** + * Declare the version of the SLF4J API this implementation is compiled against. + * The value of this field is modified with each major release. + */ + // to avoid constant folding by the compiler, this field must *not* be final + public static String REQUESTED_API_VERSION = "2.0.99"; // !final + + private ILoggerFactory loggerFactory; + private IMarkerFactory markerFactory; + private MDCAdapter mdcAdapter; + + public ILoggerFactory getLoggerFactory() { + return loggerFactory; + } + + @Override + public IMarkerFactory getMarkerFactory() { + return markerFactory; + } + + @Override + public MDCAdapter getMDCAdapter() { + return mdcAdapter; + } + + @Override + public String getRequestedApiVersion() { + return REQUESTED_API_VERSION; + } + + @Override + public void initialize() { + + loggerFactory = new TestLoggerFactory(); + markerFactory = new BasicMarkerFactory(); + mdcAdapter = new BasicMDCAdapter(); + } + +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java deleted file mode 100644 index 8889fb93c..000000000 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.logging; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.HashMap; -import java.util.Map; -import org.apache.logging.log4j.ThreadContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - - -class LoggingUtilsTest { - - @BeforeEach - void setUp() { - ThreadContext.clearAll(); - } - - @Test - void shouldSetCustomKeyOnThreadContext() { - LoggingUtils.appendKey("test", "value"); - - assertThat(ThreadContext.getImmutableContext()) - .hasSize(1) - .containsEntry("test", "value"); - } - - @Test - void shouldSetCustomKeyAsMapOnThreadContext() { - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test", "value"); - customKeys.put("test1", "value1"); - - LoggingUtils.appendKeys(customKeys); - - assertThat(ThreadContext.getImmutableContext()) - .hasSize(2) - .containsEntry("test", "value") - .containsEntry("test1", "value1"); - } - - @Test - void shouldRemoveCustomKeyOnThreadContext() { - LoggingUtils.appendKey("test", "value"); - - assertThat(ThreadContext.getImmutableContext()) - .hasSize(1) - .containsEntry("test", "value"); - - LoggingUtils.removeKey("test"); - - assertThat(ThreadContext.getImmutableContext()) - .isEmpty(); - } - - @Test - void shouldRemoveCustomKeysOnThreadContext() { - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test", "value"); - customKeys.put("test1", "value1"); - - LoggingUtils.appendKeys(customKeys); - - assertThat(ThreadContext.getImmutableContext()) - .hasSize(2) - .containsEntry("test", "value") - .containsEntry("test1", "value1"); - - LoggingUtils.removeKeys("test", "test1"); - - assertThat(ThreadContext.getImmutableContext()) - .isEmpty(); - } -} \ No newline at end of file diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggingTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggingTest.java new file mode 100644 index 000000000..9c68f687b --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggingTest.java @@ -0,0 +1,517 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.contentOf; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import com.amazonaws.services.lambda.runtime.Context; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.logging.internal.LoggingConstants; +import software.amazon.lambda.powertools.logging.internal.LoggingManagerRegistry; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; +import software.amazon.lambda.powertools.logging.internal.TestLoggingManager; + +class PowertoolsLoggingTest { + + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsLoggingTest.class); + private TestLoggingManager testManager; + private Context context; + + @BeforeEach + void setUp() throws IllegalAccessException, IOException { + // Get the TestLoggingManager instance from registry + testManager = (TestLoggingManager) LoggingManagerRegistry.getLoggingManager(); + testManager.resetBufferState(); + + context = new TestLambdaContext(); + + // Reset environment variables for clean test isolation + writeStaticField(LoggingConstants.class, "LAMBDA_LOG_LEVEL", null, true); + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", null, true); + writeStaticField(LoggingConstants.class, "POWERTOOLS_SAMPLING_RATE", null, true); + + // Clear MDC for clean test isolation + MDC.clear(); + + // Reset cold start state + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); + writeStaticField(PowertoolsLogging.class, "hasBeenInitialized", new AtomicBoolean(false), true); + + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @AfterEach + void cleanUp() throws IOException { + // Make sure file is cleaned up + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @Test + void testFlushBuffer_shouldCallBufferManager() { + // WHEN + PowertoolsLogging.flushBuffer(); + + // THEN + assertThat(testManager.isBufferFlushed()).isTrue(); + } + + @Test + void testClearBuffer_shouldCallBufferManager() { + // WHEN + PowertoolsLogging.clearBuffer(); + + // THEN + assertThat(testManager.isBufferCleared()).isTrue(); + } + + @Test + void shouldLogDebugWhenPowertoolsLevelEnvVarIsDebug() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "DEBUG", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isTrue(); + } + + @Test + void shouldLogInfoWhenPowertoolsLevelEnvVarIsInfo() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "INFO", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isTrue(); + } + + @Test + void shouldLogInfoWhenPowertoolsLevelEnvVarIsInvalid() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "INVALID", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isTrue(); + } + + @Test + void shouldLogWarnWhenPowertoolsLevelEnvVarIsWarn() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "WARN", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isFalse(); + assertThat(LOG.isWarnEnabled()).isTrue(); + } + + @Test + void shouldLogErrorWhenPowertoolsLevelEnvVarIsError() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "ERROR", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isFalse(); + assertThat(LOG.isWarnEnabled()).isFalse(); + assertThat(LOG.isErrorEnabled()).isTrue(); + } + + @Test + void shouldLogErrorWhenPowertoolsLevelEnvVarIsFatal() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "FATAL", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isFalse(); + assertThat(LOG.isWarnEnabled()).isFalse(); + assertThat(LOG.isErrorEnabled()).isTrue(); + } + + @Test + void shouldLogWarnWhenPowertoolsLevelEnvVarIsWarnAndLambdaLevelVarIsInfo() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "WARN", true); + writeStaticField(LoggingConstants.class, "LAMBDA_LOG_LEVEL", "INFO", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isFalse(); + assertThat(LOG.isWarnEnabled()).isTrue(); + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)) + .doesNotContain(" does not match AWS Lambda Advanced Logging Controls minimum log level"); + } + + @Test + void shouldLogInfoWhenPowertoolsLevelEnvVarIsInfoAndLambdaLevelVarIsWarn() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "INFO", true); + writeStaticField(LoggingConstants.class, "LAMBDA_LOG_LEVEL", "WARN", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isTrue(); + File logFile = new File("target/logfile.json"); + // should log a warning as powertools level is lower than lambda level + assertThat(contentOf(logFile)).contains( + "Current log level (INFO) does not match AWS Lambda Advanced Logging Controls minimum log level (WARN). This can lead to data loss, consider adjusting them."); + } + + @Test + void shouldLogWarnWhenPowertoolsLevelEnvVarINotSetAndLambdaLevelVarIsWarn() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", null, true); + writeStaticField(LoggingConstants.class, "LAMBDA_LOG_LEVEL", "WARN", true); + + // WHEN + reinitializeLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isFalse(); + assertThat(LOG.isWarnEnabled()).isTrue(); + } + + @Test + void initializeLogging_withContextOnly_shouldSetLambdaFields() { + // WHEN + PowertoolsLogging.initializeLogging(context); + + // THEN + Map<String, String> mdcMap = MDC.getCopyOfContextMap(); + assertThat(mdcMap) + .containsEntry(PowertoolsLoggedFields.FUNCTION_NAME.getName(), "test-function") + .containsEntry(PowertoolsLoggedFields.FUNCTION_VERSION.getName(), "1") + .containsEntry(PowertoolsLoggedFields.FUNCTION_COLD_START.getName(), "true") + .containsEntry(PowertoolsLoggedFields.SERVICE.getName(), "testService"); + } + + @Test + void initializeLogging_withSamplingRate_shouldSetSamplingRateInMdc() { + // WHEN + PowertoolsLogging.initializeLogging(context, 0.5); + + // THEN + assertThat(MDC.get(PowertoolsLoggedFields.SAMPLING_RATE.getName())).isEqualTo("0.5"); + } + + @Test + void initializeLogging_withCorrelationId_shouldExtractFromEvent() { + // GIVEN + Map<String, Object> event = Map.of("requestContext", Map.of("requestId", "test-correlation-id")); + + // WHEN + PowertoolsLogging.initializeLogging(context, "requestContext.requestId", event); + + // THEN + assertThat(MDC.get(PowertoolsLoggedFields.CORRELATION_ID.getName())).isEqualTo("test-correlation-id"); + } + + @Test + void initializeLogging_withFullConfiguration_shouldSetAllFields() { + // GIVEN + Map<String, Object> event = Map.of("id", "correlation-123"); + + // WHEN + PowertoolsLogging.initializeLogging(context, 0.5, "id", event); + + // THEN + Map<String, String> mdcMap = MDC.getCopyOfContextMap(); + assertThat(mdcMap) + .containsEntry(PowertoolsLoggedFields.FUNCTION_NAME.getName(), "test-function") + .containsEntry(PowertoolsLoggedFields.CORRELATION_ID.getName(), "correlation-123") + .containsEntry(PowertoolsLoggedFields.SAMPLING_RATE.getName(), "0.5"); + } + + @Test + void initializeLogging_withInvalidSamplingRate_shouldSkipSampling() { + // WHEN + PowertoolsLogging.initializeLogging(context, 2.0); + + // THEN + assertThat(MDC.get(PowertoolsLoggedFields.SAMPLING_RATE.getName())).isNull(); + } + + @Test + void initializeLogging_withEnvVarAndParameter_shouldUseEnvVarPrecedence() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_SAMPLING_RATE", "0.8", true); + + // WHEN + PowertoolsLogging.initializeLogging(context, 0.3); + + // THEN + assertThat(MDC.get(PowertoolsLoggedFields.SAMPLING_RATE.getName())).isEqualTo("0.8"); + } + + @Test + void initializeLogging_calledTwice_shouldMarkColdStartDoneOnSecondCall() throws IllegalAccessException { + // GIVEN + writeStaticField(PowertoolsLogging.class, "hasBeenInitialized", new AtomicBoolean(false), true); + + // WHEN - First call + PowertoolsLogging.initializeLogging(context); + String firstCallColdStart = MDC.get(PowertoolsLoggedFields.FUNCTION_COLD_START.getName()); + + // WHEN - Second call + PowertoolsLogging.initializeLogging(context); + String secondCallColdStart = MDC.get(PowertoolsLoggedFields.FUNCTION_COLD_START.getName()); + + // THEN + assertThat(firstCallColdStart).isEqualTo("true"); + assertThat(secondCallColdStart).isEqualTo("false"); + } + + @Test + void initializeLogging_withNullContext_shouldNotThrow() { + // WHEN & THEN + assertThatNoException().isThrownBy(() -> { + PowertoolsLogging.initializeLogging(null); + PowertoolsLogging.initializeLogging(null, 0.5); + PowertoolsLogging.initializeLogging(null, "path", Map.of()); + PowertoolsLogging.initializeLogging(null, 0.5, "path", Map.of()); + }); + } + + @Test + void clearState_shouldClearMdcAndBuffer() { + // GIVEN + MDC.put("test", "value"); + + // WHEN + PowertoolsLogging.clearState(true); + + // THEN + assertThat(MDC.getCopyOfContextMap()).isNull(); + assertThat(testManager.isBufferCleared()).isTrue(); + } + + @Test + void clearState_withoutMdcClear_shouldOnlyClearBuffer() { + // GIVEN + MDC.put("test", "value"); + + // WHEN + PowertoolsLogging.clearState(false); + + // THEN + assertThat(MDC.get("test")).isEqualTo("value"); + assertThat(testManager.isBufferCleared()).isTrue(); + } + + @Test + void initializeLogging_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + String[] samplingRates = new String[threadCount]; + boolean[] coldStarts = new boolean[threadCount]; + boolean[] success = new boolean[threadCount]; + + // WHEN - Multiple threads call initializeLogging with alternating sampling rates + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final double samplingRate = (i % 2 == 0) ? 1.0 : 0.0; // Alternate between 1.0 and 0.0 + + threads[i] = new Thread(() -> { + try { + PowertoolsLogging.initializeLogging(context, samplingRate); + + // Capture the sampling rate and cold start values set in MDC (thread-local) + samplingRates[threadIndex] = MDC.get(PowertoolsLoggedFields.SAMPLING_RATE.getName()); + coldStarts[threadIndex] = Boolean + .parseBoolean(MDC.get(PowertoolsLoggedFields.FUNCTION_COLD_START.getName())); + success[threadIndex] = true; + + // Clean up thread-local state + PowertoolsLogging.clearState(true); + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each thread should have its own sampling rate in MDC and exactly one invocation was a cold start + int coldStartCount = 0; + for (int i = 0; i < threadCount; i++) { + String expectedSamplingRate = (i % 2 == 0) ? "1.0" : "0.0"; + assertThat(samplingRates[i]).as("Thread %d should have sampling rate %s", i, expectedSamplingRate) + .isEqualTo(expectedSamplingRate); + + coldStartCount += coldStarts[i] ? 1 : 0; + } + assertThat(coldStartCount).isEqualTo(1); + } + + @Test + void withLogging_basicUsage_shouldInitializeAndCleanup() { + // WHEN + String result = PowertoolsLogging.withLogging(context, () -> { + assertThat(MDC.get(PowertoolsLoggedFields.FUNCTION_NAME.getName())).isEqualTo("test-function"); + return "test-result"; + }); + + // THEN + assertThat(result).isEqualTo("test-result"); + assertThat(MDC.getCopyOfContextMap()).isNull(); + assertThat(testManager.isBufferCleared()).isTrue(); + } + + @Test + void withLogging_withSamplingRate_shouldSetSamplingRateAndCleanup() { + // WHEN + String result = PowertoolsLogging.withLogging(context, 0.5, () -> { + assertThat(MDC.get(PowertoolsLoggedFields.SAMPLING_RATE.getName())).isEqualTo("0.5"); + return "sampled-result"; + }); + + // THEN + assertThat(result).isEqualTo("sampled-result"); + assertThat(MDC.getCopyOfContextMap()).isNull(); + } + + @Test + void withLogging_withCorrelationId_shouldExtractCorrelationIdAndCleanup() { + // GIVEN + Map<String, Object> event = Map.of("requestId", "correlation-123"); + + // WHEN + Integer result = PowertoolsLogging.withLogging(context, "requestId", event, () -> { + assertThat(MDC.get(PowertoolsLoggedFields.CORRELATION_ID.getName())).isEqualTo("correlation-123"); + return 42; + }); + + // THEN + assertThat(result).isEqualTo(42); + assertThat(MDC.getCopyOfContextMap()).isNull(); + } + + @Test + void withLogging_withFullConfiguration_shouldSetAllFieldsAndCleanup() { + // GIVEN + Map<String, Object> event = Map.of("id", "full-correlation"); + + // WHEN + Boolean result = PowertoolsLogging.withLogging(context, 0.8, "id", event, () -> { + Map<String, String> mdcMap = MDC.getCopyOfContextMap(); + assertThat(mdcMap) + .containsEntry(PowertoolsLoggedFields.FUNCTION_NAME.getName(), "test-function") + .containsEntry(PowertoolsLoggedFields.CORRELATION_ID.getName(), "full-correlation") + .containsEntry(PowertoolsLoggedFields.SAMPLING_RATE.getName(), "0.8"); + return true; + }); + + // THEN + assertThat(result).isTrue(); + assertThat(MDC.getCopyOfContextMap()).isNull(); + } + + @Test + void withLogging_whenSupplierThrowsException_shouldStillCleanup() { + // WHEN & THEN + try { + PowertoolsLogging.withLogging(context, () -> { + assertThat(MDC.get(PowertoolsLoggedFields.FUNCTION_NAME.getName())).isEqualTo("test-function"); + throw new RuntimeException("test exception"); + }); + } catch (RuntimeException e) { + assertThat(e.getMessage()).isEqualTo("test exception"); + } + + // THEN - cleanup should still happen + assertThat(MDC.getCopyOfContextMap()).isNull(); + assertThat(testManager.isBufferCleared()).isTrue(); + } + + private void reinitializeLogLevel() { + try { + Method initializeLogLevel = PowertoolsLogging.class.getDeclaredMethod("initializeLogLevel"); + initializeLogLevel.setAccessible(true); + initializeLogLevel.invoke(null); + } catch (Exception e) { + throw new RuntimeException("Failed to reinitialize log level", e); + } + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/argument/StructuredArgumentsTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/argument/StructuredArgumentsTest.java new file mode 100644 index 000000000..b478ac0f0 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/argument/StructuredArgumentsTest.java @@ -0,0 +1,157 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.argument; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; +import software.amazon.lambda.powertools.logging.model.Basket; +import software.amazon.lambda.powertools.logging.model.Product; + +class StructuredArgumentsTest { + private StringBuilder sb; + private JsonSerializer serializer; + + @BeforeEach + void setUp() { + sb = new StringBuilder(); + serializer = new JsonSerializer(sb); + } + + @Test + void keyValueArgument() throws IOException { + // GIVEN + Basket basket = new Basket(); + basket.add(new Product(42, "Nintendo DS", 299.45)); + basket.add(new Product(98, "Playstation 5", 499.99)); + + // WHEN + StructuredArgument argument = StructuredArguments.entry("basket", basket); + argument.writeTo(serializer); + + // THEN + assertThat(sb.toString()).hasToString( + "\"basket\":{\"products\":[{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}]}"); + assertThat(argument.toString()).hasToString( + "basket=Basket{products=[Product{id=42, name='Nintendo DS', price=299.45}, Product{id=98, name='Playstation 5', price=499.99}]}"); + } + + @Test + void mapArgument() throws IOException { + // GIVEN + Map<String, Product> catalog = new HashMap<>(); + catalog.put("nds", new Product(42, "Nintendo DS", 299.45)); + catalog.put("ps5", new Product(98, "Playstation 5", 499.99)); + + // WHEN + StructuredArgument argument = StructuredArguments.entries(catalog); + argument.writeTo(serializer); + + // THEN + assertThat(sb.toString()) + .contains("\"nds\":{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},") + .contains("\"ps5\":{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}"); + assertThat(argument.toString()) + .contains("nds=Product{id=42, name='Nintendo DS', price=299.45}") + .contains("ps5=Product{id=98, name='Playstation 5', price=499.99}"); + } + + @Test + void emptyMapArgument() throws IOException { + // GIVEN + Map<String, Product> catalog = new HashMap<>(); + + // WHEN + assertNull(StructuredArguments.entries(null)); + StructuredArgument argument = StructuredArguments.entries(catalog); + argument.writeTo(serializer); + + // THEN + assertThat(sb.toString()).isEmpty(); + assertThat(argument.toString()).hasToString("{}"); + } + + @Test + void arrayArgument() throws IOException { + // GIVEN + Product[] products = new Product[] { + new Product(42, "Nintendo DS", 299.45), + new Product(98, "Playstation 5", 499.99) + }; + + // WHEN + StructuredArgument argument = StructuredArguments.array("products", products); + argument.writeTo(serializer); + + // THEN + assertThat(sb.toString()).contains( + "\"products\":[{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}]"); + assertThat(argument.toString()).contains( + "products=[Product{id=42, name='Nintendo DS', price=299.45}, Product{id=98, name='Playstation 5', price=499.99}]"); + } + + @Test + void jsonArgument() throws IOException { + // GIVEN + String rawJson = "{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}"; + + // WHEN + StructuredArgument argument = StructuredArguments.json("product", rawJson); + argument.writeTo(serializer); + + // THEN + assertThat(sb.toString()).contains("\"product\":{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}"); + assertThat(argument.toString()).contains("product={\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}"); + } + + @Test + void reservedKeywordArgumentIgnored() throws IOException { + // GIVEN + Basket basket = new Basket(); + basket.add(new Product(42, "Nintendo DS", 299.45)); + basket.add(new Product(98, "Playstation 5", 499.99)); + Product[] products = new Product[] { + new Product(42, "Nintendo DS", 299.45), + new Product(98, "Playstation 5", 499.99) + }; + String rawJson = "{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}"; + Map<String, Product> catalog = new HashMap<>(); + catalog.put("nds", new Product(42, "Nintendo DS", 299.45)); + catalog.put("message", new Product(98, "Playstation 5", 499.99)); + + // THEN + assertNull(StructuredArguments.entry("message", basket)); + assertNull(StructuredArguments.array("message", products)); + assertNull(StructuredArguments.json("message", rawJson)); + + StructuredArgument mapArg = StructuredArguments.entries(catalog); + mapArg.writeTo(serializer); + assertThat(sb.toString()) + .contains("\"nds\":{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}"); + assertThat(sb.toString()).doesNotContain("message"); + assertThat(mapArg.toString()) + .contains("nds=Product{id=42, name='Nintendo DS', price=299.45}"); + assertThat(mapArg.toString()).doesNotContain("message"); + } + +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java deleted file mode 100644 index 838de1216..000000000 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.logging.handlers; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; - -public class PowerToolLogEventEnabledWithCustomMapper implements RequestHandler<S3EventNotification, Object> { - - static { - ObjectMapper objectMapper = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addSerializer(S3EventNotification.class, new S3EventNotificationSerializer()); - objectMapper.registerModule(module); - LoggingUtils.defaultObjectMapper(objectMapper); - } - - @Logging(logEvent = true) - @Override - public Object handleRequest(S3EventNotification input, Context context) { - return null; - } - - static class S3EventNotificationSerializer extends StdSerializer<S3EventNotification> { - - public S3EventNotificationSerializer() { - this(null); - } - - public S3EventNotificationSerializer(Class<S3EventNotification> t) { - super(t); - } - - @Override - public void serialize(S3EventNotification o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) - throws IOException { - jsonGenerator.writeStartObject(); - jsonGenerator.writeStringField("eventSource", o.getRecords().get(0).getEventSource()); - jsonGenerator.writeEndObject(); - } - } -} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java index a32e3e06e..065d4c5b0 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java @@ -14,17 +14,17 @@ package software.amazon.lambda.powertools.logging.handlers; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.APPLICATION_LOAD_BALANCER; +import static software.amazon.lambda.powertools.logging.CorrelationIdPaths.APPLICATION_LOAD_BALANCER; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; public class PowertoolsLogAlbCorrelationId implements RequestHandler<ApplicationLoadBalancerRequestEvent, Object> { - private final Logger LOG = LogManager.getLogger(PowertoolsLogAlbCorrelationId.class); + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogAlbCorrelationId.class); @Override @Logging(correlationIdPath = APPLICATION_LOAD_BALANCER) diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayHttpApiCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogApiGatewayHttpApiCorrelationId.java similarity index 78% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayHttpApiCorrelationId.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogApiGatewayHttpApiCorrelationId.java index 54d87d5cb..922a09f13 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayHttpApiCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogApiGatewayHttpApiCorrelationId.java @@ -14,17 +14,17 @@ package software.amazon.lambda.powertools.logging.handlers; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_HTTP; +import static software.amazon.lambda.powertools.logging.CorrelationIdPaths.API_GATEWAY_HTTP; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; -public class PowerLogToolApiGatewayHttpApiCorrelationId implements RequestHandler<APIGatewayV2HTTPEvent, Object> { - private final Logger LOG = LogManager.getLogger(PowerLogToolApiGatewayHttpApiCorrelationId.class); +public class PowertoolsLogApiGatewayHttpApiCorrelationId implements RequestHandler<APIGatewayV2HTTPEvent, Object> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogApiGatewayHttpApiCorrelationId.class); @Override @Logging(correlationIdPath = API_GATEWAY_HTTP) diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayRestApiCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogApiGatewayRestApiCorrelationId.java similarity index 78% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayRestApiCorrelationId.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogApiGatewayRestApiCorrelationId.java index 2b6e5a8d4..7271e1d24 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayRestApiCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogApiGatewayRestApiCorrelationId.java @@ -14,17 +14,17 @@ package software.amazon.lambda.powertools.logging.handlers; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_REST; +import static software.amazon.lambda.powertools.logging.CorrelationIdPaths.API_GATEWAY_REST; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; -public class PowerLogToolApiGatewayRestApiCorrelationId implements RequestHandler<APIGatewayProxyRequestEvent, Object> { - private final Logger LOG = LogManager.getLogger(PowerLogToolApiGatewayRestApiCorrelationId.class); +public class PowertoolsLogApiGatewayRestApiCorrelationId implements RequestHandler<APIGatewayProxyRequestEvent, Object> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogApiGatewayRestApiCorrelationId.class); @Override @Logging(correlationIdPath = API_GATEWAY_REST) diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAppSyncCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAppSyncCorrelationId.java new file mode 100644 index 000000000..fbe2bc89b --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAppSyncCorrelationId.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.handlers; + +import static software.amazon.lambda.powertools.logging.CorrelationIdPaths.APPSYNC_RESOLVER; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogAppSyncCorrelationId implements RequestStreamHandler { + + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogAppSyncCorrelationId.class); + + @Override + @Logging(correlationIdPath = APPSYNC_RESOLVER) + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { + LOG.info("Test event"); + } +} \ No newline at end of file diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogClearState.java similarity index 63% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogClearState.java index f21d9f118..cb7fbb408 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogClearState.java @@ -16,23 +16,20 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; -public class PowertoolsLogEnabledWithClearState implements RequestHandler<Object, Object> { - private static final Logger LOG = LogManager.getLogger(PowertoolsLogEnabledWithClearState.class); - public static int COUNT = 1; +public class PowertoolsLogClearState implements RequestHandler<Map<String, String>, Object> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogClearState.class); @Override @Logging(clearState = true) - public Object handleRequest(Object input, Context context) { - if (COUNT == 1) { - LoggingUtils.appendKey("TestKey", "TestValue"); - } + public Object handleRequest(Map<String, String> input, Context context) { + MDC.put("mySuperSecret", input.get("mySuperSecret")); LOG.info("Test event"); - COUNT++; return null; } } diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogDisabled.java similarity index 91% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabled.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogDisabled.java index 48a2e3b81..54e887e40 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogDisabled.java @@ -17,7 +17,7 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -public class PowerToolDisabled implements RequestHandler<Object, Object> { +public class PowertoolsLogDisabled implements RequestHandler<Object, Object> { @Override public Object handleRequest(Object input, Context context) { diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabledForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogDisabledForStream.java similarity index 92% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabledForStream.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogDisabledForStream.java index 7f93145c7..7f7418ed6 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabledForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogDisabledForStream.java @@ -19,7 +19,7 @@ import java.io.InputStream; import java.io.OutputStream; -public class PowerToolDisabledForStream implements RequestStreamHandler { +public class PowertoolsLogDisabledForStream implements RequestStreamHandler { @Override public void handleRequest(InputStream input, OutputStream output, Context context) { diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabled.java new file mode 100644 index 000000000..aa0a5942c --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabled.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); + private final boolean throwError; + + public PowertoolsLogEnabled(boolean throwError) { + this.throwError = throwError; + } + + public PowertoolsLogEnabled() { + this(false); + } + + @Override + @Logging + public Object handleRequest(Object input, Context context) { + if (throwError) { + throw new RuntimeException("Something went wrong"); + } + LOG.error("Test error event"); + LOG.warn("Test warn event"); + LOG.info("Test event"); + LOG.debug("Test debug event"); + return "Bonjour le monde"; + } + + @Logging + public void anotherMethod() { + System.out.println("test"); + } + + public static Logger getLogger() { + return LOG; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledForStream.java similarity index 93% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledForStream.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledForStream.java index 83a370437..c95627302 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledForStream.java @@ -20,7 +20,7 @@ import java.io.OutputStream; import software.amazon.lambda.powertools.logging.Logging; -public class PowerLogToolEnabledForStream implements RequestStreamHandler { +public class PowertoolsLogEnabledForStream implements RequestStreamHandler { @Logging @Override diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogError.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogError.java new file mode 100644 index 000000000..a6b1ed915 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogError.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogError implements RequestHandler<Object, Object> { + + @Override + @Logging(logError = true) + public Object handleRequest(Object input, Context context) { + throw new UnsupportedOperationException("This is an error"); + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogErrorNoFlush.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogErrorNoFlush.java new file mode 100644 index 000000000..87515654c --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogErrorNoFlush.java @@ -0,0 +1,15 @@ +package software.amazon.lambda.powertools.logging.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogErrorNoFlush implements RequestHandler<String, String> { + + @Override + @Logging(logError = true, flushBufferOnUncaughtError = false) + public String handleRequest(String input, Context context) { + throw new RuntimeException("This is an error without buffer flush"); + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEvent.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEvent.java new file mode 100644 index 000000000..c83692e95 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogEvent implements RequestHandler<Object, Object> { + + private static final Logger logger = LoggerFactory.getLogger(PowertoolsLogEvent.class); + + @Override + @Logging(logEvent = true) + public Object handleRequest(Object input, Context context) { + return null; + } + + public static Logger getLogger() { + return logger; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java index 53e06cb2e..04d56d38c 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java @@ -14,20 +14,20 @@ package software.amazon.lambda.powertools.logging.handlers; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.EVENT_BRIDGE; +import static software.amazon.lambda.powertools.logging.CorrelationIdPaths.EVENT_BRIDGE; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; public class PowertoolsLogEventBridgeCorrelationId implements RequestStreamHandler { - private final Logger LOG = LogManager.getLogger(PowertoolsLogEventBridgeCorrelationId.class); + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEventBridgeCorrelationId.class); @Override @Logging(correlationIdPath = EVENT_BRIDGE) diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventEnvVar.java similarity index 71% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventEnvVar.java index df68ea14f..230394bf7 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventEnvVar.java @@ -16,23 +16,21 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; -public class PowerLogToolEnabled implements RequestHandler<Object, Object> { - private final Logger LOG = LogManager.getLogger(PowerLogToolEnabled.class); +public class PowertoolsLogEventEnvVar implements RequestHandler<Object, Object> { + + private final Logger logger = LoggerFactory.getLogger(PowertoolsLogEventEnvVar.class); @Override @Logging public Object handleRequest(Object input, Context context) { - LOG.info("Test event"); - LOG.debug("Test debug event"); return null; } - @Logging - public void anotherMethod() { - System.out.println("test"); + public Logger getLogger() { + return logger; } } diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java similarity index 65% rename from powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java index 4a60d0949..6e27f47ce 100644 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.testsuite.handler; +package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -21,16 +21,22 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.tracing.Tracing; -public class TracingLoggingStreamMessageHandler implements RequestStreamHandler { +public class PowertoolsLogEventForStream implements RequestStreamHandler { + + private static final Logger logger = LoggerFactory.getLogger(PowertoolsLogEventForStream.class); - @Logging(logEvent = true) - @Tracing @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + @Logging(logEvent = true) + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(output, mapper.readValue(input, Map.class)); + mapper.writeValue(outputStream, mapper.readValue(inputStream, Map.class)); + } + + public static Logger getLogger() { + return logger; } } diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponse.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponse.java new file mode 100644 index 000000000..e7607d3c9 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponse.java @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogResponse implements RequestHandler<Object, Object> { + private static final Logger logger = LoggerFactory.getLogger(PowertoolsLogResponse.class); + + public static Logger getLogger() { + return logger; + } + + @Override + @Logging(logResponse = true) + public Object handleRequest(Object input, Context context) { + return "Hola mundo"; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponseForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponseForStream.java new file mode 100644 index 000000000..fe591627b --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponseForStream.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogResponseForStream implements RequestStreamHandler { + + private static final Logger logger = LoggerFactory.getLogger(PowertoolsLogResponseForStream.class); + + @Override + @Logging(logResponse = true) + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { + byte[] buf = new byte[1024]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } + + public static Logger getLogger() { + return logger; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogSamplingDisabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogSamplingDisabled.java new file mode 100644 index 000000000..5e2a7f148 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogSamplingDisabled.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogSamplingDisabled implements RequestHandler<Object, Boolean> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogSamplingDisabled.class); + + @Override + @Logging(samplingRate = 0.0) + public Boolean handleRequest(Object input, Context context) { + LOG.info("Test event"); + LOG.debug("Test debug event"); + return LOG.isDebugEnabled(); + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolSamplingEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogSamplingEnabled.java similarity index 74% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolSamplingEnabled.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogSamplingEnabled.java index 357520395..6a8c37896 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolSamplingEnabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogSamplingEnabled.java @@ -16,18 +16,18 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.logging.Logging; -public class PowerLogToolSamplingEnabled implements RequestHandler<Object, Object> { - private final Logger LOG = LogManager.getLogger(PowerLogToolSamplingEnabled.class); +public class PowertoolsLogSamplingEnabled implements RequestHandler<Object, Boolean> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogSamplingEnabled.class); @Override @Logging(samplingRate = 1.0) - public Object handleRequest(Object input, Context context) { + public Boolean handleRequest(Object input, Context context) { LOG.info("Test event"); LOG.debug("Test debug event"); - return null; + return LOG.isDebugEnabled(); } } diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/JsonSerializerTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/JsonSerializerTest.java new file mode 100644 index 000000000..8b34d32cb --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/JsonSerializerTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.logging.model.Basket; +import software.amazon.lambda.powertools.logging.model.Product; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +class JsonSerializerTest { + private StringBuilder sb; + private JsonSerializer generator; + + @BeforeEach + void setUp() { + sb = new StringBuilder(); + generator = new JsonSerializer(sb); + } + + @Test + void writeString_shouldWriteStringWithQuotes() throws IOException { + generator.writeStringField("key", "StringValue"); + assertThat(sb.toString()).hasToString("\"key\":\"StringValue\""); + } + + @Test + void writeBoolean_shouldWriteBooleanValue() throws IOException { + generator.writeBooleanField("key", true); + assertThat(sb.toString()).hasToString("\"key\":true"); + } + + @Test + void writeNull_shouldWriteNullValue() throws IOException { + generator.writeNullField("key"); + assertThat(sb.toString()).hasToString("\"key\":null"); + } + + @Test + void writeInt_shouldWriteIntValue() throws IOException { + generator.writeNumberField("key", 1); + assertThat(sb.toString()).hasToString("\"key\":1"); + } + + @Test + void writeFloat_shouldWriteFloatValue() throws IOException { + generator.writeNumberField("key", 2.4f); + assertThat(sb.toString()).hasToString("\"key\":2.4"); + assertThat(sb.toString()).doesNotContain("F").doesNotContain("f"); // should not contain the F suffix for floats. + } + + @Test + void writeDouble_shouldWriteDoubleValue() throws IOException { + generator.writeNumberField("key", 4.3); + assertThat(sb.toString()).hasToString("\"key\":4.3"); + } + + @Test + void writeLong_shouldWriteLongValue() throws IOException { + generator.writeNumberField("key", 123456789L); + assertThat(sb.toString()).hasToString("\"key\":123456789"); + assertThat(sb.toString()).doesNotContain("L").doesNotContain("l"); // should not contain the L suffix for longs. + } + + @Test + void writeBigDecimal_shouldWriteBigDecimal() throws IOException { + generator.writeNumberField("key", BigDecimal.valueOf(432.1673254564546)); + assertThat(sb.toString()).hasToString("\"key\":432.1673254564546"); + } + + @Test + void writeStringObject_shouldWriteStringWithQuotes() throws IOException { + generator.writeObjectField("key","StringValue"); + assertThat(sb.toString()).hasToString("\"key\":\"StringValue\""); + } + + @Test + void writeMapObject_shouldWriteMapAsJson() throws IOException { + Map<String, Object> map = new HashMap<>(); + map.put("string","StringValue"); + map.put("number", BigInteger.valueOf(1234567890L)); + generator.writeObjectField("map", map); + assertThat(sb.toString()).hasToString("\"map\":{\"number\":1234567890,\"string\":\"StringValue\"}"); + } + + @Test + void writeListObject_shouldWriteListAsArray() throws IOException { + List<String> list = Arrays.asList("val1", "val2", "val3"); + generator.writeObjectField("list", list); + assertThat(sb.toString()).hasToString("\"list\":[\"val1\",\"val2\",\"val3\"]"); + } + + @Test + void writeCustomObject_shouldWriteObjectAsJson() throws IOException { + Basket basket = new Basket(); + basket.add(new Product(42, "Nintendo DS", 299.45)); + basket.add(new Product(98, "Playstation 5", 499.99)); + generator.writeObjectField("basket", basket); + assertThat(sb.toString()).hasToString("\"basket\":{\"products\":[{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}]}"); + } + + @Test + void writeJsonNodeObject_shouldWriteObjectAsJson() throws IOException { + JsonNode jsonNode = JsonConfig.get().getObjectMapper().readTree( + "[{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}]"); + generator.writeObjectField("basket", jsonNode); + assertThat(sb.toString()).hasToString("\"basket\":[{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}]"); + } + + @Test + void writeTreeNodeArrayObject_shouldWriteObjectAsJson() throws IOException { + TreeNode treeNode = JsonConfig.get().getObjectMapper().readTree( + "[{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}]"); + generator.writeTree(treeNode); + assertThat(sb.toString()).hasToString("[{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45},{\"id\":98,\"name\":\"Playstation 5\",\"price\":499.99}]"); + } + + @Test + void writeTreeNodeObject_shouldWriteObjectAsJson() throws IOException { + TreeNode treeNode = JsonConfig.get().getObjectMapper().readTree( + "{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}"); + generator.writeTree(treeNode); + assertThat(sb.toString()).hasToString("{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}"); + } + +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/KeyBufferTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/KeyBufferTest.java new file mode 100644 index 000000000..fac85e230 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/KeyBufferTest.java @@ -0,0 +1,361 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Deque; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class KeyBufferTest { + + private KeyBuffer<String, String> buffer; + private static final int MAX_BYTES = 20; + + @BeforeEach + void setUp() throws IOException { + buffer = new KeyBuffer<>(MAX_BYTES, String::length); + // Clean up log file before each test + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @AfterEach + void cleanUp() throws IOException { + // Make sure file is cleaned up after each test + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @Test + void shouldAddEventToBuffer() { + buffer.add("key1", "test"); + + Deque<String> events = buffer.removeAll("key1"); + assertThat(events).containsExactly("test"); + } + + @Test + void shouldMaintainSeparateBuffersPerKey() { + buffer.add("key1", "event1"); + buffer.add("key2", "event2"); + + Deque<String> events1 = buffer.removeAll("key1"); + Deque<String> events2 = buffer.removeAll("key2"); + + assertThat(events1).containsExactly("event1"); + assertThat(events2).containsExactly("event2"); + } + + @Test + void shouldMaintainFIFOOrder() { + buffer.add("key1", "first"); + buffer.add("key1", "second"); + buffer.add("key1", "third"); + + Deque<String> events = buffer.removeAll("key1"); + assertThat(events).containsExactly("first", "second", "third"); + } + + @Test + void shouldEvictOldestEventsWhenBufferOverflows() { + // Add events that total exactly maxBytes + buffer.add("key1", "12345678901234567890"); // 20 bytes + + // Add another event that causes overflow + buffer.add("key1", "extra"); + + Deque<String> events = buffer.removeAll("key1"); + assertThat(events).containsExactly("extra"); + } + + @Test + void shouldEvictMultipleEventsIfNeeded() { + buffer.add("key1", "1234567890"); // 10 bytes + buffer.add("key1", "1234567890"); // 10 bytes, total 20 + buffer.add("key1", "12345678901234567890"); // 20 bytes, should evict both previous + + Deque<String> events = buffer.removeAll("key1"); + assertThat(events).containsExactly("12345678901234567890"); + } + + @Test + void shouldEvictMultipleSmallEventsForLargeValidEvent() { + // Add many small events that fill the buffer + buffer.add("key1", "12"); // 2 bytes + buffer.add("key1", "34"); // 2 bytes, total 4 + buffer.add("key1", "56"); // 2 bytes, total 6 + buffer.add("key1", "78"); // 2 bytes, total 8 + buffer.add("key1", "90"); // 2 bytes, total 10 + buffer.add("key1", "ab"); // 2 bytes, total 12 + buffer.add("key1", "cd"); // 2 bytes, total 14 + buffer.add("key1", "ef"); // 2 bytes, total 16 + buffer.add("key1", "gh"); // 2 bytes, total 18 + buffer.add("key1", "ij"); // 2 bytes, total 20 (exactly at limit) + + // Add a large event that requires multiple evictions + buffer.add("key1", "123456789012345678"); // 18 bytes, should evict multiple small events + + Deque<String> events = buffer.removeAll("key1"); + // Should only contain the last few small events plus the large event + // 18 bytes for large event leaves 2 bytes, so only "ij" should remain with the large event + assertThat(events).containsExactly("ij", "123456789012345678"); + } + + @Test + void shouldRejectEventLargerThanMaxBytes() { + String largeEvent = "123456789012345678901"; // 21 bytes > 20 max + + buffer.add("key1", largeEvent); + + Deque<String> events = buffer.removeAll("key1"); + assertThat(events).isNull(); + } + + @Test + void shouldNotEvictExistingEventsWhenRejectingLargeEvent() { + buffer.add("key1", "small"); + + String largeEvent = "123456789012345678901"; // 21 bytes > 20 max + buffer.add("key1", largeEvent); + + Deque<String> events = buffer.removeAll("key1"); + assertThat(events).containsExactly("small"); + } + + @Test + void shouldClearSpecificKeyBuffer() { + buffer.add("key1", "event1"); + buffer.add("key2", "event2"); + + buffer.clear("key1"); + + assertThat(buffer.removeAll("key1")).isNull(); + assertThat(buffer.removeAll("key2")).containsExactly("event2"); + } + + @Test + void shouldReturnNullForNonExistentKey() { + Deque<String> events = buffer.removeAll("nonexistent"); + assertThat(events).isNull(); + } + + @Test + void shouldReturnDefensiveCopyOnRemoveAll() { + buffer.add("key1", "event"); + + Deque<String> events1 = buffer.removeAll("key1"); + buffer.add("key1", "event"); + Deque<String> events2 = buffer.removeAll("key1"); + + // Modifying first copy shouldn't affect second + events1.add("modified"); + assertThat(events2).containsExactly("event"); + assertThat(events1).containsExactly("event", "modified"); + } + + @Test + void shouldLogWarningOnOverflow() { + StringBuilder warningCapture = new StringBuilder(); + KeyBuffer<String, String> testBuffer = new KeyBuffer<>(10, String::length, + () -> warningCapture.append("Some logs are not displayed because they were evicted from the buffer")); + + // Cause overflow + testBuffer.add("key1", "1234567890"); // 10 bytes + testBuffer.add("key1", "extra"); // causes overflow + + // Trigger warning by removing + testBuffer.removeAll("key1"); + + assertThat(warningCapture.toString()) + .contains("Some logs are not displayed because they were evicted from the buffer"); + } + + @Test + void shouldLogWarningOnLargeEventRejection() { + StringBuilder warningCapture = new StringBuilder(); + KeyBuffer<String, String> testBuffer = new KeyBuffer<>(10, String::length, + () -> warningCapture.append("Some logs are not displayed because they were evicted from the buffer")); + + // Add large event that gets rejected + testBuffer.add("key1", "12345678901"); // 11 bytes > 10 max + + // Trigger warning by removing + testBuffer.removeAll("key1"); + + assertThat(warningCapture.toString()) + .contains("Some logs are not displayed because they were evicted from the buffer"); + } + + @Test + void shouldNotLogWarningWhenNoOverflow() { + StringBuilder warningCapture = new StringBuilder(); + KeyBuffer<String, String> testBuffer = new KeyBuffer<>(20, String::length, + () -> warningCapture.append("Some logs are not displayed because they were evicted from the buffer")); + + testBuffer.add("key1", "small"); + testBuffer.removeAll("key1"); + + assertThat(warningCapture.toString()) + .doesNotContain("Some logs are not displayed because they were evicted from the buffer"); + } + + @Test + void shouldBeThreadSafeForDifferentKeys() throws InterruptedException, IOException { + int threadCount = 10; + int eventsPerThread = 100; + KeyBuffer<String, String> largeBuffer = new KeyBuffer<>(10000, String::length); + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + try (Closeable ignored = executor::shutdown) { + CountDownLatch latch = new CountDownLatch(threadCount); + + // Each thread works with different key + for (int i = 0; i < threadCount; i++) { + final String key = "key" + i; + executor.submit(() -> { + try { + for (int j = 0; j < eventsPerThread; j++) { + largeBuffer.add(key, "event" + j); + } + } finally { + latch.countDown(); + } + }); + } + + assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue(); + + // Verify each key has its events + for (int i = 0; i < threadCount; i++) { + String key = "key" + i; + Deque<String> events = largeBuffer.removeAll(key); + assertThat(events) + .isNotNull() + .hasSize(eventsPerThread); + } + } + } + + @Test + void shouldBeThreadSafeForSameKey() throws InterruptedException, IOException { + int threadCount = 5; + int eventsPerThread = 20; + KeyBuffer<String, String> largeBuffer = new KeyBuffer<>(10000, String::length); + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + try (Closeable ignored = executor::shutdown) { + CountDownLatch latch = new CountDownLatch(threadCount); + + // All threads work with same key + for (int i = 0; i < threadCount; i++) { + final int threadId = i; + executor.submit(() -> { + try { + for (int j = 0; j < eventsPerThread; j++) { + largeBuffer.add("sharedKey", "t" + threadId + "e" + j); + } + } finally { + latch.countDown(); + } + }); + } + + assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue(); + + Deque<String> events = largeBuffer.removeAll("sharedKey"); + assertThat(events) + .isNotNull() + .hasSize(threadCount * eventsPerThread); + } + } + + @Test + void shouldHandleEmptyBuffer() { + buffer.clear("nonexistent"); + assertThat(buffer.removeAll("nonexistent")).isNull(); + } + + @Test + void shouldHandleZeroSizeEvents() { + KeyBuffer<String, String> zeroBuffer = new KeyBuffer<>(10, s -> 0); + + zeroBuffer.add("key1", "event1"); + zeroBuffer.add("key1", "event2"); + + Deque<String> events = zeroBuffer.removeAll("key1"); + assertThat(events).containsExactly("event1", "event2"); + } + + @Test + void shouldUseCustomWarningLogger() { + StringBuilder customWarning = new StringBuilder(); + KeyBuffer<String, String> testBuffer = new KeyBuffer<>(5, String::length, + () -> customWarning.append("CUSTOM WARNING LOGGED")); + + // Cause overflow + testBuffer.add("key1", "12345"); // 5 bytes + testBuffer.add("key1", "extra"); // causes overflow + + // Trigger warning + testBuffer.removeAll("key1"); + + assertThat(customWarning).hasToString("CUSTOM WARNING LOGGED"); + } + + @Test + void shouldUseDefaultWarningLoggerWhenNotProvided() { + // Capture System.err output + ByteArrayOutputStream errCapture = new ByteArrayOutputStream(); + @SuppressWarnings("PMD.CloseResource") // System.err is not ours to close + PrintStream originalErr = System.err; + try (PrintStream newErr = new PrintStream(errCapture)) { + System.setErr(newErr); + + KeyBuffer<String, String> defaultBuffer = new KeyBuffer<>(5, String::length); + + // Cause overflow + defaultBuffer.add("key1", "12345"); + defaultBuffer.add("key1", "extra"); + defaultBuffer.removeAll("key1"); + + // Assert System.err received the warning + assertThat(errCapture) + .hasToString( + "WARN [KeyBuffer] - Some logs are not displayed because they were evicted from the buffer. Increase buffer size to store more logs in the buffer.\n"); + } finally { + System.setErr(originalErr); + } + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index b78710586..ca47f9097 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java @@ -14,65 +14,77 @@ package software.amazon.lambda.powertools.logging.internal; -import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.joining; import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; +import static org.assertj.core.api.Assertions.contentOf; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_NAME; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_REQUEST_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_TRACE_ID; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_VERSION; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SERVICE; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; -import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; -import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; -import com.amazonaws.services.lambda.runtime.tests.annotations.Event; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.ThreadContext; -import org.json.JSONException; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.core.internal.SystemWrapper; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolApiGatewayHttpApiCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolApiGatewayRestApiCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabled; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabledForStream; -import software.amazon.lambda.powertools.logging.handlers.PowerToolDisabled; -import software.amazon.lambda.powertools.logging.handlers.PowerToolDisabledForStream; -import software.amazon.lambda.powertools.logging.handlers.PowerToolLogEventEnabled; -import software.amazon.lambda.powertools.logging.handlers.PowerToolLogEventEnabledForStream; -import software.amazon.lambda.powertools.logging.handlers.PowerToolLogEventEnabledWithCustomMapper; +import org.junitpioneer.jupiter.ClearEnvironmentVariable; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.junitpioneer.jupiter.SetSystemProperty; +import org.slf4j.MDC; +import org.slf4j.event.Level; +import org.slf4j.test.TestLogger; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.tests.annotations.Event; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.logging.PowertoolsLogging; +import software.amazon.lambda.powertools.logging.argument.StructuredArgument; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledWithClearState; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayRestApiCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAppSyncCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogClearState; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogDisabled; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogDisabledForStream; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabled; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledForStream; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogError; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogErrorNoFlush; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEvent; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventEnvVar; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventForStream; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponse; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponseForStream; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingDisabled; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingEnabled; class LambdaLoggingAspectTest { @@ -80,194 +92,446 @@ class LambdaLoggingAspectTest { private RequestStreamHandler requestStreamHandler; private RequestHandler<Object, Object> requestHandler; - @Mock private Context context; @BeforeEach - void setUp() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { - openMocks(this); - ThreadContext.clearAll(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); - requestHandler = new PowerLogToolEnabled(); - requestStreamHandler = new PowerLogToolEnabledForStream(); - //Make sure file is cleaned up before running full stack logging regression - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); - resetLogLevel(Level.INFO); + void setUp() throws IllegalAccessException, IOException { + MDC.clear(); + + // Reset cold start state + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); + writeStaticField(PowertoolsLogging.class, "hasBeenInitialized", new AtomicBoolean(false), true); + + context = new TestLambdaContext(); + requestHandler = new PowertoolsLogEnabled(); + requestStreamHandler = new PowertoolsLogEnabledForStream(); + writeStaticField(LoggingConstants.class, "LAMBDA_LOG_LEVEL", null, true); + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", null, true); + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_EVENT", false, true); + writeStaticField(LoggingConstants.class, "POWERTOOLS_SAMPLING_RATE", null, true); + + // Reset buffer state for clean test isolation + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + ((TestLoggingManager) loggingManager).resetBufferState(); + } + + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + } + + @AfterEach + void cleanUp() throws IOException { + // Make sure file is cleaned up before running full stack logging regression + try { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } catch (NoSuchFileException e) { + // may not be there in the first run + } + + // Reset log level to INFO to ensure test isolation + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + loggingManager.setLogLevel(Level.INFO); } @Test void shouldSetLambdaContextWhenEnabled() { requestHandler.handleRequest(new Object(), context); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry(DefaultLambdaFields.FUNCTION_ARN.getName(), "testArn") - .containsEntry(DefaultLambdaFields.FUNCTION_MEMORY_SIZE.getName(), "10") - .containsEntry(DefaultLambdaFields.FUNCTION_VERSION.getName(), "1") - .containsEntry(DefaultLambdaFields.FUNCTION_NAME.getName(), "testFunction") - .containsEntry(DefaultLambdaFields.FUNCTION_REQUEST_ID.getName(), "RequestId") - .containsKey("coldStart") - .containsKey("service"); + .containsEntry(FUNCTION_ARN.getName(), "arn:aws:lambda:us-east-1:123456789012:function:test") + .containsEntry(FUNCTION_MEMORY_SIZE.getName(), "128") + .containsEntry(FUNCTION_VERSION.getName(), "1") + .containsEntry(FUNCTION_NAME.getName(), "test-function") + .containsEntry(FUNCTION_REQUEST_ID.getName(), "test-request-id") + .containsKey(FUNCTION_COLD_START.getName()) + .containsKey(SERVICE.getName()); } @Test void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { - requestStreamHandler = new PowerLogToolEnabledForStream(); + requestStreamHandler = new PowertoolsLogEnabledForStream(); requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry(DefaultLambdaFields.FUNCTION_ARN.getName(), "testArn") - .containsEntry(DefaultLambdaFields.FUNCTION_MEMORY_SIZE.getName(), "10") - .containsEntry(DefaultLambdaFields.FUNCTION_VERSION.getName(), "1") - .containsEntry(DefaultLambdaFields.FUNCTION_NAME.getName(), "testFunction") - .containsEntry(DefaultLambdaFields.FUNCTION_REQUEST_ID.getName(), "RequestId") - .containsKey("coldStart") - .containsKey("service"); + .containsEntry(FUNCTION_ARN.getName(), "arn:aws:lambda:us-east-1:123456789012:function:test") + .containsEntry(FUNCTION_MEMORY_SIZE.getName(), "128") + .containsEntry(FUNCTION_VERSION.getName(), "1") + .containsEntry(FUNCTION_NAME.getName(), "test-function") + .containsEntry(FUNCTION_REQUEST_ID.getName(), "test-request-id") + .containsKey(FUNCTION_COLD_START.getName()) + .containsKey(SERVICE.getName()); } @Test - void shouldSetColdStartFlag() throws IOException { + void shouldSetColdStartFlagOnFirstCallNotOnSecondCall() throws IOException { requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry("coldStart", "true"); + .containsEntry(FUNCTION_COLD_START.getName(), "true"); requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry("coldStart", "false"); + .containsEntry(FUNCTION_COLD_START.getName(), "false"); } @Test void shouldNotSetLambdaContextWhenDisabled() { - requestHandler = new PowerToolDisabled(); + requestHandler = new PowertoolsLogDisabled(); requestHandler.handleRequest(new Object(), context); - assertThat(ThreadContext.getImmutableContext()) - .isEmpty(); + assertThat(MDC.getCopyOfContextMap()).isNull(); } @Test void shouldNotSetLambdaContextForStreamHandlerWhenDisabled() throws IOException { - requestStreamHandler = new PowerToolDisabledForStream(); + requestStreamHandler = new PowertoolsLogDisabledForStream(); requestStreamHandler.handleRequest(null, null, context); - assertThat(ThreadContext.getImmutableContext()) - .isEmpty(); + assertThat(MDC.getCopyOfContextMap()).isNull(); + } + + @Test + void shouldClearStateWhenClearStateIsTrue() { + PowertoolsLogClearState handler = new PowertoolsLogClearState(); + + handler.handleRequest(Collections.singletonMap("mySuperSecret", "P@ssw0Rd"), context); + + assertThat(MDC.getCopyOfContextMap()).isNull(); + } + + @Test + void shouldLogDebugWhenSamplingEqualsOne() { + PowertoolsLogSamplingEnabled handler = new PowertoolsLogSamplingEnabled(); + + Boolean debugEnabled = handler.handleRequest(new Object(), context); + + assertThat(debugEnabled).isTrue(); + } + + @Test + void shouldLogDebugWhenSamplingEnvVarEqualsOne() { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "1"; + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); + + // WHEN + handler.handleRequest(new Object(), context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Test debug event"); + } + + @Test + void shouldNotLogDebugWhenSamplingEnvVarIsTooBig() { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "42"; + + // WHEN + requestHandler.handleRequest(new Object(), context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).doesNotContain("Test debug event"); + } + + @Test + void shouldNotLogDebugWhenSamplingEnvVarIsInvalid() { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "NotANumber"; + + // WHEN + requestHandler.handleRequest(new Object(), context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).doesNotContain("Test debug event"); + assertThat(contentOf(logFile)).contains( + "Skipping sampling rate on environment variable configuration because of invalid value"); + } + + @Test + void shouldNotLogDebugWhenSamplingEqualsZero() { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "0"; + PowertoolsLogSamplingDisabled handler = new PowertoolsLogSamplingDisabled(); + + // WHEN + Boolean debugEnabled = handler.handleRequest(new Object(), context); + + // THEN + assertThat(debugEnabled).isFalse(); } @Test void shouldHaveNoEffectIfNotUsedOnLambdaHandler() { - PowerLogToolEnabled handler = new PowerLogToolEnabled(); + // GIVEN + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); + // WHEN handler.anotherMethod(); - assertThat(ThreadContext.getImmutableContext()) - .isEmpty(); + // THEN + assertThat(MDC.getCopyOfContextMap()).isNull(); + } + + @Test + void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException { + // GIVEN + writeStaticField(LambdaHandlerProcessor.class, "serviceName", "testService", true); + + // WHEN + requestHandler.handleRequest(new Object(), context); + + // THEN + assertThat(MDC.getCopyOfContextMap()) + .hasSize(EXPECTED_CONTEXT_SIZE) + .containsEntry(SERVICE.getName(), "testService"); } @Test - void shouldLogEventForHandler() throws IOException, JSONException { - requestHandler = new PowerToolLogEventEnabled(); - S3EventNotification s3EventNotification = s3EventNotification(); + @ClearEnvironmentVariable(key = "_X_AMZN_TRACE_ID") + @SetSystemProperty(key = "com.amazonaws.xray.traceHeader", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") + void shouldLogxRayTraceIdSystemPropertySet() { + String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; + + requestHandler.handleRequest(new Object(), context); - requestHandler.handleRequest(s3EventNotification, context); + assertThat(MDC.getCopyOfContextMap()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry("xray_trace_id", xRayTraceId); + } - Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + @Test + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") + void shouldLogxRayTraceIdEnvVarSet() { + // GIVEN + String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; - String event = (String) log.get("message"); + // WHEN + requestHandler.handleRequest(new Object(), context); - String expectEvent = new BufferedReader( - new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) - .lines().collect(joining("\n")); + // THEN + assertThat(MDC.getCopyOfContextMap()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry(FUNCTION_TRACE_ID.getName(), xRayTraceId); + } - assertEquals(expectEvent, event, false); + @Test + void shouldLogEventForHandlerWithLogEventAnnotation() { + // GIVEN + requestHandler = new PowertoolsLogEvent(); + List<String> listOfOneElement = singletonList("ListOfOneElement"); + + // WHEN + requestHandler.handleRequest(listOfOneElement, context); + + // THEN + TestLogger logger = (TestLogger) PowertoolsLogEvent.getLogger(); + assertThat(logger.getArguments()).hasSize(1); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("event=" + listOfOneElement.toString()); } @Test - void shouldLogEventForHandlerWithOverriddenObjectMapper() throws IOException, JSONException { - RequestHandler<S3EventNotification, Object> handler = new PowerToolLogEventEnabledWithCustomMapper(); - S3EventNotification s3EventNotification = s3EventNotification(); + void shouldLogEventForHandlerWhenEnvVariableSetToTrue() { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_EVENT = true; + + requestHandler = new PowertoolsLogEventEnvVar(); - handler.handleRequest(s3EventNotification, context); + SQSEvent.SQSMessage message = new SQSEvent.SQSMessage(); + message.setBody("body"); + message.setMessageId("1234abcd"); + message.setAwsRegion("eu-west-1"); - Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + // WHEN + requestHandler.handleRequest(message, context); - String event = (String) log.get("message"); + // THEN + TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar) requestHandler).getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("event={messageId: 1234abcd,awsRegion: eu-west-1,body: body,}"); + } finally { + LoggingConstants.POWERTOOLS_LOG_EVENT = false; + if (logger != null) { + logger.clearArguments(); + } + } + } - String expectEvent = new BufferedReader( - new InputStreamReader(this.getClass().getResourceAsStream("/customizedLogEvent.json"))) - .lines().collect(joining("\n")); + @Test + void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_EVENT = false; + + // WHEN + requestHandler = new PowertoolsLogEventEnvVar(); + requestHandler.handleRequest(singletonList("ListOfOneElement"), context); - assertEquals(expectEvent, event, false); + // THEN + TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar) requestHandler).getLogger(); + assertThat(logger.getArguments()).isNull(); } @Test - void shouldLogEventForStreamAndLambdaStreamIsValid() throws IOException, JSONException { - requestStreamHandler = new PowerToolLogEventEnabledForStream(); + void shouldLogEventForStreamHandler() throws IOException { + // GIVEN + requestStreamHandler = new PowertoolsLogEventForStream(); ByteArrayOutputStream output = new ByteArrayOutputStream(); - S3EventNotification s3EventNotification = s3EventNotification(); + Map<String, String> map = Collections.singletonMap("key", "value"); - requestStreamHandler.handleRequest( - new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); + // WHEN + requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(map)), output, + context); + // THEN assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) .isNotEmpty(); - Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + TestLogger logger = (TestLogger) PowertoolsLogEventForStream.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("event={\"key\":\"value\"}"); + } finally { + logger.clearArguments(); + } + } - String event = (String) log.get("message"); + @Test + void shouldLogResponseForHandlerWithLogResponseAnnotation() { + // GIVEN + requestHandler = new PowertoolsLogResponse(); - String expectEvent = new BufferedReader( - new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) - .lines().collect(joining("\n")); + // WHEN + requestHandler.handleRequest("input", context); - assertEquals(expectEvent, event, false); + // THEN + TestLogger logger = (TestLogger) PowertoolsLogResponse.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("response=Hola mundo"); + } finally { + logger.clearArguments(); + } } @Test - void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException { - writeStaticField(LambdaHandlerProcessor.class, "SERVICE_NAME", "testService", true); - requestHandler.handleRequest(new Object(), context); + void shouldLogResponseForHandlerWhenEnvVariableSetToTrue() { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_RESPONSE = true; - assertThat(ThreadContext.getImmutableContext()) - .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry("service", "testService"); + requestHandler = new PowertoolsLogEnabled(); + + // WHEN + requestHandler.handleRequest("input", context); + + // THEN + TestLogger logger = (TestLogger) PowertoolsLogEnabled.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("response=Bonjour le monde"); + } finally { + LoggingConstants.POWERTOOLS_LOG_RESPONSE = false; + logger.clearArguments(); + } } @Test - void shouldLogxRayTraceIdEnvVarSet() { - String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; + void shouldLogResponseForStreamHandler() throws IOException { + // GIVEN + requestStreamHandler = new PowertoolsLogResponseForStream(); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + String input = "<user><firstName>Bob</firstName><lastName>The Sponge</lastName></user>"; + + // WHEN + requestStreamHandler.handleRequest(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), output, + context); + + // THEN + assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) + .isEqualTo(input); + + TestLogger logger = (TestLogger) PowertoolsLogResponseForStream.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("response=" + input); + } finally { + logger.clearArguments(); + } + } + + @Test + void shouldLogErrorForHandlerWithLogErrorAnnotation() { + // GIVEN + requestHandler = new PowertoolsLogError(); - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + // WHEN + try { + requestHandler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } - requestHandler.handleRequest(new Object(), context); + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("This is an error"); + } - assertThat(ThreadContext.getImmutableContext()) - .hasSize(EXPECTED_CONTEXT_SIZE + 1) - .containsEntry("xray_trace_id", xRayTraceId); + @Test + void shouldLogErrorForHandlerWhenEnvVariableSetToTrue() { + try { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_ERROR = true; + + requestHandler = new PowertoolsLogEnabled(true); + + // WHEN + try { + requestHandler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Something went wrong"); + } finally { + LoggingConstants.POWERTOOLS_LOG_ERROR = false; } } @ParameterizedTest @Event(value = "apiGatewayProxyEventV1.json", type = APIGatewayProxyRequestEvent.class) void shouldLogCorrelationIdOnAPIGatewayProxyRequestEvent(APIGatewayProxyRequestEvent event) { - RequestHandler<APIGatewayProxyRequestEvent, Object> handler = new PowerLogToolApiGatewayRestApiCorrelationId(); + // GIVEN + RequestHandler<APIGatewayProxyRequestEvent, Object> handler = new PowertoolsLogApiGatewayRestApiCorrelationId(); + + // WHEN handler.handleRequest(event, context); - assertThat(ThreadContext.getImmutableContext()) + // THEN + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE + 1) .containsEntry("correlation_id", event.getRequestContext().getRequestId()); } @@ -275,10 +539,14 @@ void shouldLogCorrelationIdOnAPIGatewayProxyRequestEvent(APIGatewayProxyRequestE @ParameterizedTest @Event(value = "apiGatewayProxyEventV2.json", type = APIGatewayV2HTTPEvent.class) void shouldLogCorrelationIdOnAPIGatewayV2HTTPEvent(APIGatewayV2HTTPEvent event) { - RequestHandler<APIGatewayV2HTTPEvent, Object> handler = new PowerLogToolApiGatewayHttpApiCorrelationId(); + // GIVEN + RequestHandler<APIGatewayV2HTTPEvent, Object> handler = new PowertoolsLogApiGatewayHttpApiCorrelationId(); + + // WHEN handler.handleRequest(event, context); - assertThat(ThreadContext.getImmutableContext()) + // THEN + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE + 1) .containsEntry("correlation_id", event.getRequestContext().getRequestId()); } @@ -286,96 +554,176 @@ void shouldLogCorrelationIdOnAPIGatewayV2HTTPEvent(APIGatewayV2HTTPEvent event) @ParameterizedTest @Event(value = "albEvent.json", type = ApplicationLoadBalancerRequestEvent.class) void shouldLogCorrelationIdOnALBEvent(ApplicationLoadBalancerRequestEvent event) { + // GIVEN RequestHandler<ApplicationLoadBalancerRequestEvent, Object> handler = new PowertoolsLogAlbCorrelationId(); + + // WHEN handler.handleRequest(event, context); - assertThat(ThreadContext.getImmutableContext()) + // THEN + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE + 1) .containsEntry("correlation_id", event.getHeaders().get("x-amzn-trace-id")); } @Test void shouldLogCorrelationIdOnStreamHandler() throws IOException { + // GIVEN RequestStreamHandler handler = new PowertoolsLogEventBridgeCorrelationId(); String eventId = "3"; - String event = "{\"id\":" + eventId + "}"; // CorrelationIdPathConstants.EVENT_BRIDGE + String event = "{\"id\":" + eventId + "}"; // CorrelationIdPath.EVENT_BRIDGE ByteArrayInputStream inputStream = new ByteArrayInputStream(event.getBytes()); + + // WHEN handler.handleRequest(inputStream, new ByteArrayOutputStream(), context); + // THEN + assertThat(MDC.getCopyOfContextMap()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry("correlation_id", eventId); + } + + @Test + void shouldLogCorrelationIdOnAppSyncEvent() throws IOException { + // GIVEN + RequestStreamHandler handler = new PowertoolsLogAppSyncCorrelationId(); + String eventId = "456"; + String event = "{\"request\":{\"headers\":{\"x-amzn-trace-id\":" + eventId + "}}}"; // CorrelationIdPath.APPSYNC_RESOLVER + ByteArrayInputStream inputStream = new ByteArrayInputStream(event.getBytes()); + + // WHEN + handler.handleRequest(inputStream, new ByteArrayOutputStream(), context); - assertThat(ThreadContext.getImmutableContext()) + // THEN + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE + 1) .containsEntry("correlation_id", eventId); } @Test - void shouldLogAndClearLogContextOnEachRequest() throws IOException { - requestHandler = new PowertoolsLogEnabledWithClearState(); - S3EventNotification s3EventNotification = s3EventNotification(); + void shouldClearBufferAfterSuccessfulHandlerExecution() { + // WHEN + requestHandler.handleRequest(new Object(), context); + + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferCleared()).isTrue(); + } + } - requestHandler.handleRequest(s3EventNotification, context); - requestHandler.handleRequest(s3EventNotification, context); + @Test + void shouldClearBufferAfterSuccessfulStreamHandlerExecution() throws IOException { + // WHEN + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); - List<String> logLines = Files.lines(Paths.get("target/logfile.json")).collect(Collectors.toList()); - Map<String, Object> invokeLog = parseToMap(logLines.get(0)); + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferCleared()).isTrue(); + } + } - assertThat(invokeLog) - .containsEntry("TestKey", "TestValue"); + @Test + void shouldClearBufferAfterHandlerExceptionWithLogError() { + // GIVEN + requestHandler = new PowertoolsLogError(); - invokeLog = parseToMap(logLines.get(1)); + // WHEN + try { + requestHandler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } - assertThat(invokeLog) - .doesNotContainKey("TestKey"); + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferCleared()).isTrue(); + } } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - when(context.getAwsRequestId()).thenReturn("RequestId"); + @Test + void shouldClearBufferAfterHandlerExceptionWithEnvVarLogError() { + try { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_ERROR = true; + requestHandler = new PowertoolsLogEnabled(true); + + // WHEN + try { + requestHandler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } + + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferCleared()).isTrue(); + } + } finally { + LoggingConstants.POWERTOOLS_LOG_ERROR = false; + } } - private void resetLogLevel(Level level) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); - resetLogLevels.setAccessible(true); - resetLogLevels.invoke(null, level); - writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); + @Test + void shouldFlushBufferOnUncaughtErrorWhenEnabled() { + // GIVEN + requestHandler = new PowertoolsLogError(); + + // WHEN + try { + requestHandler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } + + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferFlushed()).isTrue(); + } } - private S3EventNotification s3EventNotification() { - S3EventNotification.S3EventNotificationRecord record = - new S3EventNotification.S3EventNotificationRecord("us-west-2", - "ObjectCreated:Put", - "aws:s3", - null, - "2.1", - new S3EventNotification.RequestParametersEntity("127.0.0.1"), - new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", - "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), - new S3EventNotification.S3Entity("testConfigRule", - new S3EventNotification.S3BucketEntity("mybucket", - new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), - "arn:aws:s3:::mybucket"), - new S3EventNotification.S3ObjectEntity("HappyFace.jpg", - 1024L, - "d41d8cd98f00b204e9800998ecf8427e", - "096fKKXTRTtl3on89fVO.nfljtsv6qko", - "0055AED6DCD90281E5"), - "1.0"), - new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") - ); + @Test + void shouldNotFlushBufferOnUncaughtErrorWhenDisabled() { + // GIVEN + PowertoolsLogErrorNoFlush handler = new PowertoolsLogErrorNoFlush(); - return new S3EventNotification(singletonList(record)); + // WHEN + try { + handler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } + + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferFlushed()).isFalse(); + } } - private Map<String, Object> parseToMap(String stringAsJson) { + @Test + void shouldClearBufferBeforeErrorLoggingWhenFlushBufferOnUncaughtErrorDisabled() { + // GIVEN + PowertoolsLogErrorNoFlush handler = new PowertoolsLogErrorNoFlush(); + + // WHEN try { - return new ObjectMapper().readValue(stringAsJson, Map.class); - } catch (JsonProcessingException e) { - fail("Failed parsing logger line " + stringAsJson); - return emptyMap(); + handler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } + + // THEN - Buffer should be cleared and not flushed + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferCleared()).isTrue(); + assertThat(((TestLoggingManager) loggingManager).isBufferFlushed()).isFalse(); } } -} \ No newline at end of file + +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistryTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistryTest.java new file mode 100644 index 000000000..6bed0d9c1 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistryTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Test; + +class LoggingManagerRegistryTest { + + @Test + void testMultipleLoggingManagers_shouldWarnAndSelectFirstOne() throws UnsupportedEncodingException { + // GIVEN + List<LoggingManager> list = new ArrayList<>(); + list.add(new TestLoggingManager()); + list.add(new DefaultLoggingManager()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(outputStream); + + // WHEN + LoggingManagerRegistry.selectLoggingManager(list, stream); + + // THEN + String output = outputStream.toString("UTF-8"); + assertThat(output) + .contains("WARN. Multiple LoggingManagers were found on the classpath") + .contains( + "WARN. Make sure to have only one of powertools-logging-log4j OR powertools-logging-logback in your dependencies") + .contains("WARN. Using the first LoggingManager found on the classpath: [" + list.get(0) + "]"); + } + + @Test + void testNoLoggingManagers_shouldWarnAndCreateDefault() throws UnsupportedEncodingException { + // GIVEN + List<LoggingManager> list = new ArrayList<>(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(outputStream); + + // WHEN + LoggingManager loggingManager = LoggingManagerRegistry.selectLoggingManager(list, stream); + + // THEN + String output = outputStream.toString("UTF-8"); + assertThat(output) + .contains("ERROR. No LoggingManager was found on the classpath") + .contains("ERROR. Applying default LoggingManager: POWERTOOLS_LOG_LEVEL variable is ignored") + .contains( + "ERROR. Make sure to add either powertools-logging-log4j or powertools-logging-logback to your dependencies"); + + assertThat(loggingManager).isExactlyInstanceOf(DefaultLoggingManager.class); + } + + @Test + void testSingleLoggingManager_shouldReturnWithoutWarning() throws UnsupportedEncodingException { + // GIVEN + List<LoggingManager> list = new ArrayList<>(); + TestLoggingManager testManager = new TestLoggingManager(); + list.add(testManager); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(outputStream); + + // WHEN + LoggingManager loggingManager = LoggingManagerRegistry.selectLoggingManager(list, stream); + + // THEN + String output = outputStream.toString("UTF-8"); + assertThat(output).isEmpty(); + assertThat(loggingManager) + .isSameAs(testManager) + .isInstanceOf(BufferManager.class); + } + + @Test + void testGetLoggingManager_shouldReturnSameInstance() { + // WHEN + LoggingManager first = LoggingManagerRegistry.getLoggingManager(); + LoggingManager second = LoggingManagerRegistry.getLoggingManager(); + + // THEN + assertThat(first) + .isSameAs(second) + .isNotNull() + .isInstanceOf(BufferManager.class); + } + + @Test + void testGetLoggingManager_shouldBeThreadSafe() throws InterruptedException, IOException { + // GIVEN + int threadCount = 10; + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + try (Closeable ignored = executor::shutdown) { + CountDownLatch latch = new CountDownLatch(threadCount); + AtomicReference<LoggingManager> sharedInstance = new AtomicReference<>(); + + // WHEN + for (int i = 0; i < threadCount; i++) { + executor.submit(() -> { + try { + LoggingManager instance = LoggingManagerRegistry.getLoggingManager(); + sharedInstance.compareAndSet(null, instance); + assertThat(instance).isSameAs(sharedInstance.get()); + } finally { + latch.countDown(); + } + }); + } + + // THEN + latch.await(5, TimeUnit.SECONDS); + assertThat(sharedInstance.get()).isNotNull(); + } + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/TestLoggingManager.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/TestLoggingManager.java new file mode 100644 index 000000000..f2aa2417e --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/TestLoggingManager.java @@ -0,0 +1,57 @@ +package software.amazon.lambda.powertools.logging.internal; + +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; +import org.slf4j.test.TestLogger; +import org.slf4j.test.TestLoggerFactory; + +public class TestLoggingManager implements LoggingManager, BufferManager { + + private final TestLoggerFactory loggerFactory; + private boolean bufferFlushed = false; + private boolean bufferCleared = false; + + public TestLoggingManager() { + ILoggerFactory loggerFactoryInstance = LoggerFactory.getILoggerFactory(); + if (!(loggerFactoryInstance instanceof TestLoggerFactory)) { + throw new RuntimeException( + "LoggerFactory does not match required type: " + TestLoggerFactory.class.getName()); + } + this.loggerFactory = (TestLoggerFactory) loggerFactoryInstance; + } + + @Override + public void setLogLevel(Level logLevel) { + loggerFactory.getLoggers().forEach((key, logger) -> ((TestLogger) logger).setLogLevel(logLevel.toString())); + } + + @Override + public Level getLogLevel(Logger logger) { + return org.slf4j.event.Level.intToLevel(((TestLogger) logger).getLogLevel()); + } + + @Override + public void flushBuffer() { + bufferFlushed = true; + } + + @Override + public void clearBuffer() { + bufferCleared = true; + } + + public boolean isBufferFlushed() { + return bufferFlushed; + } + + public boolean isBufferCleared() { + return bufferCleared; + } + + public void resetBufferState() { + bufferFlushed = false; + bufferCleared = false; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Basket.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Basket.java new file mode 100644 index 000000000..0fa544cf1 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Basket.java @@ -0,0 +1,67 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class Basket { + private List<Product> products = new ArrayList<>(); + + public Basket() { + } + + public Basket(Product... p) { + products.addAll(Arrays.asList(p)); + } + + public List<Product> getProducts() { + return products; + } + + public void setProducts(List<Product> products) { + this.products = products; + } + + public void add(Product product) { + products.add(product); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Basket basket = (Basket) o; + return products.equals(basket.products); + } + + @Override + public int hashCode() { + return Objects.hash(products); + } + + @Override + public String toString() { + return "Basket{" + + "products=" + products + + '}'; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Product.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Product.java new file mode 100644 index 000000000..fee5bc20d --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Product.java @@ -0,0 +1,84 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.logging.model; + +import java.util.Objects; + +public class Product { + private long id; + + private String name; + + private double price; + + public Product() { + } + + public Product(long id, String name, double price) { + this.id = id; + this.name = name; + this.price = price; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Product product = (Product) o; + return id == product.id && Double.compare(product.price, price) == 0 && Objects.equals(name, product.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, price); + } + + @Override + public String toString() { + return "Product{" + + "id=" + id + + ", name='" + name + '\'' + + ", price=" + price + + '}'; + } +} diff --git a/powertools-logging/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/powertools-logging/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 000000000..ade4bb1e2 --- /dev/null +++ b/powertools-logging/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider @@ -0,0 +1 @@ +org.slf4j.test.TestServiceProvider \ No newline at end of file diff --git a/powertools-logging/src/test/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager b/powertools-logging/src/test/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager new file mode 100644 index 000000000..adbf7ae69 --- /dev/null +++ b/powertools-logging/src/test/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager @@ -0,0 +1,15 @@ +# +# Copyright 2023 Amazon.com, Inc. or its affiliates. +# Licensed under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +software.amazon.lambda.powertools.logging.internal.TestLoggingManager \ No newline at end of file diff --git a/powertools-logging/src/test/resources/s3EventNotification.json b/powertools-logging/src/test/resources/s3EventNotification.json deleted file mode 100644 index feb88ec02..000000000 --- a/powertools-logging/src/test/resources/s3EventNotification.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "records":[ - { - "eventVersion":"2.1", - "eventSource":"aws:s3", - "awsRegion":"us-west-2", - "eventName":"ObjectCreated:Put", - "userIdentity":{ - "principalId":"AIDAJDPLRKLG7UEXAMPLE" - }, - "requestParameters":{ - "sourceIPAddress":"127.0.0.1" - }, - "responseElements":{ - "xAmzId2":"C3D13FE58DE4C810", - "xAmzRequestId":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD" - }, - "s3":{ - "s3SchemaVersion":"1.0", - "configurationId":"testConfigRule", - "bucket":{ - "name":"mybucket", - "ownerIdentity":{ - "principalId":"A3NL1KOZZKExample" - }, - "arn":"arn:aws:s3:::mybucket" - }, - "object":{ - "key":"HappyFace.jpg", - "size":1024, - "eTag":"d41d8cd98f00b204e9800998ecf8427e", - "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko", - "sequencer":"0055AED6DCD90281E5" - } - } - } - ] -} \ No newline at end of file diff --git a/powertools-logging/src/test/resources/testlogger.properties b/powertools-logging/src/test/resources/testlogger.properties new file mode 100644 index 000000000..84b7beaae --- /dev/null +++ b/powertools-logging/src/test/resources/testlogger.properties @@ -0,0 +1,3 @@ +org.slf4j.simpleLogger.defaultLogLevel=warn +org.slf4j.simpleLogger.log.software.amazon.lambda.powertools=info +org.slf4j.simpleLogger.logFile=target/logfile.json \ No newline at end of file diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 7b2e99045..5f6c971e3 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -14,8 +14,8 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>powertools-metrics</artifactId> @@ -24,41 +24,28 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> - <name>Powertools for AWS Lambda (Java) library Metrics</name> + <name>Powertools for AWS Lambda (Java) - Metrics</name> <description> A suite of utilities for AWS Lambda Functions that make creating custom metrics via AWS Embedded Metric Format asynchronously easier. </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -82,10 +69,14 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <scope>provided</scope> </dependency> <!-- Test dependencies --> @@ -100,18 +91,13 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> <scope>test</scope> </dependency> <dependency> @@ -124,14 +110,131 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> + <profiles> + <profile> + <id>generate-classesloaded-file</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Xlog:class+load=info:classesloaded.txt + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-metrics</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> + <resources> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_EMF_ENVIRONMENT>Lambda</AWS_EMF_ENVIRONMENT> + <AWS_LAMBDA_INITIALIZATION_TYPE>on-demand</AWS_LAMBDA_INITIALIZATION_TYPE> + </environmentVariables> + </configuration> </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> diff --git a/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java b/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java deleted file mode 100644 index e2d886fe5..000000000 --- a/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.cloudwatchlogs.emf.model; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - -import java.lang.reflect.Field; - -public final class MetricsLoggerHelper { - private MetricsLoggerHelper() { - } - - public static boolean hasNoMetrics() { - return metricsContext().getRootNode().getAws().isEmpty(); - } - - public static long dimensionsCount() { - return metricsContext().getDimensions().size(); - } - - public static MetricsContext metricsContext() { - try { - Field f = metricsLogger().getClass().getDeclaredField("context"); - f.setAccessible(true); - return (MetricsContext) f.get(metricsLogger()); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/FlushMetrics.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/FlushMetrics.java new file mode 100644 index 000000000..952625f5b --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/FlushMetrics.java @@ -0,0 +1,71 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@code FlushMetrics} is used to signal that the annotated Lambda handler method should be + * extended with Metrics flushing functionality. Will have no effect when used on a method that is not a Lambda handler. + * + * <p>{@code FlushMetrics} allows users to asynchronously create Amazon + * CloudWatch metrics by using the CloudWatch <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html">Embedded Metrics Format</a>. + * {@code FlushMetrics} manages the life-cycle and configuration of Metrics to simplify the user experience when used with AWS Lambda. + * + * <p>{@code FlushMetrics} should be used with the handleRequest method of a class + * which implements either + * {@code com.amazonaws.services.lambda.runtime.RequestHandler} or + * {@code com.amazonaws.services.lambda.runtime.RequestStreamHandler}.</p> + * + * <p>{@code FlushMetrics} creates Amazon CloudWatch custom metrics. You can find + * pricing information on the <a href="https://aws.amazon.com/cloudwatch/pricing/">CloudWatch pricing documentation</a> page.</p> + * + * <p>To enable creation of custom metrics for cold starts you can add {@code @FlushMetrics(captureColdStart = true)}. + * </br>This will create a metric with the key {@code "ColdStart"} and the unit type {@code COUNT}. + * </p> + * + * <p>To raise exception if no metrics are emitted, use {@code @FlushMetrics(raiseOnEmptyMetrics = true)}. + * </br>This will create an exception if no metrics are emitted. By default its value is set to false. + * </p> + * + * <p>By default the service name associated with metrics created will be + * "service_undefined". This can be overridden with the environment variable {@code POWERTOOLS_SERVICE_NAME} + * or the annotation variable {@code @FlushMetrics(service = "Service Name")}. + * If both are specified then the value of the annotation variable will be used.</p> + * + * <p>A namespace must be specified for metrics. This can be set with the environment variable {@code POWERTOOLS_METRICS_NAMESPACE} + * or the annotation variable {@code @FlushMetrics(namespace = "Namespace")}. If not specified, an IllegalStateException will be thrown. + * If both are specified then the value of the annotation variable will be used.</p> + * + * <p>You can specify a custom function name with {@code @FlushMetrics(functionName = "MyFunction")}. + * If specified, this will be used instead of the function name from the Lambda context. + * </p> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface FlushMetrics { + String namespace() default ""; + + String service() default ""; + + String functionName() default ""; + + boolean captureColdStart() default false; + + boolean raiseOnEmptyMetrics() default false; +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java index fb92c900d..77db2aba0 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java @@ -14,54 +14,194 @@ package software.amazon.lambda.powertools.metrics; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import com.amazonaws.services.lambda.runtime.Context; +import java.time.Instant; +import java.util.function.Consumer; + +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; /** - * {@code Metrics} is used to signal that the annotated method should be - * extended with Metrics functionality. - * - * <p>{@code Metrics} allows users to asynchronously create Amazon - * CloudWatch metrics by using the CloudWatch <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html">Embedded Metrics Format</a>. - * {@code Metrics} manages the life-cycle of the MetricsLogger class, - * to simplify the user experience when used with AWS Lambda. - * - * <p>{@code Metrics} should be used with the handleRequest method of a class - * which implements either - * {@code com.amazonaws.services.lambda.runtime.RequestHandler} or - * {@code com.amazonaws.services.lambda.runtime.RequestStreamHandler}.</p> - * - * <p>{@code Metrics} creates Amazon CloudWatch custom metrics. You can find - * pricing information on the <a href="https://aws.amazon.com/cloudwatch/pricing/">CloudWatch pricing documentation</a> page.</p> - * - * <p>To enable creation of custom metrics for cold starts you can add {@code @Metrics(captureColdStart = true)}. - * </br>This will create a metric with the key {@code "ColdStart"} and the unit type {@code COUNT}. - * </p> - * - * <p>To raise exception if no metrics are emitted, use {@code @Metrics(raiseOnEmptyMetrics = true)}. - * </br>This will create a create a exception of type {@link ValidationException}. By default its value is set to false. - * </p> - * - * <p>By default the service name associated with metrics created will be - * "service_undefined". This can be overridden with the environment variable {@code POWERTOOLS_SERVICE_NAME} - * or the annotation variable {@code @Metrics(service = "Service Name")}. - * If both are specified then the value of the annotation variable will be used.</p> - * - * <p>By default the namespace associated with metrics created will be "aws-embedded-metrics". - * This can be overridden with the environment variable {@code POWERTOOLS_METRICS_NAMESPACE} - * or the annotation variable {@code @Metrics(namespace = "Namespace")}. - * If both are specified then the value of the annotation variable will be used.</p> + * Interface for metrics implementations. + * This interface is used to collect metrics in the Lambda function. + * It provides methods to add metrics, dimensions, and metadata. */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Metrics { - String namespace() default ""; +public interface Metrics { + + /** + * Add a metric + * + * @param key the name of the metric + * @param value the value of the metric + * @param unit the unit of the metric + * @param resolution the resolution of the metric + */ + void addMetric(String key, double value, MetricUnit unit, MetricResolution resolution); + + /** + * Add a metric with default resolution + * + * @param key the name of the metric + * @param value the value of the metric + * @param unit the unit of the metric + */ + default void addMetric(String key, double value, MetricUnit unit) { + addMetric(key, value, unit, MetricResolution.STANDARD); + } + + /** + * Add a metric with default unit and resolution + * + * @param key the name of the metric + * @param value the value of the metric + */ + default void addMetric(String key, double value) { + addMetric(key, value, MetricUnit.NONE, MetricResolution.STANDARD); + } + + /** + * Add a dimension + * This is equivalent to calling {@code addDimension(DimensionSet.of(key, value))} + * + * @param key the name of the dimension + * @param value the value of the dimension + */ + default void addDimension(String key, String value) { + addDimension(DimensionSet.of(key, value)); + } + + /** + * Add a dimension set + * + * @param dimensionSet the dimension set to add + */ + void addDimension(DimensionSet dimensionSet); + + /** + * Set a custom timestamp for the metrics + * + * @param timestamp the timestamp to use for the metrics + */ + void setTimestamp(Instant timestamp); + + /** + * Add metadata + * + * @param key the name of the metadata + * @param value the value of the metadata + */ + void addMetadata(String key, Object value); + + /** + * Set default dimensions + * + * @param dimensionSet the dimension set to use as default dimensions + */ + void setDefaultDimensions(DimensionSet dimensionSet); + + /** + * Get the default dimensions + * + * @return the default dimensions as a DimensionSet + */ + DimensionSet getDefaultDimensions(); + + /** + * Set the namespace + * + * @param namespace the namespace + */ + void setNamespace(String namespace); + + /** + * Set whether to raise an exception if no metrics are emitted + * + * @param raiseOnEmptyMetrics true to raise an exception, false otherwise + */ + void setRaiseOnEmptyMetrics(boolean raiseOnEmptyMetrics); + + /** + * Clear default dimensions + */ + void clearDefaultDimensions(); + + /** + * Flush metrics to the configured sink + */ + void flush(); + + /** + * Capture cold start metric and flush immediately + * + * @param context Lambda context + * @param dimensions custom dimensions for this metric (optional) + */ + void captureColdStartMetric(Context context, DimensionSet dimensions); + + /** + * Capture cold start metric and flush immediately + * + * @param context Lambda context + */ + default void captureColdStartMetric(Context context) { + captureColdStartMetric(context, null); + } + + /** + * Capture cold start metric without Lambda context and flush immediately + * + * @param dimensions custom dimensions for this metric (optional) + */ + void captureColdStartMetric(DimensionSet dimensions); + + /** + * Capture cold start metric without Lambda context and flush immediately + */ + default void captureColdStartMetric() { + captureColdStartMetric((DimensionSet) null); + } + + /** + * Flush a separate metrics context that inherits the namespace, default dimensions, and metadata. This creates a separate metrics context + * that doesn't affect the default metrics context. + * + * @param metricsConsumer the consumer to use to edit the metrics instance (e.g. add metrics, override namespace, set or add custom dimensions) before flushing + */ + void flushMetrics(Consumer<Metrics> metricsConsumer); - String service() default ""; + /** + * Flush a single metric with custom namespace and dimensions. This creates a separate metrics context + * that doesn't affect the default metrics context. + * + * @param name the name of the metric + * @param value the value of the metric + * @param unit the unit of the metric + * @param namespace the namespace for the metric + * @param dimensions custom dimensions for this metric (optional) + */ + default void flushSingleMetric(String name, double value, MetricUnit unit, String namespace, DimensionSet dimensions) { + flushMetrics(metrics -> { + metrics.setNamespace(namespace); + metrics.setDefaultDimensions(dimensions); + metrics.addMetric(name, value, unit); + }); - boolean captureColdStart() default false; + } - boolean raiseOnEmptyMetrics() default false; + /** + * Flush a single metric with custom namespace. This creates a separate metrics context + * that doesn't affect the default metrics context. + * + * @param name the name of the metric + * @param value the value of the metric + * @param unit the unit of the metric + * @param namespace the namespace for the metric + */ + default void flushSingleMetric(String name, double value, MetricUnit unit, String namespace) { + flushMetrics(metrics -> { + metrics.setNamespace(namespace); + metrics.addMetric(name, value, unit); + }); + } } diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsBuilder.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsBuilder.java new file mode 100644 index 000000000..2eb127b00 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsBuilder.java @@ -0,0 +1,144 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics; + +import java.util.LinkedHashMap; +import java.util.Map; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; + +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; + +/** + * Builder for configuring the singleton Metrics instance + */ +public class MetricsBuilder { + private MetricsProvider provider; + private String namespace; + private String service; + private boolean raiseOnEmptyMetrics = false; + private final Map<String, String> defaultDimensions = new LinkedHashMap<>(); + + private MetricsBuilder() { + } + + /** + * Create a new builder instance + * + * @return a new builder instance + */ + public static MetricsBuilder builder() { + return new MetricsBuilder(); + } + + /** + * Set the metrics provider + * + * @param provider the metrics provider + * @return this builder + */ + public MetricsBuilder withMetricsProvider(MetricsProvider provider) { + this.provider = provider; + return this; + } + + /** + * Set the namespace + * + * @param namespace the namespace + * @return this builder + */ + public MetricsBuilder withNamespace(String namespace) { + this.namespace = namespace; + return this; + } + + /** + * Set the service name. Does not apply if used in combination with default dimensions. If you would like to use a + * service name with default dimensions, use {@link #withDefaultDimension(String, String)} instead. + * + * @param service the service name + * @return this builder + */ + public MetricsBuilder withService(String service) { + this.service = service; + return this; + } + + /** + * Set whether to raise an exception if no metrics are emitted + * + * @param raiseOnEmptyMetrics true to raise an exception, false otherwise + * @return this builder + */ + public MetricsBuilder withRaiseOnEmptyMetrics(boolean raiseOnEmptyMetrics) { + this.raiseOnEmptyMetrics = raiseOnEmptyMetrics; + return this; + } + + /** + * Add a default dimension. + * + * @param key the dimension key + * @param value the dimension value + * @return this builder + */ + public MetricsBuilder withDefaultDimension(String key, String value) { + this.defaultDimensions.put(key, value); + return this; + } + + /** + * Add default dimensions + * + * @param dimensionSet the dimension set to add + * @return this builder + */ + public MetricsBuilder withDefaultDimensions(DimensionSet dimensionSet) { + if (dimensionSet != null) { + this.defaultDimensions.putAll(dimensionSet.getDimensions()); + } + return this; + } + + /** + * Configure and return the singleton Metrics instance + * + * @return the configured singleton Metrics instance + */ + public Metrics build() { + if (provider != null) { + MetricsFactory.setMetricsProvider(provider); + } + + Metrics metrics = MetricsFactory.getMetricsInstance(); + + if (namespace != null) { + metrics.setNamespace(namespace); + } + + metrics.setRaiseOnEmptyMetrics(raiseOnEmptyMetrics); + + if (service != null) { + metrics.setDefaultDimensions(DimensionSet.of("Service", service)); + } + + // If the user provided default dimension, we overwrite the default Service dimension again + if (!defaultDimensions.isEmpty()) { + metrics.setDefaultDimensions(DimensionSet.of(defaultDimensions)); + } + + return metrics; + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java new file mode 100644 index 000000000..0644329c9 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java @@ -0,0 +1,92 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics; + +import org.crac.Core; +import org.crac.Resource; + +import software.amazon.lambda.powertools.common.internal.ClassPreLoader; +import software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.internal.RequestScopedMetricsProxy; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; + +/** + * Factory for accessing the singleton Metrics instance + */ +public final class MetricsFactory implements Resource { + private static MetricsProvider provider = new EmfMetricsProvider(); + private static RequestScopedMetricsProxy metricsProxy; + + // Dummy instance to register MetricsFactory with CRaC + private static final MetricsFactory INSTANCE = new MetricsFactory(); + + // Static block to ensure CRaC registration happens at class loading time + static { + Core.getGlobalContext().register(INSTANCE); + } + + /** + * Get the singleton instance of the Metrics + * + * @return the singleton Metrics instance + */ + public static synchronized Metrics getMetricsInstance() { + if (metricsProxy == null) { + metricsProxy = new RequestScopedMetricsProxy(provider); + + // Apply default configuration from environment variables + String envNamespace = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); + if (envNamespace != null) { + metricsProxy.setNamespace(envNamespace); + } + + // Only set Service dimension if it's not the default undefined value + String serviceName = LambdaHandlerProcessor.serviceName(); + if (!LambdaConstants.SERVICE_UNDEFINED.equals(serviceName)) { + metricsProxy.setDefaultDimensions(DimensionSet.of("Service", serviceName)); + } + } + + return metricsProxy; + } + + /** + * Set the metrics provider + * + * @param metricsProvider the metrics provider + */ + public static synchronized void setMetricsProvider(MetricsProvider metricsProvider) { + if (metricsProvider == null) { + throw new IllegalArgumentException("Metrics provider cannot be null"); + } + provider = metricsProvider; + // Reset the metrics proxy so it will be recreated with the new provider + metricsProxy = null; + } + + @Override + public void beforeCheckpoint(org.crac.Context<? extends Resource> context) throws Exception { + MetricsFactory.getMetricsInstance(); + ClassPreLoader.preloadClasses(); + } + + @Override + public void afterRestore(org.crac.Context<? extends Resource> context) throws Exception { + // No action needed after restore + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java deleted file mode 100644 index 09517d46e..000000000 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics; - -import static java.util.Objects.requireNonNull; -import static java.util.Optional.ofNullable; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.getXrayTraceId; -import static software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect.REQUEST_ID_PROPERTY; -import static software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect.TRACE_ID_PROPERTY; - -import java.util.Arrays; -import java.util.Optional; -import java.util.function.Consumer; -import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; -import software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.MetricsContext; -import software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper; -import software.amazon.cloudwatchlogs.emf.model.Unit; - -/** - * A class used to retrieve the instance of the {@code MetricsLogger} used by - * {@code Metrics}. - * <p> - * {@see Metrics} - */ -public final class MetricsUtils { - private static final MetricsLogger metricsLogger = new MetricsLogger(); - private static DimensionSet[] defaultDimensions; - - private MetricsUtils() { - } - - /** - * The instance of the {@code MetricsLogger} used by {@code Metrics}. - * - * @return The instance of the MetricsLogger used by Metrics. - */ - public static MetricsLogger metricsLogger() { - return metricsLogger; - } - - /** - * Configure default dimension to be used by logger. - * By default, @{@link Metrics} annotation captures configured service as a dimension <i>Service</i> - * - * @param dimensionSets Default value of dimensions set for logger - */ - public static void defaultDimensions(final DimensionSet... dimensionSets) { - MetricsUtils.defaultDimensions = dimensionSets; - } - - /** - * Configure default dimension to be used by logger. - * By default, @{@link Metrics} annotation captures configured service as a dimension <i>Service</i> - * - * @param dimensionSet Default value of dimension set for logger - * @deprecated use {@link #defaultDimensions(DimensionSet...)} instead - */ - @Deprecated - public static void defaultDimensionSet(final DimensionSet dimensionSet) { - requireNonNull(dimensionSet, "Null dimension set not allowed"); - - if (dimensionSet.getDimensionKeys().size() > 0) { - defaultDimensions(dimensionSet); - } - } - - - /** - * Add and immediately flush a single metric. It will use the default namespace - * specified either on {@link Metrics} annotation or via POWERTOOLS_METRICS_NAMESPACE env var. - * It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also - * capture xray_trace_id as property if tracing is enabled. - * - * @param name the name of the metric - * @param value the value of the metric - * @param unit the unit type of the metric - * @param logger the MetricsLogger - */ - public static void withSingleMetric(final String name, - final double value, - final Unit unit, - final Consumer<MetricsLogger> logger) { - withMetricsLogger(metricsLogger -> - { - metricsLogger.putMetric(name, value, unit); - logger.accept(metricsLogger); - }); - } - - /** - * Add and immediately flush a single metric. - * It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also - * capture xray_trace_id as property if tracing is enabled. - * - * @param name the name of the metric - * @param value the value of the metric - * @param unit the unit type of the metric - * @param namespace the namespace associated with the metric - * @param logger the MetricsLogger - */ - public static void withSingleMetric(final String name, - final double value, - final Unit unit, - final String namespace, - final Consumer<MetricsLogger> logger) { - withMetricsLogger(metricsLogger -> - { - metricsLogger.setNamespace(namespace); - metricsLogger.putMetric(name, value, unit); - logger.accept(metricsLogger); - }); - } - - /** - * Provide and immediately flush a {@link MetricsLogger}. It uses the default namespace - * specified either on {@link Metrics} annotation or via POWERTOOLS_METRICS_NAMESPACE env var. - * It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also - * capture xray_trace_id as property if tracing is enabled. - * - * @param logger the MetricsLogger - */ - public static void withMetricsLogger(final Consumer<MetricsLogger> logger) { - MetricsLogger metricsLogger = logger(); - - try { - metricsLogger.setNamespace(defaultNameSpace()); - captureRequestAndTraceId(metricsLogger); - logger.accept(metricsLogger); - } finally { - metricsLogger.flush(); - } - } - - /** - * Provide and immediately flush a {@link MetricsLogger}. It uses the default namespace - * specified either on {@link Metrics} annotation or via POWERTOOLS_METRICS_NAMESPACE env var. - * It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also - * capture xray_trace_id as property if tracing is enabled. - * - * @param logger the MetricsLogger - * @deprecated use {@link MetricsUtils#withMetricsLogger} instead - */ - @Deprecated - public static void withMetricLogger(final Consumer<MetricsLogger> logger) { - withMetricsLogger(logger); - } - - public static DimensionSet[] getDefaultDimensions() { - return Arrays.copyOf(defaultDimensions, defaultDimensions.length); - } - - public static boolean hasDefaultDimension() { - return null != defaultDimensions; - } - - private static void captureRequestAndTraceId(MetricsLogger metricsLogger) { - awsRequestId(). - ifPresent(requestId -> metricsLogger.putProperty(REQUEST_ID_PROPERTY, requestId)); - - getXrayTraceId() - .ifPresent(traceId -> metricsLogger.putProperty(TRACE_ID_PROPERTY, traceId)); - } - - private static String defaultNameSpace() { - MetricsContext context = MetricsLoggerHelper.metricsContext(); - return "aws-embedded-metrics".equals(context.getNamespace()) ? - SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE") : context.getNamespace(); - } - - private static Optional<String> awsRequestId() { - MetricsContext context = MetricsLoggerHelper.metricsContext(); - return ofNullable(context.getProperty(REQUEST_ID_PROPERTY)) - .map(Object::toString); - } - - private static MetricsLogger logger() { - MetricsContext metricsContext = new MetricsContext(); - - if (hasDefaultDimension()) { - metricsContext.setDimensions(defaultDimensions); - } - - return new MetricsLogger(new EnvironmentProvider(), metricsContext); - } -} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java new file mode 100644 index 000000000..611d4dcc6 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java @@ -0,0 +1,296 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.internal; + +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isColdStart; + +import java.time.Instant; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; + +import software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider; +import software.amazon.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.MetricsContext; +import software.amazon.cloudwatchlogs.emf.model.StorageResolution; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +/** + * Implementation of Metrics that uses the EMF library. Proxies Metrics interface calls to underlying + * library {@link software.amazon.cloudwatchlogs.emf.logger.MetricsLogger}. + */ +public class EmfMetricsLogger implements Metrics { + private static final Logger LOGGER = LoggerFactory.getLogger(EmfMetricsLogger.class); + private static final String COLD_START_METRIC = "ColdStart"; + private static final String METRICS_DISABLED_ENV_VAR = "POWERTOOLS_METRICS_DISABLED"; + + private final software.amazon.cloudwatchlogs.emf.logger.MetricsLogger emfLogger; + private final EnvironmentProvider environmentProvider; + private final AtomicBoolean raiseOnEmptyMetrics = new AtomicBoolean(false); + private String namespace; + private Map<String, String> defaultDimensions = new HashMap<>(); + private final Map<String, Object> properties = new HashMap<>(); + private final AtomicBoolean hasMetrics = new AtomicBoolean(false); + + public EmfMetricsLogger(EnvironmentProvider environmentProvider, MetricsContext metricsContext) { + this.emfLogger = new software.amazon.cloudwatchlogs.emf.logger.MetricsLogger(environmentProvider, + metricsContext); + this.environmentProvider = environmentProvider; + } + + @Override + public void addMetric(String key, double value, MetricUnit unit, MetricResolution resolution) { + StorageResolution storageResolution = resolution == MetricResolution.HIGH ? StorageResolution.HIGH + : StorageResolution.STANDARD; + emfLogger.putMetric(key, value, convertUnit(unit), storageResolution); + hasMetrics.set(true); + } + + @Override + public void addDimension(software.amazon.lambda.powertools.metrics.model.DimensionSet dimensionSet) { + if (dimensionSet == null) { + throw new IllegalArgumentException("DimensionSet cannot be null"); + } + + DimensionSet emfDimensionSet = new DimensionSet(); + dimensionSet.getDimensions().forEach((key, val) -> { + try { + emfDimensionSet.addDimension(key, val); + } catch (Exception e) { + // Ignore dimension errors + } + }); + + emfLogger.putDimensions(emfDimensionSet); + } + + @Override + public void addMetadata(String key, Object value) { + emfLogger.putProperty(key, value); + properties.put(key, value); + } + + @Override + public void setDefaultDimensions(software.amazon.lambda.powertools.metrics.model.DimensionSet dimensionSet) { + if (dimensionSet == null) { + throw new IllegalArgumentException("DimensionSet cannot be null"); + } + + DimensionSet emfDimensionSet = new DimensionSet(); + Map<String, String> dimensions = dimensionSet.getDimensions(); + dimensions.forEach((key, value) -> { + try { + emfDimensionSet.addDimension(key, value); + } catch (Exception e) { + // Ignore dimension errors + } + }); + emfLogger.setDimensions(emfDimensionSet); + // Store a copy of the default dimensions + this.defaultDimensions = new LinkedHashMap<>(dimensions); + } + + @Override + public software.amazon.lambda.powertools.metrics.model.DimensionSet getDefaultDimensions() { + return software.amazon.lambda.powertools.metrics.model.DimensionSet.of(defaultDimensions); + } + + @Override + public void setNamespace(String namespace) { + Validator.validateNamespace(namespace); + + this.namespace = namespace; + try { + emfLogger.setNamespace(namespace); + } catch (Exception e) { + LOGGER.error("Namespace cannot be set due to an error in EMF", e); + } + } + + @Override + public void setRaiseOnEmptyMetrics(boolean raiseOnEmptyMetrics) { + this.raiseOnEmptyMetrics.set(raiseOnEmptyMetrics); + } + + @Override + public void setTimestamp(Instant timestamp) { + Validator.validateTimestamp(timestamp); + + emfLogger.setTimestamp(timestamp); + } + + @Override + public void clearDefaultDimensions() { + emfLogger.resetDimensions(false); + defaultDimensions.clear(); + } + + @Override + public void flush() { + if (isMetricsDisabled()) { + LOGGER.debug("Metrics are disabled, skipping flush"); + return; + } + + Validator.validateNamespace(namespace); + + if (!hasMetrics.get()) { + if (raiseOnEmptyMetrics.get()) { + throw new IllegalStateException("No metrics were emitted"); + } else { + LOGGER.warn("No metrics were emitted"); + } + } else { + emfLogger.flush(); + + // Clear custom dimensions after flush while preserving default dimensions + clearCustomDimensions(); + } + } + + private void clearCustomDimensions() { + // Reset all dimensions in the EMF logger + emfLogger.resetDimensions(false); + + // Re-apply default dimensions if they exist + if (!defaultDimensions.isEmpty()) { + DimensionSet emfDimensionSet = new DimensionSet(); + defaultDimensions.forEach((key, value) -> { + try { + emfDimensionSet.addDimension(key, value); + } catch (Exception e) { + // Ignore dimension errors + } + }); + emfLogger.setDimensions(emfDimensionSet); + } + } + + @Override + public void captureColdStartMetric(Context context, + software.amazon.lambda.powertools.metrics.model.DimensionSet dimensions) { + if (isColdStart()) { + flushMetrics(metrics -> { + MetricsUtils.addRequestIdAndXrayTraceIdIfAvailable(context, metrics); + if (dimensions != null) { + metrics.setDefaultDimensions(dimensions); + } + metrics.addMetric(COLD_START_METRIC, 1, MetricUnit.COUNT); + }); + } + } + + @Override + public void captureColdStartMetric(software.amazon.lambda.powertools.metrics.model.DimensionSet dimensions) { + captureColdStartMetric(null, dimensions); + } + + @Override + public void flushMetrics(Consumer<Metrics> metricsConsumer) { + if (isMetricsDisabled()) { + LOGGER.debug("Metrics are disabled, skipping single metric flush"); + return; + } + // Create a new instance, inheriting namespace, default dimensions, and metadata + EmfMetricsLogger metrics = new EmfMetricsLogger(environmentProvider, new MetricsContext()); + if (namespace != null) { + metrics.setNamespace(this.namespace); + } + if (!defaultDimensions.isEmpty()) { + metrics.setDefaultDimensions( + software.amazon.lambda.powertools.metrics.model.DimensionSet.of(defaultDimensions)); + } + properties.forEach(metrics::addMetadata); + + metricsConsumer.accept(metrics); + + metrics.flush(); + } + + private boolean isMetricsDisabled() { + String disabledValue = System.getenv(METRICS_DISABLED_ENV_VAR); + return "true".equalsIgnoreCase(disabledValue); + } + + private Unit convertUnit(MetricUnit unit) { + switch (unit) { + case SECONDS: + return Unit.SECONDS; + case MICROSECONDS: + return Unit.MICROSECONDS; + case MILLISECONDS: + return Unit.MILLISECONDS; + case BYTES: + return Unit.BYTES; + case KILOBYTES: + return Unit.KILOBYTES; + case MEGABYTES: + return Unit.MEGABYTES; + case GIGABYTES: + return Unit.GIGABYTES; + case TERABYTES: + return Unit.TERABYTES; + case BITS: + return Unit.BITS; + case KILOBITS: + return Unit.KILOBITS; + case MEGABITS: + return Unit.MEGABITS; + case GIGABITS: + return Unit.GIGABITS; + case TERABITS: + return Unit.TERABITS; + case PERCENT: + return Unit.PERCENT; + case COUNT: + return Unit.COUNT; + case BYTES_SECOND: + return Unit.BYTES_SECOND; + case KILOBYTES_SECOND: + return Unit.KILOBYTES_SECOND; + case MEGABYTES_SECOND: + return Unit.MEGABYTES_SECOND; + case GIGABYTES_SECOND: + return Unit.GIGABYTES_SECOND; + case TERABYTES_SECOND: + return Unit.TERABYTES_SECOND; + case BITS_SECOND: + return Unit.BITS_SECOND; + case KILOBITS_SECOND: + return Unit.KILOBITS_SECOND; + case MEGABITS_SECOND: + return Unit.MEGABITS_SECOND; + case GIGABITS_SECOND: + return Unit.GIGABITS_SECOND; + case TERABITS_SECOND: + return Unit.TERABITS_SECOND; + case COUNT_SECOND: + return Unit.COUNT_SECOND; + case NONE: + default: + return Unit.NONE; + } + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java index 8ca069b01..1f0e3ec8c 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java @@ -14,138 +14,126 @@ package software.amazon.lambda.powertools.metrics.internal; -import static software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper.dimensionsCount; -import static software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper.hasNoMetrics; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.extractContext; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.hasDefaultDimension; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.extractContext; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isHandlerMethod; -import com.amazonaws.services.lambda.runtime.Context; -import java.lang.reflect.Field; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.MetricsContext; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; + +import com.amazonaws.services.lambda.runtime.Context; + +import software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; -import software.amazon.lambda.powertools.metrics.MetricsUtils; -import software.amazon.lambda.powertools.metrics.ValidationException; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; @Aspect public class LambdaMetricsAspect { - public static final String TRACE_ID_PROPERTY = "xray_trace_id"; - public static final String REQUEST_ID_PROPERTY = "function_request_id"; - private static final String NAMESPACE = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); - - private static String service(Metrics metrics) { - return !"".equals(metrics.service()) ? metrics.service() : serviceName(); - } + private static final String SERVICE_DIMENSION = "Service"; + private static final String FUNCTION_NAME_ENV_VAR = "POWERTOOLS_METRICS_FUNCTION_NAME"; - // This can be simplified after this issues https://github.com/awslabs/aws-embedded-metrics-java/issues/35 is fixed - public static void refreshMetricsContext(Metrics metrics) { - try { - Field f = metricsLogger().getClass().getDeclaredField("context"); - f.setAccessible(true); - MetricsContext context = new MetricsContext(); + private String functionName(FlushMetrics metrics, Context context) { + if (!"".equals(metrics.functionName())) { + return metrics.functionName(); + } - DimensionSet[] defaultDimensions = hasDefaultDimension() ? MetricsUtils.getDefaultDimensions() - : new DimensionSet[] {DimensionSet.of("Service", service(metrics))}; + String envFunctionName = System.getenv(FUNCTION_NAME_ENV_VAR); + if (envFunctionName != null && !envFunctionName.isEmpty()) { + return envFunctionName; + } - context.setDimensions(defaultDimensions); + return context != null ? context.getFunctionName() : null; + } - f.set(metricsLogger(), context); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); + private String serviceNameWithFallback(FlushMetrics metrics) { + if (!"".equals(metrics.service())) { + return metrics.service(); } + return LambdaHandlerProcessor.serviceName(); } - @SuppressWarnings({"EmptyMethod"}) + @SuppressWarnings({ "EmptyMethod" }) @Pointcut("@annotation(metrics)") - public void callAt(Metrics metrics) { + public void callAt(FlushMetrics metrics) { + // AspectJ point cut referenced in around() method } - @Around(value = "callAt(metrics) && execution(@Metrics * *.*(..))", argNames = "pjp,metrics") + @Around(value = "callAt(metrics) && execution(@FlushMetrics * *.*(..))", argNames = "pjp,metrics") public Object around(ProceedingJoinPoint pjp, - Metrics metrics) throws Throwable { + FlushMetrics metrics) throws Throwable { Object[] proceedArgs = pjp.getArgs(); if (isHandlerMethod(pjp)) { + Metrics metricsInstance = MetricsFactory.getMetricsInstance(); - MetricsLogger logger = metricsLogger(); + // The MetricsFactory applies default settings from the environment or can be configured by the + // MetricsBuilder. We only overwrite settings if they are explicitly set in the @FlushMetrics + // annotation. + if (!"".equals(metrics.namespace())) { + metricsInstance.setNamespace(metrics.namespace()); + } - refreshMetricsContext(metrics); + // We only overwrite the default dimensions if the user didn't overwrite them previously. This means that + // they are either empty or only contain the default "Service" dimension. + if (!"".equals(metrics.service().trim()) + && (metricsInstance.getDefaultDimensions().getDimensionKeys().size() <= 1 + || metricsInstance.getDefaultDimensions().getDimensionKeys().contains(SERVICE_DIMENSION))) { + metricsInstance.setDefaultDimensions(DimensionSet.of(SERVICE_DIMENSION, metrics.service())); + } - logger.setNamespace(namespace(metrics)); + metricsInstance.setRaiseOnEmptyMetrics(metrics.raiseOnEmptyMetrics()); Context extractedContext = extractContext(pjp); + MetricsUtils.addRequestIdAndXrayTraceIdIfAvailable(extractedContext, metricsInstance); - if (null != extractedContext) { - coldStartSingleMetricIfApplicable(extractedContext.getAwsRequestId(), - extractedContext.getFunctionName(), metrics); - logger.putProperty(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); - } - - LambdaHandlerProcessor.getXrayTraceId() - .ifPresent(traceId -> logger.putProperty(TRACE_ID_PROPERTY, traceId)); + captureColdStartMetricIfEnabled(extractedContext, metrics); try { return pjp.proceed(proceedArgs); - } finally { coldStartDone(); - validateMetricsAndRefreshOnFailure(metrics); - logger.flush(); - refreshMetricsContext(metrics); + metricsInstance.flush(); } } return pjp.proceed(proceedArgs); } - private void coldStartSingleMetricIfApplicable(final String awsRequestId, - final String functionName, - final Metrics metrics) { - if (metrics.captureColdStart() - && isColdStart()) { - MetricsLogger metricsLogger = new MetricsLogger(); - metricsLogger.setNamespace(namespace(metrics)); - metricsLogger.putMetric("ColdStart", 1, Unit.COUNT); - metricsLogger.setDimensions(DimensionSet.of("Service", service(metrics), "FunctionName", functionName)); - metricsLogger.putProperty(REQUEST_ID_PROPERTY, awsRequestId); - metricsLogger.flush(); + private void captureColdStartMetricIfEnabled(Context extractedContext, FlushMetrics flushMetrics) { + if (extractedContext == null) { + return; } - } + Metrics metrics = MetricsFactory.getMetricsInstance(); - private void validateBeforeFlushingMetrics(Metrics metrics) { - if (metrics.raiseOnEmptyMetrics() && hasNoMetrics()) { - throw new ValidationException("No metrics captured, at least one metrics must be emitted"); - } + // Only capture cold start metrics if enabled on annotation + if (flushMetrics.captureColdStart()) { + // Get function name from annotation or context + String funcName = functionName(flushMetrics, extractedContext); - if (dimensionsCount() > 9) { - throw new ValidationException(String.format("Number of Dimensions must be in range of 0-9." + - " Actual size: %d.", dimensionsCount())); - } - } + DimensionSet dimensionSet = new DimensionSet(); - private String namespace(Metrics metrics) { - return !"".equals(metrics.namespace()) ? metrics.namespace() : NAMESPACE; - } + // Get service name from metrics instance default dimensions or fallback + String serviceName = metrics.getDefaultDimensions().getDimensions().getOrDefault( + SERVICE_DIMENSION, + serviceNameWithFallback(flushMetrics)); + + // Only add service if it is not undefined + if (!LambdaConstants.SERVICE_UNDEFINED.equals(serviceName)) { + dimensionSet.addDimension(SERVICE_DIMENSION, serviceName); + } + + // Add function name + if (funcName != null) { + dimensionSet.addDimension("FunctionName", funcName); + } - private void validateMetricsAndRefreshOnFailure(Metrics metrics) { - try { - validateBeforeFlushingMetrics(metrics); - } catch (ValidationException e) { - refreshMetricsContext(metrics); - throw e; + metrics.captureColdStartMetric(extractedContext, dimensionSet); } } } diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptor.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptor.java new file mode 100644 index 000000000..5a466cc3a --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.metrics.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the metrics module is on the classpath. + */ +public final class MetricsUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("metrics"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUtils.java new file mode 100644 index 000000000..246f6effc --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUtils.java @@ -0,0 +1,22 @@ +package software.amazon.lambda.powertools.metrics.internal; + +import com.amazonaws.services.lambda.runtime.Context; +import software.amazon.lambda.powertools.metrics.Metrics; + +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.getXrayTraceId; + +final class MetricsUtils { + private static final String TRACE_ID_PROPERTY = "xray_trace_id"; + private static final String REQUEST_ID_PROPERTY = "function_request_id"; + + private MetricsUtils() { + // Utility class + } + + static void addRequestIdAndXrayTraceIdIfAvailable(Context context, Metrics metrics) { + if (context != null && context.getAwsRequestId() != null) { + metrics.addMetadata(REQUEST_ID_PROPERTY, context.getAwsRequestId()); + } + getXrayTraceId().ifPresent(traceId -> metrics.addMetadata(TRACE_ID_PROPERTY, traceId)); + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java new file mode 100644 index 000000000..61c83be17 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java @@ -0,0 +1,156 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.internal; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import com.amazonaws.services.lambda.runtime.Context; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; + +public class RequestScopedMetricsProxy implements Metrics { + private static final String DEFAULT_TRACE_ID = "DEFAULT"; + private final ConcurrentMap<String, Metrics> metricsMap = new ConcurrentHashMap<>(); + private final MetricsProvider provider; + private final AtomicReference<String> initialNamespace = new AtomicReference<>(); + private final AtomicReference<DimensionSet> initialDefaultDimensions = new AtomicReference<>(); + private final AtomicBoolean initialRaiseOnEmptyMetrics = new AtomicBoolean(false); + + public RequestScopedMetricsProxy(MetricsProvider provider) { + this.provider = provider; + } + + private String getTraceId() { + return LambdaHandlerProcessor.getXrayTraceId().orElse(DEFAULT_TRACE_ID); + } + + private Metrics getOrCreateMetrics() { + String traceId = getTraceId(); + return metricsMap.computeIfAbsent(traceId, key -> { + Metrics metrics = provider.getMetricsInstance(); + String namespace = initialNamespace.get(); + if (namespace != null) { + metrics.setNamespace(namespace); + } + DimensionSet dimensions = initialDefaultDimensions.get(); + if (dimensions != null) { + metrics.setDefaultDimensions(dimensions); + } + metrics.setRaiseOnEmptyMetrics(initialRaiseOnEmptyMetrics.get()); + return metrics; + }); + } + + // Configuration methods - called by MetricsFactory and MetricsBuilder + // These methods DO NOT eagerly create instances because they are typically called + // outside the Lambda handler (e.g., during class initialization) potentially on a different thread. + // We delay instance creation until the first operation that needs the metrics backend (e.g., addMetric). + // See {@link software.amazon.lambda.powertools.metrics.MetricsFactory#getMetricsInstance()} + // and {@link software.amazon.lambda.powertools.metrics.MetricsBuilder#build()} + + @Override + public void setNamespace(String namespace) { + this.initialNamespace.set(namespace); + Optional.ofNullable(metricsMap.get(getTraceId())).ifPresent(m -> m.setNamespace(namespace)); + } + + @Override + public void setDefaultDimensions(DimensionSet dimensionSet) { + if (dimensionSet == null) { + throw new IllegalArgumentException("DimensionSet cannot be null"); + } + this.initialDefaultDimensions.set(dimensionSet); + Optional.ofNullable(metricsMap.get(getTraceId())).ifPresent(m -> m.setDefaultDimensions(dimensionSet)); + } + + @Override + public void setRaiseOnEmptyMetrics(boolean raiseOnEmptyMetrics) { + this.initialRaiseOnEmptyMetrics.set(raiseOnEmptyMetrics); + Optional.ofNullable(metricsMap.get(getTraceId())).ifPresent(m -> m.setRaiseOnEmptyMetrics(raiseOnEmptyMetrics)); + } + + @Override + public DimensionSet getDefaultDimensions() { + Metrics metrics = metricsMap.get(getTraceId()); + if (metrics != null) { + return metrics.getDefaultDimensions(); + } + DimensionSet dimensions = initialDefaultDimensions.get(); + return dimensions != null ? dimensions : DimensionSet.of(new HashMap<>()); + } + + // Metrics operations - these eagerly create instances + + @Override + public void addMetric(String key, double value, MetricUnit unit, MetricResolution resolution) { + getOrCreateMetrics().addMetric(key, value, unit, resolution); + } + + @Override + public void addDimension(DimensionSet dimensionSet) { + getOrCreateMetrics().addDimension(dimensionSet); + } + + @Override + public void setTimestamp(Instant timestamp) { + getOrCreateMetrics().setTimestamp(timestamp); + } + + @Override + public void addMetadata(String key, Object value) { + getOrCreateMetrics().addMetadata(key, value); + } + + @Override + public void clearDefaultDimensions() { + getOrCreateMetrics().clearDefaultDimensions(); + } + + @Override + public void flush() { + // Always create instance to ensure validation and warnings are triggered. E.g. when raiseOnEmptyMetrics + // is enabled. + Metrics metrics = getOrCreateMetrics(); + metrics.flush(); + metricsMap.remove(getTraceId()); + } + + @Override + public void captureColdStartMetric(Context context, DimensionSet dimensions) { + getOrCreateMetrics().captureColdStartMetric(context, dimensions); + } + + @Override + public void captureColdStartMetric(DimensionSet dimensions) { + getOrCreateMetrics().captureColdStartMetric(dimensions); + } + + @Override + public void flushMetrics(Consumer<Metrics> metricsConsumer) { + getOrCreateMetrics().flushMetrics(metricsConsumer); + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/Validator.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/Validator.java new file mode 100644 index 000000000..eebb54739 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/Validator.java @@ -0,0 +1,135 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.internal; + +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; + +/** + * Utility class for validating metrics-related parameters. + */ +public class Validator { + private static final int MAX_DIMENSION_NAME_LENGTH = 250; + private static final int MAX_DIMENSION_VALUE_LENGTH = 1024; + private static final int MAX_NAMESPACE_LENGTH = 255; + private static final String NAMESPACE_REGEX = "^[a-zA-Z0-9._#:/-]+$"; + public static final long MAX_TIMESTAMP_PAST_AGE_SECONDS = TimeUnit.DAYS.toSeconds(14); + public static final long MAX_TIMESTAMP_FUTURE_AGE_SECONDS = TimeUnit.HOURS.toSeconds(2); + + private Validator() { + // Private constructor to prevent instantiation + } + + /** + * Validates that a namespace is properly specified. + * + * @param namespace The namespace to validate + * @throws IllegalArgumentException if the namespace is invalid + */ + public static void validateNamespace(String namespace) { + if (namespace == null || namespace.trim().isEmpty()) { + throw new IllegalArgumentException("Namespace must be specified before flushing metrics"); + } + + if (namespace.length() > MAX_NAMESPACE_LENGTH) { + throw new IllegalArgumentException( + "Namespace exceeds maximum length of " + MAX_NAMESPACE_LENGTH + ": " + namespace); + } + + if (!namespace.matches(NAMESPACE_REGEX)) { + throw new IllegalArgumentException("Namespace contains invalid characters: " + namespace); + } + } + + /** + * Validates Timestamp. + * + * @see <a + * href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#about_timestamp">CloudWatch + * Timestamp</a> + * @param timestamp Timestamp + * @throws IllegalArgumentException if timestamp is invalid + */ + public static void validateTimestamp(Instant timestamp) { + if (timestamp == null) { + throw new IllegalArgumentException("Timestamp cannot be null"); + } + + if (timestamp.isAfter( + Instant.now().plusSeconds(MAX_TIMESTAMP_FUTURE_AGE_SECONDS))) { + throw new IllegalArgumentException( + "Timestamp cannot be more than " + + MAX_TIMESTAMP_FUTURE_AGE_SECONDS + + " seconds in the future"); + } + + if (timestamp.isBefore( + Instant.now().minusSeconds(MAX_TIMESTAMP_PAST_AGE_SECONDS))) { + throw new IllegalArgumentException( + "Timestamp cannot be more than " + + MAX_TIMESTAMP_PAST_AGE_SECONDS + + " seconds in the past"); + } + } + + /** + * Validates a dimension key-value pair. + * + * @param key The dimension key to validate + * @param value The dimension value to validate + * @throws IllegalArgumentException if the key or value is invalid + */ + public static void validateDimension(String key, String value) { + if (key == null || key.trim().isEmpty()) { + throw new IllegalArgumentException("Dimension key cannot be null or empty"); + } + + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("Dimension value cannot be null or empty"); + } + + if (StringUtils.containsWhitespace(key)) { + throw new IllegalArgumentException("Dimension key cannot contain whitespaces: " + key); + } + + if (StringUtils.containsWhitespace(value)) { + throw new IllegalArgumentException("Dimension value cannot contain whitespaces: " + value); + } + + if (key.startsWith(":")) { + throw new IllegalArgumentException("Dimension key cannot start with colon: " + key); + } + + if (key.length() > MAX_DIMENSION_NAME_LENGTH) { + throw new IllegalArgumentException( + "Dimension name exceeds maximum length of " + MAX_DIMENSION_NAME_LENGTH + ": " + key); + } + + if (value.length() > MAX_DIMENSION_VALUE_LENGTH) { + throw new IllegalArgumentException( + "Dimension value exceeds maximum length of " + MAX_DIMENSION_VALUE_LENGTH + ": " + value); + } + + if (!StringUtils.isAsciiPrintable(key)) { + throw new IllegalArgumentException("Dimension name has invalid characters: " + key); + } + + if (!StringUtils.isAsciiPrintable(value)) { + throw new IllegalArgumentException("Dimension value has invalid characters: " + value); + } + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/DimensionSet.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/DimensionSet.java new file mode 100644 index 000000000..e93f34237 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/DimensionSet.java @@ -0,0 +1,193 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.model; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import software.amazon.lambda.powertools.metrics.internal.Validator; + +/** + * Represents a set of dimensions for CloudWatch metrics + */ +public class DimensionSet { + private static final int MAX_DIMENSION_SET_SIZE = 30; + + private final Map<String, String> dimensions = new LinkedHashMap<>(); + + /** + * Create a dimension set with a single key-value pair + * + * @param key dimension key + * @param value dimension value + * @return a new DimensionSet + */ + public static DimensionSet of(String key, String value) { + DimensionSet dimensionSet = new DimensionSet(); + dimensionSet.addDimension(key, value); + return dimensionSet; + } + + /** + * Create a dimension set with two key-value pairs + * + * @param key1 first dimension key + * @param value1 first dimension value + * @param key2 second dimension key + * @param value2 second dimension value + * @return a new DimensionSet + */ + public static DimensionSet of(String key1, String value1, String key2, String value2) { + DimensionSet dimensionSet = new DimensionSet(); + dimensionSet.addDimension(key1, value1); + dimensionSet.addDimension(key2, value2); + return dimensionSet; + } + + /** + * Create a dimension set with three key-value pairs + * + * @param key1 first dimension key + * @param value1 first dimension value + * @param key2 second dimension key + * @param value2 second dimension value + * @param key3 third dimension key + * @param value3 third dimension value + * @return a new DimensionSet + */ + public static DimensionSet of(String key1, String value1, String key2, String value2, String key3, String value3) { + DimensionSet dimensionSet = new DimensionSet(); + dimensionSet.addDimension(key1, value1); + dimensionSet.addDimension(key2, value2); + dimensionSet.addDimension(key3, value3); + return dimensionSet; + } + + /** + * Create a dimension set with four key-value pairs + * + * @param key1 first dimension key + * @param value1 first dimension value + * @param key2 second dimension key + * @param value2 second dimension value + * @param key3 third dimension key + * @param value3 third dimension value + * @param key4 fourth dimension key + * @param value4 fourth dimension value + * @return a new DimensionSet + */ + public static DimensionSet of(String key1, String value1, String key2, String value2, + String key3, String value3, String key4, String value4) { + DimensionSet dimensionSet = new DimensionSet(); + dimensionSet.addDimension(key1, value1); + dimensionSet.addDimension(key2, value2); + dimensionSet.addDimension(key3, value3); + dimensionSet.addDimension(key4, value4); + return dimensionSet; + } + + /** + * Create a dimension set with five key-value pairs + * + * @param key1 first dimension key + * @param value1 first dimension value + * @param key2 second dimension key + * @param value2 second dimension value + * @param key3 third dimension key + * @param value3 third dimension value + * @param key4 fourth dimension key + * @param value4 fourth dimension value + * @param key5 fifth dimension key + * @param value5 fifth dimension value + * @return a new DimensionSet + */ + public static DimensionSet of(String key1, String value1, String key2, String value2, + String key3, String value3, String key4, String value4, + String key5, String value5) { + DimensionSet dimensionSet = new DimensionSet(); + dimensionSet.addDimension(key1, value1); + dimensionSet.addDimension(key2, value2); + dimensionSet.addDimension(key3, value3); + dimensionSet.addDimension(key4, value4); + dimensionSet.addDimension(key5, value5); + return dimensionSet; + } + + /** + * Create a dimension set from a map of key-value pairs + * + * @param dimensions map of dimension key-value pairs + * @return a new DimensionSet + */ + public static DimensionSet of(Map<String, String> dimensions) { + DimensionSet dimensionSet = new DimensionSet(); + dimensions.forEach(dimensionSet::addDimension); + return dimensionSet; + } + + /** + * Add a dimension to this dimension set + * + * @param key dimension key + * @param value dimension value + * @return this dimension set for chaining + * @throws IllegalArgumentException if key or value is invalid + * @throws IllegalStateException if adding would exceed the maximum number of dimensions + */ + public DimensionSet addDimension(String key, String value) { + validateDimension(key, value); + + if (dimensions.size() >= MAX_DIMENSION_SET_SIZE) { + throw new IllegalStateException( + "Cannot exceed " + MAX_DIMENSION_SET_SIZE + " dimensions per dimension set"); + } + + dimensions.put(key, value); + return this; + } + + /** + * Get the dimension keys in this dimension set + * + * @return set of dimension keys + */ + public Set<String> getDimensionKeys() { + return dimensions.keySet(); + } + + /** + * Get the value for a dimension key + * + * @param key dimension key + * @return dimension value or null if not found + */ + public String getDimensionValue(String key) { + return dimensions.get(key); + } + + /** + * Get the dimensions as a map. Creates a shallow copy + * + * @return map of dimensions + */ + public Map<String, String> getDimensions() { + return new LinkedHashMap<>(dimensions); + } + + private void validateDimension(String key, String value) { + Validator.validateDimension(key, value); + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricResolution.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricResolution.java new file mode 100644 index 000000000..db514c8b7 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricResolution.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.model; + +/** + * Resolution for metrics + */ +public enum MetricResolution { + STANDARD(60), HIGH(1); + + private final int seconds; + + MetricResolution(int seconds) { + this.seconds = seconds; + } + + public int getSeconds() { + return seconds; + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricUnit.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricUnit.java new file mode 100644 index 000000000..445d950b2 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricUnit.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.model; + +/** + * Metric units supported by CloudWatch + */ +public enum MetricUnit { + SECONDS("Seconds"), + MICROSECONDS("Microseconds"), + MILLISECONDS("Milliseconds"), + BYTES("Bytes"), + KILOBYTES("Kilobytes"), + MEGABYTES("Megabytes"), + GIGABYTES("Gigabytes"), + TERABYTES("Terabytes"), + BITS("Bits"), + KILOBITS("Kilobits"), + MEGABITS("Megabits"), + GIGABITS("Gigabits"), + TERABITS("Terabits"), + PERCENT("Percent"), + COUNT("Count"), + BYTES_SECOND("Bytes/Second"), + KILOBYTES_SECOND("Kilobytes/Second"), + MEGABYTES_SECOND("Megabytes/Second"), + GIGABYTES_SECOND("Gigabytes/Second"), + TERABYTES_SECOND("Terabytes/Second"), + BITS_SECOND("Bits/Second"), + KILOBITS_SECOND("Kilobits/Second"), + MEGABITS_SECOND("Megabits/Second"), + GIGABITS_SECOND("Gigabits/Second"), + TERABITS_SECOND("Terabits/Second"), + COUNT_SECOND("Count/Second"), + NONE("None"); + + private final String name; + + MetricUnit(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProvider.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProvider.java new file mode 100644 index 000000000..12c99b18f --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.provider; + +import software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider; +import software.amazon.cloudwatchlogs.emf.model.MetricsContext; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger; + +/** + * Provider implementation for EMF metrics + */ +public class EmfMetricsProvider implements MetricsProvider { + + @Override + public Metrics getMetricsInstance() { + return new EmfMetricsLogger(new EnvironmentProvider(), new MetricsContext()); + } +} diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/MetricsProvider.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/MetricsProvider.java new file mode 100644 index 000000000..e6c79e000 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/MetricsProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.provider; + +import software.amazon.lambda.powertools.metrics.Metrics; + +/** + * Interface for metrics provider implementations + */ +public interface MetricsProvider { + + /** + * Get a new instance of a metrics implementation + * + * @return a new metrics instance + */ + Metrics getMetricsInstance(); +} diff --git a/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/jni-config.json b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/jni-config.json new file mode 100644 index 000000000..75ee9e5f5 --- /dev/null +++ b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/jni-config.json @@ -0,0 +1,33 @@ +[ + { + "name": "java.lang.Boolean", + "methods": [{ "name": "getBoolean", "parameterTypes": ["java.lang.String"] }] + }, + { + "name": "java.lang.String", + "methods": [ + { "name": "lastIndexOf", "parameterTypes": ["int"] }, + { "name": "substring", "parameterTypes": ["int"] } + ] + }, + { + "name": "java.lang.System", + "methods": [ + { "name": "getProperty", "parameterTypes": ["java.lang.String"] }, + { "name": "setProperty", "parameterTypes": ["java.lang.String", "java.lang.String"] } + ] + }, + { + "name": "sun.management.VMManagementImpl", + "fields": [ + { "name": "compTimeMonitoringSupport" }, + { "name": "currentThreadCpuTimeSupport" }, + { "name": "objectMonitorUsageSupport" }, + { "name": "otherThreadCpuTimeSupport" }, + { "name": "remoteDiagnosticCommandsSupport" }, + { "name": "synchronizerUsageSupport" }, + { "name": "threadAllocatedMemorySupport" }, + { "name": "threadContentionMonitoringSupport" } + ] + } +] diff --git a/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/reflect-config.json b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/reflect-config.json new file mode 100644 index 000000000..a0ac5bfec --- /dev/null +++ b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/reflect-config.json @@ -0,0 +1,156 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.Context", + "allDeclaredClasses": true, + "queryAllPublicMethods": true + }, + { + "name": "com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "java.io.Serializable", + "queryAllDeclaredMethods": true + }, + { + "name": "java.lang.Comparable", + "queryAllDeclaredMethods": true + }, + { + "name": "java.lang.Double", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true + }, + { + "name": "java.lang.Number", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true + }, + { + "name": "java.lang.ProcessEnvironment", + "fields": [{ "name": "theCaseInsensitiveEnvironment" }, { "name": "theEnvironment" }] + }, + { + "name": "java.lang.String" + }, + { + "name": "java.lang.constant.Constable", + "queryAllDeclaredMethods": true + }, + { + "name": "java.lang.constant.ConstantDesc", + "queryAllDeclaredMethods": true + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "java.util.Map" + }, + { + "name": "java.util.concurrent.atomic.AtomicBoolean", + "fields": [{ "name": "value" }] + }, + { + "name": "java.util.concurrent.atomic.AtomicReference", + "fields": [{ "name": "value" }] + }, + { + "name": "java.util.function.Consumer", + "queryAllPublicMethods": true + }, + { + "name": "org.apiguardian.api.API", + "queryAllPublicMethods": true + }, + { + "name": "software.amazon.cloudwatchlogs.emf.model.Metadata", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true, + "methods": [ + { "name": "getCloudWatchMetrics", "parameterTypes": [] }, + { "name": "getCustomMetadata", "parameterTypes": [] }, + { "name": "getTimestamp", "parameterTypes": [] } + ] + }, + { + "name": "software.amazon.cloudwatchlogs.emf.model.MetricDefinition", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true, + "methods": [ + { "name": "getName", "parameterTypes": [] }, + { "name": "getStorageResolution", "parameterTypes": [] }, + { "name": "getUnit", "parameterTypes": [] } + ] + }, + { + "name": "software.amazon.cloudwatchlogs.emf.model.MetricDirective", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true, + "methods": [ + { "name": "getAllDimensionKeys", "parameterTypes": [] }, + { "name": "getAllMetrics", "parameterTypes": [] }, + { "name": "getNamespace", "parameterTypes": [] } + ] + }, + { + "name": "software.amazon.cloudwatchlogs.emf.model.RootNode", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true, + "methods": [ + { "name": "getAws", "parameterTypes": [] }, + { "name": "getTargetMembers", "parameterTypes": [] } + ] + }, + { + "name": "software.amazon.cloudwatchlogs.emf.serializers.InstantSerializer", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionFilter", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionSerializer", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "software.amazon.cloudwatchlogs.emf.serializers.UnitSerializer", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + }, + { + "name": "software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields": [{ "name": "isColdStart" }], + "methods": [{ "name": "resetServiceName", "parameterTypes": [] }] + }, + { + "name": "software.amazon.lambda.powertools.metrics.Metrics", + "allDeclaredClasses": true, + "queryAllPublicMethods": true + }, + { + "name": "software.amazon.lambda.powertools.metrics.MetricsFactory", + "fields": [{ "name": "metrics" }, { "name": "provider" }] + }, + { + "name": "software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger", + "methods": [ + { "name": "convertUnit", "parameterTypes": ["software.amazon.lambda.powertools.metrics.model.MetricUnit"] } + ] + }, + { + "name": "software.amazon.lambda.powertools.metrics.provider.MetricsProvider", + "allDeclaredClasses": true, + "queryAllPublicMethods": true + }, + { + "name": "software.amazon.lambda.powertools.metrics.internal.MetricsUserAgentInterceptor", + "methods": [{ "name": "<init>", "parameterTypes": [] }] + } +] diff --git a/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/resource-config.json b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/resource-config.json new file mode 100644 index 000000000..0667ee26a --- /dev/null +++ b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/resource-config.json @@ -0,0 +1,16 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, + { + "pattern": "\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, + { + "pattern": "\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + } + ] + }, + "bundles": [] +} diff --git a/powertools-metrics/src/main/resources/classesloaded.txt b/powertools-metrics/src/main/resources/classesloaded.txt new file mode 100644 index 000000000..f56d6af78 --- /dev/null +++ b/powertools-metrics/src/main/resources/classesloaded.txt @@ -0,0 +1,3651 @@ +java.lang.Object +java.io.Serializable +java.lang.Comparable +java.lang.CharSequence +java.lang.constant.Constable +java.lang.constant.ConstantDesc +java.lang.String +java.lang.reflect.AnnotatedElement +java.lang.reflect.GenericDeclaration +java.lang.reflect.Type +java.lang.invoke.TypeDescriptor +java.lang.invoke.TypeDescriptor$OfField +java.lang.Class +java.lang.Cloneable +java.lang.ClassLoader +java.lang.System +java.lang.Throwable +java.lang.Error +java.lang.ThreadDeath +java.lang.Exception +java.lang.RuntimeException +java.lang.SecurityManager +java.security.ProtectionDomain +java.security.AccessControlContext +java.security.AccessController +java.security.SecureClassLoader +java.lang.ReflectiveOperationException +java.lang.ClassNotFoundException +java.lang.Record +java.lang.LinkageError +java.lang.NoClassDefFoundError +java.lang.ClassCastException +java.lang.ArrayStoreException +java.lang.VirtualMachineError +java.lang.InternalError +java.lang.OutOfMemoryError +java.lang.StackOverflowError +java.lang.IllegalMonitorStateException +java.lang.ref.Reference +java.lang.ref.SoftReference +java.lang.ref.WeakReference +java.lang.ref.FinalReference +java.lang.ref.PhantomReference +java.lang.ref.Finalizer +java.lang.Runnable +java.lang.Thread +java.lang.Thread$UncaughtExceptionHandler +java.lang.ThreadGroup +java.util.Dictionary +java.util.Map +java.util.Hashtable +java.util.Properties +java.lang.Module +java.lang.reflect.AccessibleObject +java.lang.reflect.Member +java.lang.reflect.Field +java.lang.reflect.Parameter +java.lang.reflect.Executable +java.lang.reflect.Method +java.lang.reflect.Constructor +jdk.internal.reflect.MagicAccessorImpl +jdk.internal.reflect.MethodAccessor +jdk.internal.reflect.MethodAccessorImpl +jdk.internal.reflect.ConstructorAccessor +jdk.internal.reflect.ConstructorAccessorImpl +jdk.internal.reflect.DelegatingClassLoader +jdk.internal.reflect.ConstantPool +jdk.internal.reflect.FieldAccessor +jdk.internal.reflect.FieldAccessorImpl +jdk.internal.reflect.UnsafeFieldAccessorImpl +jdk.internal.reflect.UnsafeStaticFieldAccessorImpl +java.lang.annotation.Annotation +jdk.internal.reflect.CallerSensitive +jdk.internal.reflect.NativeConstructorAccessorImpl +java.lang.invoke.MethodHandle +java.lang.invoke.DirectMethodHandle +java.lang.invoke.VarHandle +java.lang.invoke.MemberName +java.lang.invoke.ResolvedMethodName +java.lang.invoke.MethodHandleNatives +java.lang.invoke.LambdaForm +java.lang.invoke.TypeDescriptor$OfMethod +java.lang.invoke.MethodType +java.lang.BootstrapMethodError +java.lang.invoke.CallSite +jdk.internal.invoke.NativeEntryPoint +java.lang.invoke.MethodHandleNatives$CallSiteContext +java.lang.invoke.ConstantCallSite +java.lang.invoke.MutableCallSite +java.lang.invoke.VolatileCallSite +java.lang.AssertionStatusDirectives +java.lang.Appendable +java.lang.AbstractStringBuilder +java.lang.StringBuffer +java.lang.StringBuilder +jdk.internal.misc.UnsafeConstants +jdk.internal.misc.Unsafe +jdk.internal.module.Modules +java.lang.AutoCloseable +java.io.Closeable +java.io.InputStream +java.io.ByteArrayInputStream +java.net.URL +java.util.jar.Manifest +jdk.internal.loader.BuiltinClassLoader +jdk.internal.loader.ClassLoaders +jdk.internal.loader.ClassLoaders$AppClassLoader +jdk.internal.loader.ClassLoaders$PlatformClassLoader +java.security.CodeSource +java.util.AbstractMap +java.util.concurrent.ConcurrentMap +java.util.concurrent.ConcurrentHashMap +java.lang.Iterable +java.util.Collection +java.util.AbstractCollection +java.util.List +java.util.AbstractList +java.util.RandomAccess +java.util.ArrayList +java.lang.StackTraceElement +java.nio.Buffer +java.lang.StackWalker +java.lang.StackStreamFactory$AbstractStackWalker +java.lang.StackWalker$StackFrame +java.lang.StackFrameInfo +java.lang.LiveStackFrame +java.lang.LiveStackFrameInfo +java.util.concurrent.locks.AbstractOwnableSynchronizer +java.lang.Boolean +java.lang.Character +java.lang.Number +java.lang.Float +java.lang.Double +java.lang.Byte +java.lang.Short +java.lang.Integer +java.lang.Long +java.util.Iterator +java.lang.reflect.RecordComponent +jdk.internal.vm.vector.VectorSupport +jdk.internal.vm.vector.VectorSupport$VectorPayload +jdk.internal.vm.vector.VectorSupport$Vector +jdk.internal.vm.vector.VectorSupport$VectorMask +jdk.internal.vm.vector.VectorSupport$VectorShuffle +java.lang.Integer$IntegerCache +java.lang.Long$LongCache +java.lang.Byte$ByteCache +java.lang.Short$ShortCache +java.lang.Character$CharacterCache +java.util.jar.Attributes$Name +java.util.ImmutableCollections$AbstractImmutableMap +java.util.ImmutableCollections$MapN +sun.util.locale.BaseLocale +jdk.internal.module.ArchivedModuleGraph +java.lang.module.ModuleFinder +jdk.internal.module.SystemModuleFinders$SystemModuleFinder +java.util.ImmutableCollections$AbstractImmutableCollection +java.util.Set +java.util.ImmutableCollections$AbstractImmutableSet +java.util.ImmutableCollections$SetN +java.lang.module.ModuleReference +jdk.internal.module.ModuleReferenceImpl +java.lang.module.ModuleDescriptor +java.lang.module.ModuleDescriptor$Version +java.util.ImmutableCollections$Set12 +java.lang.module.ModuleDescriptor$Requires +java.lang.Enum +java.lang.module.ModuleDescriptor$Requires$Modifier +java.lang.module.ModuleDescriptor$Exports +java.net.URI +java.util.function.Supplier +jdk.internal.module.SystemModuleFinders$2 +jdk.internal.module.ModuleHashes$HashSupplier +jdk.internal.module.SystemModuleFinders$3 +java.lang.module.ModuleDescriptor$Provides +java.util.ImmutableCollections$AbstractImmutableList +java.util.ImmutableCollections$List12 +java.util.ImmutableCollections$ListN +java.lang.module.ModuleDescriptor$Opens +jdk.internal.module.ModuleTarget +jdk.internal.module.ModuleHashes +java.util.Collections$UnmodifiableMap +java.util.HashMap +java.util.Map$Entry +java.util.HashMap$Node +java.lang.module.Configuration +java.lang.module.ResolvedModule +java.util.function.Function +jdk.internal.module.ModuleLoaderMap$Mapper +java.util.ImmutableCollections +java.lang.ModuleLayer +jdk.internal.math.FDBigInteger +java.lang.NullPointerException +java.lang.ArithmeticException +java.io.ObjectStreamField +java.util.Comparator +java.lang.String$CaseInsensitiveComparator +java.lang.Module$ArchivedData +jdk.internal.misc.CDS +java.util.Objects +jdk.internal.access.JavaLangReflectAccess +java.lang.reflect.ReflectAccess +jdk.internal.access.SharedSecrets +java.lang.invoke.MethodHandles +java.lang.invoke.MemberName$Factory +java.security.Guard +java.security.Permission +java.security.BasicPermission +java.lang.reflect.ReflectPermission +java.lang.StringLatin1 +java.lang.invoke.MethodHandles$Lookup +jdk.internal.reflect.Reflection +java.lang.Math +java.util.AbstractSet +java.util.ImmutableCollections$MapN$1 +java.util.ImmutableCollections$MapN$MapNIterator +java.util.KeyValueHolder +java.util.LinkedHashMap$Entry +java.util.HashMap$TreeNode +java.lang.Runtime +java.util.concurrent.locks.Lock +java.util.concurrent.locks.ReentrantLock +java.util.concurrent.ConcurrentHashMap$Segment +java.util.concurrent.ConcurrentHashMap$CounterCell +java.util.concurrent.ConcurrentHashMap$Node +java.util.concurrent.locks.LockSupport +java.util.concurrent.ConcurrentHashMap$ReservationNode +java.security.PrivilegedAction +jdk.internal.reflect.ReflectionFactory$GetReflectionFactoryAction +jdk.internal.reflect.ReflectionFactory +java.lang.ref.Reference$ReferenceHandler +jdk.internal.ref.Cleaner +java.lang.ref.ReferenceQueue +java.lang.ref.ReferenceQueue$Null +java.lang.ref.ReferenceQueue$Lock +jdk.internal.access.JavaLangRefAccess +java.lang.ref.Reference$1 +java.lang.ref.Finalizer$FinalizerThread +jdk.internal.access.JavaLangAccess +java.lang.System$2 +jdk.internal.misc.VM +jdk.internal.util.SystemProps +jdk.internal.util.SystemProps$Raw +java.lang.StringConcatHelper +java.lang.VersionProps +java.util.Arrays +java.lang.CharacterData +java.lang.CharacterDataLatin1 +java.util.HashMap$EntrySet +java.util.HashMap$HashIterator +java.util.HashMap$EntryIterator +jdk.internal.util.StaticProperty +java.io.FileInputStream +java.io.FileDescriptor +jdk.internal.access.JavaIOFileDescriptorAccess +java.io.FileDescriptor$1 +java.io.Flushable +java.io.OutputStream +java.io.FileOutputStream +java.io.FilterInputStream +java.io.BufferedInputStream +java.io.FilterOutputStream +java.io.PrintStream +java.io.BufferedOutputStream +java.io.Writer +java.io.OutputStreamWriter +java.nio.charset.Charset +java.nio.charset.spi.CharsetProvider +sun.nio.cs.StandardCharsets +java.lang.ThreadLocal +java.util.concurrent.atomic.AtomicInteger +sun.security.action.GetPropertyAction +sun.nio.cs.HistoricallyNamedCharset +sun.nio.cs.Unicode +sun.nio.cs.UTF_8 +sun.nio.cs.StreamEncoder +java.nio.charset.CharsetEncoder +sun.nio.cs.UTF_8$Encoder +java.nio.charset.CodingErrorAction +java.nio.ByteBuffer +jdk.internal.misc.ScopedMemoryAccess +jdk.internal.access.JavaNioAccess +java.nio.Buffer$1 +java.nio.HeapByteBuffer +java.nio.ByteOrder +java.io.BufferedWriter +java.lang.Terminator +jdk.internal.misc.Signal$Handler +java.lang.Terminator$1 +jdk.internal.misc.Signal +java.util.Hashtable$Entry +jdk.internal.misc.Signal$NativeHandler +jdk.internal.misc.OSEnvironment +java.util.Collections +java.util.Collections$EmptySet +java.util.Collections$EmptyList +java.util.Collections$EmptyMap +java.lang.IllegalArgumentException +java.lang.invoke.MethodHandleStatics +jdk.internal.module.ModuleBootstrap +sun.invoke.util.VerifyAccess +java.lang.reflect.Modifier +jdk.internal.access.JavaLangModuleAccess +java.lang.module.ModuleDescriptor$1 +java.io.File +java.io.DefaultFileSystem +java.io.FileSystem +java.io.UnixFileSystem +jdk.internal.util.ArraysSupport +jdk.internal.module.ModulePatcher +jdk.internal.module.ModuleBootstrap$Counters +jdk.internal.module.ArchivedBootLayer +jdk.internal.access.JavaNetUriAccess +java.net.URI$1 +jdk.internal.loader.ArchivedClassLoaders +jdk.internal.loader.ClassLoaders$BootClassLoader +java.security.cert.Certificate +java.lang.ClassLoader$ParallelLoaders +java.util.WeakHashMap +java.util.WeakHashMap$Entry +java.util.Collections$SetFromMap +java.util.WeakHashMap$KeySet +jdk.internal.access.JavaSecurityAccess +java.security.ProtectionDomain$JavaSecurityAccessImpl +java.security.ProtectionDomain$Key +java.security.Principal +jdk.internal.loader.NativeLibraries +jdk.internal.loader.ClassLoaderHelper +java.util.HashSet +java.util.Queue +java.util.Deque +java.util.ArrayDeque +jdk.internal.loader.URLClassPath +java.net.URLStreamHandlerFactory +java.net.URL$DefaultFactory +jdk.internal.access.JavaNetURLAccess +java.net.URL$3 +java.io.File$PathStatus +sun.net.www.ParseUtil +java.util.HexFormat +java.net.URLStreamHandler +sun.net.www.protocol.file.Handler +sun.net.util.IPAddressUtil +jdk.internal.util.Preconditions +sun.net.www.protocol.jar.Handler +jdk.internal.module.ServicesCatalog +jdk.internal.loader.AbstractClassLoaderValue +jdk.internal.loader.ClassLoaderValue +java.util.Optional +jdk.internal.loader.BootLoader +jdk.internal.loader.BuiltinClassLoader$LoadedModule +java.util.ImmutableCollections$SetN$SetNIterator +java.util.ImmutableCollections$Set12$1 +java.util.ListIterator +java.util.ImmutableCollections$ListItr +jdk.internal.module.ModuleLoaderMap +jdk.internal.loader.AbstractClassLoaderValue$Memoizer +jdk.internal.module.ServicesCatalog$ServiceProvider +java.util.concurrent.CopyOnWriteArrayList +java.util.HashMap$KeySet +java.util.HashMap$KeyIterator +java.lang.ModuleLayer$Controller +java.lang.invoke.LambdaMetafactory +java.lang.invoke.MethodType$ConcurrentWeakInternSet +java.lang.Void +java.lang.invoke.MethodTypeForm +java.lang.invoke.MethodType$ConcurrentWeakInternSet$WeakEntry +sun.invoke.util.Wrapper +sun.invoke.util.Wrapper$Format +java.lang.invoke.LambdaForm$NamedFunction +java.lang.invoke.DirectMethodHandle$Holder +sun.invoke.util.ValueConversions +java.lang.invoke.MethodHandleImpl +java.lang.invoke.Invokers +java.lang.invoke.LambdaForm$Kind +java.lang.NoSuchMethodException +java.lang.invoke.LambdaForm$BasicType +java.lang.reflect.Array +java.lang.invoke.LambdaForm$Name +java.lang.invoke.LambdaForm$Holder +java.lang.invoke.InvokerBytecodeGenerator +java.lang.invoke.InvokerBytecodeGenerator$2 +java.lang.invoke.MethodHandleImpl$Intrinsic +java.lang.invoke.BootstrapMethodInvoker +java.lang.invoke.VarHandle$AccessMode +java.lang.invoke.VarHandle$AccessType +java.lang.invoke.Invokers$Holder +jdk.internal.access.JavaLangInvokeAccess +java.lang.invoke.MethodHandleImpl$1 +java.lang.invoke.AbstractValidatingLambdaMetafactory +java.lang.invoke.InnerClassLambdaMetafactory +jdk.internal.org.objectweb.asm.Type +sun.security.action.GetBooleanAction +jdk.internal.org.objectweb.asm.Handle +sun.invoke.util.BytecodeDescriptor +jdk.internal.org.objectweb.asm.ConstantDynamic +java.lang.invoke.MethodHandleInfo +java.lang.invoke.InfoFromMemberName +jdk.internal.org.objectweb.asm.ClassVisitor +jdk.internal.org.objectweb.asm.ClassWriter +jdk.internal.org.objectweb.asm.SymbolTable +jdk.internal.org.objectweb.asm.Symbol +jdk.internal.org.objectweb.asm.SymbolTable$Entry +jdk.internal.org.objectweb.asm.ByteVector +java.lang.invoke.LambdaProxyClassArchive +jdk.internal.org.objectweb.asm.MethodVisitor +jdk.internal.org.objectweb.asm.MethodWriter +jdk.internal.org.objectweb.asm.Label +java.lang.invoke.TypeConvertingMethodAdapter +java.lang.invoke.InnerClassLambdaMetafactory$ForwardingMethodGenerator +jdk.internal.org.objectweb.asm.Handler +jdk.internal.org.objectweb.asm.Attribute +jdk.internal.org.objectweb.asm.AnnotationVisitor +jdk.internal.org.objectweb.asm.AnnotationWriter +java.lang.invoke.MethodHandles$Lookup$ClassOption +java.lang.invoke.MethodHandles$Lookup$ClassFile +jdk.internal.org.objectweb.asm.ClassReader +java.lang.StringUTF16 +java.lang.invoke.MethodHandles$Lookup$ClassDefiner +jdk.internal.module.ModuleBootstrap$$Lambda$1/0x0000007001040850 +java.lang.invoke.InnerClassLambdaMetafactory$1 +java.lang.Class$ReflectionData +java.lang.Class$Atomic +jdk.internal.reflect.DelegatingConstructorAccessorImpl +java.lang.invoke.BoundMethodHandle +java.lang.invoke.ClassSpecializer +java.lang.invoke.BoundMethodHandle$Specializer +java.lang.invoke.ClassSpecializer$1 +java.lang.invoke.ClassSpecializer$SpeciesData +java.lang.invoke.BoundMethodHandle$SpeciesData +java.lang.invoke.ClassSpecializer$Factory +java.lang.invoke.BoundMethodHandle$Specializer$Factory +java.lang.invoke.SimpleMethodHandle +java.lang.NoSuchFieldException +java.lang.invoke.BoundMethodHandle$Species_L +sun.invoke.util.VerifyType +sun.invoke.empty.Empty +java.lang.invoke.DirectMethodHandle$2 +java.lang.invoke.DirectMethodHandle$Accessor +java.lang.invoke.DelegatingMethodHandle +java.lang.invoke.MethodHandleImpl$IntrinsicMethodHandle +java.lang.invoke.DelegatingMethodHandle$Holder +sun.invoke.util.Wrapper$1 +java.lang.invoke.LambdaFormEditor +java.lang.invoke.LambdaFormEditor$TransformKey +java.lang.invoke.LambdaFormBuffer +java.lang.invoke.LambdaFormEditor$Transform +jdk.internal.org.objectweb.asm.Frame +java.lang.invoke.InvokerBytecodeGenerator$ClassData +java.util.ArrayList$Itr +jdk.internal.org.objectweb.asm.FieldVisitor +jdk.internal.org.objectweb.asm.FieldWriter +java.lang.invoke.LambdaForm$MH/0x0000007001000400 +jdk.internal.ref.CleanerFactory +java.util.concurrent.ThreadFactory +jdk.internal.ref.CleanerFactory$1 +java.lang.ref.Cleaner +java.lang.ref.Cleaner$1 +jdk.internal.ref.CleanerImpl +java.lang.ref.Cleaner$Cleanable +jdk.internal.ref.PhantomCleanable +jdk.internal.ref.CleanerImpl$PhantomCleanableRef +jdk.internal.ref.CleanerImpl$CleanerCleanable +jdk.internal.misc.InnocuousThread +java.util.ArrayList$SubList +java.lang.Module$ReflectionData +java.lang.WeakPairMap +java.lang.WeakPairMap$Pair +java.lang.WeakPairMap$Pair$Lookup +java.util.function.BiFunction +java.lang.Module$$Lambda$2/0x0000007001040a90 +java.lang.WeakPairMap$WeakRefPeer +java.lang.WeakPairMap$Pair$Weak +java.lang.WeakPairMap$Pair$Weak$1 +java.lang.WeakPairMap$$Lambda$3/0x00000070010413e8 +java.lang.invoke.DirectMethodHandle$Constructor +java.lang.invoke.StringConcatFactory +java.lang.invoke.StringConcatFactory$1 +java.lang.invoke.StringConcatFactory$2 +java.lang.invoke.StringConcatFactory$3 +sun.launcher.LauncherHelper +java.lang.StringCoding +java.util.zip.ZipConstants +java.util.zip.ZipFile +java.util.jar.JarFile +jdk.internal.access.JavaUtilZipFileAccess +java.util.zip.ZipFile$1 +jdk.internal.access.JavaUtilJarAccess +java.util.jar.JavaUtilJarAccessImpl +java.lang.Runtime$Version +java.util.zip.ZipFile$CleanableResource +java.util.zip.ZipCoder +java.util.zip.ZipCoder$UTF8ZipCoder +java.util.zip.ZipFile$Source +sun.nio.fs.DefaultFileSystemProvider +java.nio.file.spi.FileSystemProvider +sun.nio.fs.AbstractFileSystemProvider +sun.nio.fs.UnixFileSystemProvider +sun.nio.fs.BsdFileSystemProvider +sun.nio.fs.MacOSXFileSystemProvider +java.nio.file.OpenOption +java.nio.file.StandardOpenOption +java.nio.file.FileSystem +sun.nio.fs.UnixFileSystem +sun.nio.fs.BsdFileSystem +sun.nio.fs.MacOSXFileSystem +java.nio.file.Watchable +java.nio.file.Path +sun.nio.fs.UnixPath +sun.nio.fs.Util +sun.nio.fs.UnixNativeDispatcher +jdk.internal.loader.NativeLibraries$LibraryPaths +jdk.internal.loader.NativeLibraries$1 +java.util.ArrayDeque$DeqIterator +jdk.internal.loader.NativeLibrary +jdk.internal.loader.NativeLibraries$NativeLibraryImpl +jdk.internal.loader.NativeLibraries$NativeLibraryImpl$1 +java.util.concurrent.ConcurrentHashMap$CollectionView +java.util.concurrent.ConcurrentHashMap$ValuesView +java.util.concurrent.ConcurrentHashMap$Traverser +java.util.concurrent.ConcurrentHashMap$BaseIterator +java.util.Enumeration +java.util.concurrent.ConcurrentHashMap$ValueIterator +java.nio.file.attribute.BasicFileAttributes +java.nio.file.attribute.PosixFileAttributes +sun.nio.fs.UnixFileAttributes +sun.nio.fs.UnixFileStoreAttributes +sun.nio.fs.UnixMountEntry +java.util.zip.ZipFile$Source$Key +java.nio.file.CopyOption +java.nio.file.LinkOption +java.nio.file.Files +java.nio.file.attribute.AttributeView +java.nio.file.attribute.FileAttributeView +java.nio.file.attribute.BasicFileAttributeView +java.nio.file.attribute.UserDefinedFileAttributeView +sun.nio.fs.UnixFileAttributeViews +sun.nio.fs.DynamicFileAttributeView +sun.nio.fs.AbstractBasicFileAttributeView +sun.nio.fs.UnixFileAttributeViews$Basic +sun.nio.fs.NativeBuffers +jdk.internal.misc.TerminatingThreadLocal +sun.nio.fs.NativeBuffers$1 +jdk.internal.misc.TerminatingThreadLocal$1 +java.lang.ThreadLocal$ThreadLocalMap +java.lang.ThreadLocal$ThreadLocalMap$Entry +java.util.IdentityHashMap +java.util.IdentityHashMap$KeySet +sun.nio.fs.NativeBuffer +sun.nio.fs.NativeBuffer$Deallocator +sun.nio.fs.UnixFileAttributes$UnixAsBasicFileAttributes +java.io.DataOutput +java.io.DataInput +java.io.RandomAccessFile +jdk.internal.access.JavaIORandomAccessFileAccess +java.io.RandomAccessFile$2 +java.io.FileCleanable +java.util.zip.ZipFile$Source$End +java.util.zip.ZipUtils +java.util.concurrent.TimeUnit +java.nio.file.attribute.FileTime +jdk.internal.perf.PerfCounter +jdk.internal.perf.Perf$GetPerfAction +jdk.internal.perf.Perf +jdk.internal.perf.PerfCounter$CoreCounters +sun.nio.ch.DirectBuffer +java.nio.MappedByteBuffer +java.nio.DirectByteBuffer +java.nio.Bits +java.util.concurrent.atomic.AtomicLong +jdk.internal.misc.VM$BufferPool +java.nio.Bits$1 +java.nio.LongBuffer +java.nio.DirectLongBufferU +java.util.zip.ZipEntry +java.util.jar.JarEntry +java.util.jar.JarFile$JarFileEntry +java.util.zip.ZipFile$ZipFileInputStream +java.util.zip.InflaterInputStream +java.util.zip.ZipFile$ZipFileInflaterInputStream +java.util.zip.Inflater +java.util.zip.Inflater$InflaterZStreamRef +java.util.zip.ZipFile$InflaterCleanupAction +sun.security.util.SignatureFileVerifier +sun.security.util.Debug +java.util.Locale +sun.util.locale.LocaleUtils +sun.security.action.GetIntegerAction +java.util.jar.JarVerifier +java.security.CodeSigner +java.io.ByteArrayOutputStream +java.util.jar.Attributes +java.util.LinkedHashMap +java.util.jar.Manifest$FastInputStream +java.io.RandomAccessFile$1 +sun.net.util.URLUtil +java.security.PrivilegedExceptionAction +jdk.internal.loader.URLClassPath$3 +jdk.internal.loader.URLClassPath$Loader +jdk.internal.loader.URLClassPath$JarLoader +jdk.internal.loader.URLClassPath$JarLoader$1 +jdk.internal.loader.FileURLMapper +jdk.internal.util.jar.JarIndex +java.util.StringTokenizer +jdk.internal.loader.Resource +jdk.internal.loader.URLClassPath$JarLoader$2 +java.lang.NamedPackage +java.lang.Package +java.lang.Package$VersionInfo +sun.nio.ByteBuffered +java.util.zip.Checksum +java.util.zip.CRC32 +java.util.zip.Checksum$1 +java.security.SecureClassLoader$CodeSourceKey +java.security.SecureClassLoader$1 +java.security.PermissionCollection +sun.security.util.LazyCodeSourcePermissionCollection +java.security.Permissions +java.lang.RuntimePermission +java.security.BasicPermissionCollection +java.security.AllPermission +java.security.UnresolvedPermission +java.security.SecureClassLoader$DebugHolder +org.apache.maven.surefire.booter.ForkedBooter +org.apache.maven.surefire.api.fork.ForkNodeArguments +org.apache.maven.plugin.surefire.log.api.ConsoleLogger +java.lang.SecurityException +java.security.AccessControlException +java.io.IOException +org.apache.maven.surefire.api.provider.CommandListener +java.lang.InterruptedException +java.util.concurrent.ConcurrentHashMap$ForwardingNode +java.util.concurrent.Executor +java.util.concurrent.ExecutorService +java.util.concurrent.ScheduledExecutorService +org.apache.maven.surefire.api.report.ReporterFactory +org.apache.maven.surefire.api.provider.CommandChainReader +java.lang.PublicMethods$MethodList +java.lang.PublicMethods$Key +java.util.concurrent.Semaphore +java.util.concurrent.locks.AbstractQueuedSynchronizer +java.util.concurrent.Semaphore$Sync +java.util.concurrent.Semaphore$NonfairSync +org.apache.maven.surefire.booter.BooterDeserializer +org.apache.maven.surefire.booter.SystemPropertyManager +java.util.Properties$LineReader +java.util.Properties$EntrySet +java.util.concurrent.ConcurrentHashMap$EntrySetView +java.util.Collections$SynchronizedCollection +java.util.Collections$SynchronizedSet +java.util.concurrent.ConcurrentHashMap$EntryIterator +java.util.concurrent.ConcurrentHashMap$MapEntry +java.util.Collections$UnmodifiableCollection +java.util.Collections$UnmodifiableSet +java.util.Collections$UnmodifiableCollection$1 +org.apache.maven.surefire.booter.KeyValueSource +org.apache.maven.surefire.booter.PropertiesWrapper +java.lang.IllegalStateException +java.io.FileInputStream$1 +org.apache.maven.surefire.booter.TypeEncodedValue +org.apache.maven.surefire.api.testset.DirectoryScannerParameters +org.apache.maven.surefire.api.util.RunOrder +org.apache.maven.surefire.api.testset.RunOrderParameters +org.apache.maven.surefire.api.testset.TestArtifactInfo +org.apache.maven.surefire.api.testset.TestRequest +org.apache.maven.surefire.api.testset.TestFilter +org.apache.maven.surefire.api.testset.GenericTestPattern +org.apache.maven.surefire.api.testset.TestListResolver +java.util.Collections$SingletonSet +org.apache.maven.surefire.api.testset.IncludedExcludedPatterns +java.util.LinkedHashSet +java.util.Collections$1 +org.apache.maven.surefire.shared.utils.StringUtils +java.lang.IndexOutOfBoundsException +java.lang.StringIndexOutOfBoundsException +org.apache.maven.surefire.api.testset.ResolvedTest +org.apache.maven.surefire.api.testset.ResolvedTest$Type +org.apache.maven.surefire.api.testset.ResolvedTest$ClassMatcher +org.apache.maven.surefire.api.testset.ResolvedTest$MethodMatcher +org.apache.maven.surefire.api.report.ReporterConfiguration +org.apache.maven.surefire.api.booter.Shutdown +java.lang.Class$3 +jdk.internal.reflect.NativeMethodAccessorImpl +jdk.internal.reflect.DelegatingMethodAccessorImpl +org.apache.maven.surefire.booter.ProviderConfiguration +org.apache.maven.surefire.api.cli.CommandLineOption +org.apache.maven.surefire.api.booter.DumpErrorSingleton +org.apache.maven.surefire.api.util.internal.DumpFileUtils +java.lang.management.ManagementFactory +java.lang.IncompatibleClassChangeError +java.lang.NoSuchMethodError +java.lang.invoke.LambdaForm$DMH/0x0000007001006000 +java.lang.management.ManagementFactory$$Lambda$4/0x00000070010443a0 +java.lang.management.PlatformManagedObject +java.lang.management.RuntimeMXBean +java.lang.management.ManagementFactory$PlatformMBeanFinder +java.lang.management.ManagementFactory$PlatformMBeanFinder$1 +java.io.FilePermission +jdk.internal.access.JavaIOFilePermissionAccess +java.io.FilePermission$1 +sun.security.util.FilePermCompat +sun.security.util.SecurityProperties +java.security.Security +java.security.Security$1 +jdk.internal.access.JavaSecurityPropertiesAccess +java.security.Security$2 +sun.management.spi.PlatformMBeanProvider +java.util.ServiceLoader +java.util.ServiceLoader$ModuleServicesLookupIterator +java.util.ServiceLoader$LazyClassPathLookupIterator +java.util.ServiceLoader$2 +java.util.ServiceLoader$3 +java.util.concurrent.CopyOnWriteArrayList$COWIterator +com.sun.management.internal.PlatformMBeanProviderImpl +java.util.ServiceLoader$1 +java.util.ServiceLoader$Provider +java.util.ServiceLoader$ProviderImpl +com.sun.management.internal.PlatformMBeanProviderImpl$$Lambda$5/0x0000007001045a48 +sun.management.spi.PlatformMBeanProvider$PlatformComponent +com.sun.management.internal.PlatformMBeanProviderImpl$1 +java.util.stream.BaseStream +java.util.stream.Stream +java.util.Spliterators +java.util.Spliterators$EmptySpliterator +java.util.Spliterator +java.util.Spliterators$EmptySpliterator$OfRef +java.util.Spliterator$OfPrimitive +java.util.Spliterator$OfInt +java.util.Spliterators$EmptySpliterator$OfInt +java.util.Spliterator$OfLong +java.util.Spliterators$EmptySpliterator$OfLong +java.util.Spliterator$OfDouble +java.util.Spliterators$EmptySpliterator$OfDouble +java.util.Spliterators$ArraySpliterator +java.util.stream.StreamSupport +java.util.stream.PipelineHelper +java.util.stream.AbstractPipeline +java.util.stream.ReferencePipeline +java.util.stream.ReferencePipeline$Head +java.util.stream.StreamOpFlag +java.util.stream.StreamOpFlag$Type +java.util.stream.StreamOpFlag$MaskBuilder +java.util.EnumMap +java.util.EnumMap$1 +sun.reflect.annotation.AnnotationParser +java.util.stream.Collectors +java.util.stream.Collector$Characteristics +java.util.EnumSet +java.util.RegularEnumSet +java.util.stream.Collector +java.util.stream.Collectors$CollectorImpl +java.util.stream.Collectors$$Lambda$31/0x800000046 +java.util.function.BiConsumer +java.lang.invoke.DirectMethodHandle$Interface +java.util.stream.Collectors$$Lambda$23/0x80000003a +java.util.function.BinaryOperator +java.util.stream.Collectors$$Lambda$26/0x800000041 +java.util.stream.Collectors$$Lambda$28/0x800000043 +java.util.stream.ReduceOps +java.util.stream.TerminalOp +java.util.stream.ReduceOps$ReduceOp +java.util.stream.ReduceOps$3 +java.util.stream.StreamShape +java.util.stream.ReduceOps$Box +java.util.function.Consumer +java.util.stream.Sink +java.util.stream.TerminalSink +java.util.stream.ReduceOps$AccumulatingSink +java.util.stream.ReduceOps$3ReducingSink +com.sun.management.internal.PlatformMBeanProviderImpl$2 +com.sun.management.internal.PlatformMBeanProviderImpl$3 +com.sun.management.internal.PlatformMBeanProviderImpl$4 +javax.management.DynamicMBean +com.sun.management.DiagnosticCommandMBean +javax.management.NotificationBroadcaster +javax.management.NotificationEmitter +sun.management.NotificationEmitterSupport +com.sun.management.internal.DiagnosticCommandImpl +sun.management.ManagementFactoryHelper +sun.management.VMManagement +sun.management.VMManagementImpl +com.sun.management.internal.PlatformMBeanProviderImpl$5 +java.util.Collections$UnmodifiableList +java.util.Collections$UnmodifiableRandomAccessList +jdk.management.jfr.internal.FlightRecorderMXBeanProvider +java.util.concurrent.Callable +java.util.Collections$EmptyEnumeration +java.lang.management.DefaultPlatformMBeanProvider +java.lang.management.DefaultPlatformMBeanProvider$1 +java.lang.management.DefaultPlatformMBeanProvider$2 +java.lang.management.DefaultPlatformMBeanProvider$3 +java.lang.management.DefaultPlatformMBeanProvider$4 +java.lang.management.DefaultPlatformMBeanProvider$5 +java.lang.management.DefaultPlatformMBeanProvider$6 +java.lang.management.DefaultPlatformMBeanProvider$7 +java.lang.management.DefaultPlatformMBeanProvider$8 +sun.management.ManagementFactoryHelper$LoggingMXBeanAccess +sun.management.ManagementFactoryHelper$LoggingMXBeanAccess$1 +java.util.logging.LogManager +java.lang.management.DefaultPlatformMBeanProvider$9 +java.lang.management.DefaultPlatformMBeanProvider$10 +java.lang.management.DefaultPlatformMBeanProvider$11 +jdk.management.jfr.FlightRecorderMXBean +jdk.management.jfr.internal.FlightRecorderMXBeanProvider$SingleMBeanComponent +java.util.Collections$SingletonList +java.util.HashMap$Values +java.util.HashMap$HashMapSpliterator +java.util.HashMap$ValueSpliterator +java.util.function.Predicate +java.lang.management.ManagementFactory$PlatformMBeanFinder$$Lambda$10/0x000000700104b430 +java.util.stream.ReferencePipeline$StatelessOp +java.util.stream.ReferencePipeline$2 +java.lang.management.ManagementFactory$PlatformMBeanFinder$$Lambda$11/0x000000700104b688 +java.util.stream.ReduceOps$2 +java.util.stream.ReduceOps$2ReducingSink +java.util.stream.Sink$ChainedReference +java.util.stream.ReferencePipeline$2$1 +sun.management.RuntimeImpl +java.util.Collections$SingletonMap +java.util.Collections$2 +java.lang.invoke.LambdaForm$DMH/0x0000007001006400 +sun.management.spi.PlatformMBeanProvider$PlatformComponent$$Lambda$12/0x000000700104c478 +sun.management.spi.PlatformMBeanProvider$PlatformComponent$$Lambda$13/0x000000700104c6d0 +java.util.stream.ReferencePipeline$3 +java.util.stream.Collectors$$Lambda$14/0x000000700104c918 +java.util.stream.Collectors$$Lambda$15/0x000000700104cb38 +java.util.stream.Collectors$$Lambda$16/0x000000700104cd68 +java.util.stream.ReferencePipeline$3$1 +sun.management.Util +java.lang.management.ManagementPermission +java.util.Arrays$ArrayList +java.util.Arrays$ArrayItr +org.apache.maven.surefire.booter.ClassLoaderConfiguration +org.apache.maven.surefire.booter.AbstractPathConfiguration +org.apache.maven.surefire.booter.ClasspathConfiguration +org.apache.maven.surefire.booter.Classpath +java.net.MalformedURLException +java.net.URLClassLoader +org.apache.maven.surefire.booter.IsolatedClassLoader +org.apache.maven.surefire.booter.SurefireExecutionException +org.apache.maven.surefire.booter.ProcessCheckerType +org.apache.maven.surefire.booter.StartupConfiguration +org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory +java.util.Spliterators$1Adapter +java.util.HashMap$ValueIterator +jdk.internal.module.Resources +jdk.internal.loader.BuiltinClassLoader$2 +jdk.internal.loader.BuiltinClassLoader$5 +java.lang.module.ModuleReader +jdk.internal.module.SystemModuleFinders$SystemModuleReader +jdk.internal.module.SystemModuleFinders$SystemImage +jdk.internal.jimage.ImageReaderFactory +java.nio.file.Paths +java.nio.file.FileSystems +java.nio.file.FileSystems$DefaultFileSystemHolder +java.nio.file.FileSystems$DefaultFileSystemHolder$1 +java.net.URI$Parser +jdk.internal.jimage.ImageReaderFactory$1 +jdk.internal.jimage.ImageReader +jdk.internal.jimage.BasicImageReader +jdk.internal.jimage.ImageReader$SharedImageReader +jdk.internal.jimage.BasicImageReader$1 +jdk.internal.jimage.NativeImageBuffer +jdk.internal.jimage.NativeImageBuffer$1 +jdk.internal.jimage.ImageHeader +java.nio.IntBuffer +java.nio.DirectIntBufferU +java.nio.DirectByteBufferR +java.nio.DirectIntBufferRU +jdk.internal.jimage.ImageStrings +jdk.internal.jimage.ImageStringsReader +jdk.internal.jimage.decompressor.Decompressor +jdk.internal.jimage.ImageLocation +java.util.Collections$EmptyIterator +jdk.internal.loader.BuiltinClassLoader$1 +java.lang.CompoundEnumeration +jdk.internal.loader.URLClassPath$1 +jdk.internal.loader.URLClassPath$FileLoader +java.util.SortedSet +java.util.NavigableSet +java.util.TreeSet +java.util.SortedMap +java.util.NavigableMap +java.util.TreeMap +java.util.TreeMap$Entry +java.util.TreeMap$KeySet +java.util.TreeMap$PrivateEntryIterator +java.util.TreeMap$KeyIterator +java.lang.Readable +java.io.Reader +java.io.BufferedReader +java.io.InputStreamReader +sun.nio.cs.StreamDecoder +java.nio.charset.CharsetDecoder +sun.nio.cs.UTF_8$Decoder +java.util.Vector +java.nio.CharBuffer +java.nio.HeapCharBuffer +java.nio.charset.CoderResult +java.util.AbstractSequentialList +java.util.LinkedList +java.util.LinkedList$Node +java.net.URLConnection +java.net.JarURLConnection +sun.net.www.protocol.jar.JarURLConnection +sun.net.www.protocol.jar.URLJarFile$URLJarFileCloseController +sun.net.www.protocol.jar.JarFileFactory +sun.net.www.URLConnection +sun.net.www.protocol.file.FileURLConnection +sun.net.www.MessageHeader +sun.net.www.protocol.jar.URLJarFile +sun.nio.fs.UnixFileKey +sun.net.www.protocol.jar.URLJarFile$URLJarFileEntry +sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream +java.util.LinkedHashMap$LinkedKeySet +java.util.LinkedHashMap$LinkedHashIterator +java.util.LinkedHashMap$LinkedKeyIterator +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory +org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory +org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder +org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder +org.apache.maven.surefire.api.util.internal.DaemonThreadFactory +java.util.concurrent.Executors +java.util.concurrent.Executors$DefaultThreadFactory +org.apache.maven.surefire.api.util.internal.DaemonThreadFactory$NamedThreadFactory +java.util.concurrent.AbstractExecutorService +java.util.concurrent.ThreadPoolExecutor +java.util.concurrent.ScheduledThreadPoolExecutor +java.util.concurrent.RejectedExecutionHandler +java.util.concurrent.ThreadPoolExecutor$AbortPolicy +java.util.concurrent.BlockingQueue +java.util.AbstractQueue +java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue +java.util.concurrent.Future +java.util.concurrent.RunnableFuture +java.util.concurrent.Delayed +java.util.concurrent.ScheduledFuture +java.util.concurrent.RunnableScheduledFuture +java.util.concurrent.locks.ReentrantLock$Sync +java.util.concurrent.locks.ReentrantLock$NonfairSync +java.util.concurrent.locks.Condition +java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject +org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory +java.net.URISyntaxException +java.util.concurrent.ExecutionException +java.net.SocketAddress +java.net.InetSocketAddress +java.nio.channels.Channel +java.nio.channels.AsynchronousChannel +java.nio.channels.AsynchronousByteChannel +org.apache.maven.surefire.booter.ForkedNodeArg +java.lang.UnsupportedOperationException +org.apache.maven.plugin.surefire.log.api.NullConsoleLogger +org.apache.maven.surefire.api.util.internal.Channels +org.apache.maven.surefire.api.util.internal.Channels$2 +org.apache.maven.surefire.api.util.internal.Channels$1 +java.nio.channels.ReadableByteChannel +java.nio.channels.WritableByteChannel +org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel +org.apache.maven.surefire.api.util.internal.AbstractNoninterruptibleWritableChannel +org.apache.maven.surefire.api.util.internal.Channels$4 +java.nio.channels.ClosedChannelException +java.nio.channels.NonWritableChannelException +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory$1 +java.util.concurrent.FutureTask +java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask +java.lang.invoke.VarHandles +java.lang.invoke.VarHandleInts$FieldInstanceReadOnly +java.lang.invoke.VarHandleInts$FieldInstanceReadWrite +java.lang.invoke.VarHandle$1 +jdk.internal.util.Preconditions$1 +java.lang.invoke.VarHandleGuards +java.lang.invoke.VarForm +java.lang.invoke.VarHandleReferences$FieldInstanceReadOnly +java.lang.invoke.VarHandleReferences$FieldInstanceReadWrite +java.util.concurrent.FutureTask$WaitNode +java.util.concurrent.Executors$RunnableAdapter +java.util.concurrent.ThreadPoolExecutor$Worker +java.lang.Thread$State +java.util.concurrent.TimeUnit$1 +java.time.temporal.TemporalUnit +java.time.temporal.ChronoUnit +java.time.temporal.TemporalAmount +java.time.Duration +java.math.BigInteger +java.lang.invoke.VarHandle$AccessDescriptor +org.apache.maven.surefire.api.stream.AbstractStreamEncoder +org.apache.maven.surefire.booter.stream.EventEncoder +org.apache.maven.surefire.booter.spi.EventChannelEncoder +java.lang.AssertionError +java.util.concurrent.ForkJoinPool$ManagedBlocker +java.util.concurrent.locks.AbstractQueuedSynchronizer$Node +java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode +org.apache.maven.surefire.api.booter.ForkedProcessEventType +org.apache.maven.surefire.api.report.ReportEntry +java.util.concurrent.atomic.AtomicBoolean +org.apache.maven.surefire.booter.spi.CommandChannelDecoder +org.apache.maven.surefire.api.stream.MalformedChannelException +org.apache.maven.surefire.api.stream.AbstractStreamDecoder +org.apache.maven.surefire.booter.stream.CommandDecoder +org.apache.maven.surefire.api.util.internal.AbstractNoninterruptibleReadableChannel +org.apache.maven.surefire.api.util.internal.Channels$3 +java.nio.channels.NonReadableChannelException +java.io.EOFException +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$MalformedFrameException +org.apache.maven.surefire.api.stream.SegmentType +java.io.FileNotFoundException +org.apache.maven.surefire.api.booter.Constants +java.nio.charset.StandardCharsets +sun.nio.cs.US_ASCII +sun.nio.cs.ISO_8859_1 +sun.nio.cs.UTF_16BE +sun.nio.cs.UTF_16LE +sun.nio.cs.UTF_16 +org.apache.maven.surefire.api.booter.MasterProcessCommand +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$Segment +org.apache.maven.surefire.booter.ForkedBooter$8 +org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils +java.lang.ApplicationShutdownHooks +java.lang.ApplicationShutdownHooks$1 +java.lang.Shutdown +java.lang.Shutdown$Lock +org.apache.maven.surefire.api.booter.ForkingReporterFactory +org.apache.maven.surefire.api.report.RunListener +org.apache.maven.surefire.api.report.TestOutputReceiver +org.apache.maven.surefire.api.report.TestReportListener +org.apache.maven.surefire.api.booter.ForkingRunListener +org.apache.maven.surefire.booter.CommandReader +org.apache.maven.surefire.api.testset.TestSetFailedException +java.util.concurrent.ConcurrentLinkedQueue +java.util.concurrent.ConcurrentLinkedQueue$Node +org.apache.maven.surefire.booter.CommandReader$CommandRunnable +java.util.concurrent.atomic.AtomicReference +java.util.concurrent.CountDownLatch +java.util.concurrent.CountDownLatch$Sync +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$Memento +org.apache.maven.surefire.booter.PpidChecker +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$BufferedStream +org.apache.maven.surefire.booter.PpidChecker$ProcessInfoConsumer +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$StreamReadStatus +org.apache.maven.surefire.booter.PpidChecker$1 +org.apache.maven.surefire.booter.stream.CommandDecoder$1 +org.apache.maven.surefire.booter.PpidChecker$2 +java.lang.NoSuchFieldError +org.apache.maven.surefire.api.booter.Command +org.apache.maven.surefire.booter.CommandReader$1 +java.util.concurrent.ConcurrentLinkedQueue$Itr +org.apache.maven.surefire.shared.lang3.SystemUtils +org.apache.maven.surefire.shared.lang3.JavaVersion +org.apache.maven.surefire.shared.lang3.math.NumberUtils +java.lang.NumberFormatException +java.math.BigDecimal +jdk.internal.math.FloatingDecimal +jdk.internal.math.FloatingDecimal$BinaryToASCIIConverter +jdk.internal.math.FloatingDecimal$ExceptionalBinaryToASCIIBuffer +jdk.internal.math.FloatingDecimal$BinaryToASCIIBuffer +jdk.internal.math.FloatingDecimal$1 +jdk.internal.math.FloatingDecimal$ASCIIToBinaryConverter +jdk.internal.math.FloatingDecimal$PreparedASCIIToBinaryBuffer +jdk.internal.math.FloatingDecimal$ASCIIToBinaryBuffer +org.apache.maven.surefire.shared.lang3.StringUtils +java.util.regex.Pattern +java.util.regex.Pattern$Node +java.util.regex.Pattern$LastNode +java.util.regex.Pattern$GroupHead +java.util.regex.CharPredicates +java.lang.Character$Subset +java.lang.Character$UnicodeBlock +java.util.regex.Pattern$CharPredicate +java.util.regex.CharPredicates$$Lambda$17/0x000000700105cff8 +java.util.regex.Pattern$BmpCharPredicate +java.util.regex.Pattern$CharProperty +java.util.regex.Pattern$Qtype +java.util.regex.Pattern$BmpCharProperty +java.util.regex.Pattern$CharPropertyGreedy +java.util.regex.Pattern$SliceNode +java.util.regex.Pattern$Slice +java.util.regex.Pattern$Begin +java.util.regex.Pattern$First +java.util.regex.Pattern$Start +java.util.regex.Pattern$StartS +java.util.regex.Pattern$TreeInfo +java.util.regex.Pattern$GroupTail +java.util.regex.CharPredicates$$Lambda$17/0x800000025 +java.util.regex.Pattern$BmpCharPropertyGreedy +java.util.regex.Pattern$$Lambda$19/0x800000029 +java.util.regex.Pattern$Ques +java.util.regex.Pattern$BranchConn +java.util.regex.Pattern$Branch +java.util.regex.ASCII +java.util.regex.Pattern$Curly +java.util.regex.CharPredicates$$Lambda$18/0x800000026 +java.util.regex.Pattern$Dollar +java.util.regex.Pattern$BitClass +org.apache.maven.surefire.booter.ForkedBooter$4 +org.apache.maven.surefire.api.booter.BiProperty +org.apache.maven.surefire.booter.ForkedBooter$3 +org.apache.maven.surefire.booter.ForkedBooter$PingScheduler +org.apache.maven.surefire.api.provider.ProviderParameters +org.apache.maven.surefire.api.booter.BaseProviderFactory +org.apache.maven.surefire.api.util.DirectoryScanner +org.apache.maven.surefire.api.util.ScanResult +org.apache.maven.surefire.api.util.RunOrderCalculator +org.apache.maven.surefire.api.util.ReflectionUtils +org.apache.maven.surefire.api.util.SurefireReflectionException +java.lang.reflect.InvocationTargetException +java.lang.IllegalAccessException +org.apache.maven.surefire.api.provider.SurefireProvider +org.apache.maven.surefire.api.provider.AbstractProvider +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +org.junit.platform.launcher.Launcher +org.apache.maven.surefire.api.util.ScannerFilter +java.io.StringReader +java.io.UncheckedIOException +org.apache.maven.surefire.junitplatform.LazyLauncher +org.junit.platform.launcher.TagFilter +org.junit.platform.commons.JUnitException +org.junit.platform.commons.util.PreconditionViolationException +org.junit.platform.commons.PreconditionViolationException +org.junit.platform.engine.Filter +org.junit.platform.launcher.PostDiscoveryFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$21/0x00000070010136c8 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$22/0x0000007001013908 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$23/0x0000007001013b40 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$24/0x0000007001013d80 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$25/0x0000007001013fb8 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$26/0x0000007001014208 +org.apache.maven.surefire.junitplatform.TestMethodFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$27/0x00000070010146b8 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$28/0x00000070010148f8 +org.junit.platform.launcher.EngineFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$29/0x0000007001014d88 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$30/0x0000007001014fc8 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$31/0x0000007001015200 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$32/0x0000007001015440 +org.junit.platform.launcher.TestExecutionListener +org.apache.maven.surefire.report.RunModeSetter +org.apache.maven.surefire.junitplatform.RunListenerAdapter +org.apache.maven.surefire.api.report.OutputReportEntry +org.apache.maven.surefire.api.report.TestSetReportEntry +org.apache.maven.surefire.api.report.StackTraceWriter +org.apache.maven.surefire.report.ClassMethodIndexer +org.apache.maven.surefire.api.report.RunMode +org.apache.maven.surefire.api.report.ConsoleOutputCapture +org.apache.maven.surefire.api.report.ConsoleOutputCapture$ForwardingPrintStream +org.apache.maven.surefire.api.report.ConsoleOutputCapture$NullOutputStream +java.util.logging.Logger +java.util.logging.Handler +java.util.logging.Level +java.util.logging.Level$KnownLevel +java.util.logging.Level$KnownLevel$$Lambda$14/0x800000022 +java.util.logging.Level$KnownLevel$$Lambda$15/0x800000023 +java.util.logging.Logger$LoggerBundle +java.util.logging.Logger$ConfigurationData +java.util.logging.LogManager$1 +java.util.logging.LogManager$LoggerContext +java.util.logging.LogManager$SystemLoggerContext +java.util.logging.LogManager$LogNode +java.util.Collections$SynchronizedMap +java.util.logging.LogManager$Cleaner +java.util.logging.LoggingPermission +sun.util.logging.internal.LoggingProviderImpl$LogManagerAccess +java.util.logging.LogManager$LoggingProviderAccess +java.lang.System$LoggerFinder +jdk.internal.logger.DefaultLoggerFinder +sun.util.logging.internal.LoggingProviderImpl +java.util.logging.LogManager$2 +java.util.logging.LogManager$RootLogger +java.util.logging.LogManager$LoggerWeakRef +java.lang.invoke.MethodHandleImpl$AsVarargsCollector +java.lang.invoke.BoundMethodHandle$Species_LL +java.lang.invoke.LambdaForm$MH/0x0000007001018000 +java.util.logging.LogManager$VisitedLoggers +java.util.logging.LogManager$LoggerContext$1 +java.util.concurrent.ConcurrentHashMap$KeySetView +java.util.Collections$3 +java.util.concurrent.ConcurrentHashMap$KeyIterator +java.util.Hashtable$Enumerator +java.util.logging.Level$$Lambda$13/0x800000011 +java.util.ArrayList$ArrayListSpliterator +java.util.logging.Level$KnownLevel$$Lambda$16/0x800000024 +java.util.stream.ReferencePipeline$7 +java.util.stream.FindOps +java.util.stream.FindOps$FindSink +java.util.stream.FindOps$FindSink$OfRef +java.util.stream.FindOps$FindOp +java.util.stream.FindOps$FindSink$OfRef$$Lambda$41/0x800000050 +java.util.stream.FindOps$FindSink$OfRef$$Lambda$39/0x80000004e +java.util.stream.FindOps$FindSink$OfRef$$Lambda$40/0x80000004f +java.util.stream.FindOps$FindSink$OfRef$$Lambda$38/0x80000004d +java.util.stream.ReferencePipeline$7$1 +java.util.stream.Streams$AbstractStreamBuilderImpl +java.util.stream.Stream$Builder +java.util.stream.Streams$StreamBuilderImpl +java.util.stream.Streams +java.util.IdentityHashMap$Values +java.lang.System$Logger +sun.util.logging.PlatformLogger$Bridge +sun.util.logging.PlatformLogger$ConfigurableBridge +jdk.internal.logger.BootstrapLogger +jdk.internal.logger.BootstrapLogger$DetectBackend +jdk.internal.logger.BootstrapLogger$DetectBackend$1 +jdk.internal.logger.BootstrapLogger$LoggingBackend +jdk.internal.logger.BootstrapLogger$RedirectedLoggers +jdk.internal.logger.BootstrapLogger$BootstrapExecutors +java.util.logging.LogManager$4 +java.util.logging.Logger$SystemLoggerHelper +java.util.logging.Logger$SystemLoggerHelper$1 +jdk.internal.logger.DefaultLoggerFinder$1 +org.apache.maven.surefire.junitplatform.TestPlanScannerFilter +org.apache.maven.surefire.api.util.DefaultScanResult +jdk.internal.loader.URLClassPath$FileLoader$1 +software.amazon.lambda.powertools.metrics.MetricsBuilderTest +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder +org.junit.platform.engine.ConfigurationParameters +org.junit.platform.engine.EngineDiscoveryRequest +org.junit.platform.launcher.LauncherDiscoveryRequest +org.junit.platform.engine.DiscoverySelector +org.junit.platform.engine.discovery.DiscoverySelectors +org.junit.platform.commons.util.Preconditions +org.junit.platform.commons.util.StringUtils +java.util.regex.CharPredicates$$Lambda$41/0x000000700105e690 +org.junit.platform.engine.discovery.ClassSelector +java.lang.invoke.LambdaForm$DMH/0x0000007001018400 +org.junit.platform.commons.util.Preconditions$$Lambda$42/0x000000700101d038 +org.junit.platform.commons.util.Preconditions$$Lambda$43/0x000000700101d270 +java.lang.invoke.LambdaForm$DMH/0x0000007001018800 +java.lang.invoke.DirectMethodHandle$Special +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$44/0x000000700101d4a8 +org.junit.platform.launcher.core.LauncherConfigurationParameters +org.junit.platform.commons.logging.LoggerFactory +org.junit.platform.commons.logging.Logger +org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger +org.junit.platform.launcher.core.LauncherConfigurationParameters$Builder +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$2 +org.junit.platform.commons.util.ClassLoaderUtils +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$3 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$45/0x000000700101ea88 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$46/0x000000700101ecd0 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners +org.junit.platform.engine.EngineDiscoveryListener +org.junit.platform.launcher.LauncherDiscoveryListener +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType$$Lambda$47/0x000000700101f770 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType$$Lambda$48/0x000000700101f990 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$49/0x000000700101fda8 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$50/0x000000700101a000 +org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener +org.junit.platform.engine.EngineDiscoveryListener$1 +org.junit.platform.launcher.LauncherDiscoveryListener$1 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$51/0x000000700101a958 +org.junit.platform.launcher.core.DefaultDiscoveryRequest +org.junit.platform.launcher.LauncherSession +org.junit.platform.launcher.core.LauncherFactory +org.junit.platform.launcher.core.LauncherConfig +org.junit.platform.launcher.core.LauncherConfig$Builder +org.junit.platform.launcher.core.DefaultLauncherConfig +org.junit.platform.launcher.core.DefaultLauncherSession +org.junit.platform.launcher.LauncherInterceptor +org.junit.platform.launcher.core.DefaultLauncherSession$1 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$52/0x0000007001019000 +org.junit.platform.launcher.LauncherSessionListener +org.junit.platform.launcher.core.LauncherFactory$$Lambda$53/0x0000007001019440 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$54/0x0000007001019668 +org.junit.platform.launcher.core.ListenerRegistry +org.junit.platform.launcher.listeners.session.LauncherSessionListeners +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$55/0x0000007001019cc8 +org.junit.platform.launcher.core.ServiceLoaderRegistry +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$56/0x0000007001020000 +java.lang.invoke.LambdaForm$DMH/0x0000007001024000 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$57/0x0000007001020228 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$58/0x0000007001020460 +org.junit.platform.launcher.LauncherSessionListener$1 +org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry +org.junit.platform.engine.TestEngine +org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry$$Lambda$59/0x0000007001020cd8 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$60/0x0000007001020f00 +org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine +org.junit.jupiter.engine.JupiterTestEngine +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService +org.junit.jupiter.engine.config.JupiterConfiguration +org.junit.platform.engine.TestDescriptor +org.junit.platform.engine.support.hierarchical.EngineExecutionContext +org.junit.platform.launcher.core.LauncherFactory$$Lambda$61/0x0000007001021e48 +org.junit.platform.launcher.core.DefaultLauncher +org.junit.platform.launcher.TestPlan +org.junit.platform.launcher.core.InternalTestPlan +org.junit.platform.launcher.core.LauncherListenerRegistry +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$62/0x0000007001022a18 +org.junit.platform.launcher.core.CompositeTestExecutionListener +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$63/0x0000007001022ee8 +org.junit.platform.launcher.core.EngineExecutionOrchestrator +org.junit.platform.engine.EngineExecutionListener +org.junit.platform.launcher.TestPlan$Visitor +org.junit.platform.launcher.core.DefaultLauncher$$Lambda$64/0x0000007001023750 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator +org.junit.platform.launcher.core.EngineDiscoveryResultValidator +org.junit.platform.launcher.core.EngineIdValidator +org.junit.platform.launcher.core.LauncherFactory$$Lambda$65/0x0000007001026000 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$66/0x0000007001026238 +java.util.Spliterators$IteratorSpliterator +org.junit.platform.commons.util.ClassNamePatternFilterUtils +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$67/0x0000007001026678 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$68/0x00000070010268b8 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$69/0x0000007001026b08 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$70/0x0000007001026d48 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$71/0x0000007001026f90 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$72/0x00000070010271e0 +java.util.stream.ForEachOps +java.util.stream.ForEachOps$ForEachOp +java.util.stream.ForEachOps$ForEachOp$OfRef +org.junitpioneer.jupiter.issue.IssueExtensionExecutionListener +org.junitpioneer.jupiter.IssueProcessor +org.junit.platform.launcher.listeners.UniqueIdTrackingListener +org.junit.platform.launcher.core.LauncherFactory$$Lambda$73/0x0000007001027b40 +org.junit.platform.launcher.core.DelegatingLauncher +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$Phase +org.junit.platform.engine.UniqueId +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$74/0x0000007001025670 +org.junit.platform.launcher.core.EngineFilterer +org.junit.platform.engine.FilterResult +org.junit.platform.launcher.core.EngineFilterer$$Lambda$75/0x0000007001025cf8 +java.lang.invoke.LambdaForm$DMH/0x0000007001024400 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$76/0x0000007001024800 +java.util.stream.MatchOps$MatchKind +java.util.stream.MatchOps +java.util.stream.MatchOps$MatchOp +java.util.stream.MatchOps$BooleanTerminalSink +java.util.stream.MatchOps$$Lambda$77/0x000000700105f7f0 +java.util.stream.MatchOps$1MatchSink +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$78/0x0000007001024a50 +org.junit.platform.engine.UniqueIdFormat +java.io.UnsupportedEncodingException +java.util.Formatter +java.util.regex.Pattern$$Lambda$20/0x80000002a +java.util.regex.Pattern$$Lambda$22/0x800000032 +java.util.Locale$Category +java.util.Formatter$Conversion +java.util.Formatter$FormatString +java.util.Formatter$FormatSpecifier +java.util.Formatter$Flags +java.util.Formatter$FixedString +java.util.Formattable +java.util.regex.Pattern$$Lambda$81/0x0000007001060378 +java.lang.invoke.LambdaForm$DMH/0x0000007001028000 +org.junit.platform.engine.UniqueIdFormat$$Lambda$82/0x000000700102c000 +java.net.URLEncoder +java.util.BitSet +java.io.CharArrayWriter +org.junit.platform.engine.UniqueIdFormat$$Lambda$83/0x000000700102c240 +org.junit.platform.engine.UniqueIdFormat$$Lambda$84/0x000000700102c480 +org.junit.platform.engine.UniqueIdFormat$$Lambda$85/0x000000700102c6c0 +org.junit.platform.engine.UniqueIdFormat$$Lambda$86/0x000000700102c900 +org.junit.platform.engine.UniqueIdFormat$$Lambda$87/0x000000700102cb40 +org.junit.platform.engine.UniqueId$Segment +org.junit.jupiter.engine.config.CachingJupiterConfiguration +org.junit.jupiter.engine.config.DefaultJupiterConfiguration +org.junit.jupiter.api.parallel.ExecutionMode +org.junit.jupiter.api.TestInstance$Lifecycle +org.junit.jupiter.api.io.CleanupMode +org.junit.jupiter.engine.config.EnumConfigurationParameterConverter +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter +org.junit.jupiter.api.DisplayNameGenerator +org.junit.jupiter.api.MethodOrderer +org.junit.jupiter.api.ClassOrderer +org.junit.jupiter.api.io.TempDirFactory +org.junit.platform.engine.support.hierarchical.Node +org.junit.platform.engine.support.descriptor.AbstractTestDescriptor +org.junit.platform.engine.support.descriptor.EngineDescriptor +org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor +org.junit.jupiter.api.extension.ExecutableInvoker +org.junit.jupiter.api.extension.ExtensionContext +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver +org.junit.platform.engine.support.discovery.SelectorResolver +org.junit.platform.engine.TestDescriptor$Visitor +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$InitializationContext +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$Builder +org.junit.jupiter.engine.discovery.predicates.IsTestClassWithTests +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod +org.junit.jupiter.engine.discovery.predicates.IsTestMethod +org.junit.jupiter.api.Test +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod +org.junit.jupiter.api.TestFactory +org.junit.jupiter.engine.discovery.predicates.IsTestTemplateMethod +org.junit.jupiter.api.TestTemplate +java.lang.invoke.LambdaForm$DMH/0x0000007001028400 +java.util.function.Predicate$$Lambda$88/0x0000007001060df0 +org.junit.jupiter.engine.discovery.predicates.IsPotentialTestContainer +org.junit.jupiter.engine.discovery.predicates.IsNestedTestClass +org.junit.jupiter.engine.discovery.predicates.IsInnerClass +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$Builder$$Lambda$89/0x0000007001029000 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$90/0x0000007001029248 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$91/0x0000007001029488 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$92/0x00000070010296c8 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$93/0x0000007001029908 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$94/0x0000007001029b48 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$DefaultInitializationContext +org.junit.platform.engine.DiscoveryFilter +org.junit.platform.engine.discovery.ClassNameFilter +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$95/0x0000007001028c00 +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$96/0x0000007001030000 +org.junit.platform.engine.discovery.PackageNameFilter +org.junit.platform.engine.CompositeFilter +org.junit.platform.engine.CompositeFilter$1 +org.junit.platform.engine.CompositeFilter$1$$Lambda$97/0x00000070010308c0 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$$Lambda$98/0x0000007001030b10 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$$Lambda$99/0x0000007001030d58 +java.util.stream.Collectors$$Lambda$100/0x0000007001061048 +java.util.stream.Collectors$$Lambda$101/0x0000007001061278 +org.junit.platform.engine.support.discovery.ClassContainerSelectorResolver +org.junit.jupiter.engine.discovery.ClassSelectorResolver +org.junit.jupiter.engine.discovery.MethodSelectorResolver +org.junit.jupiter.engine.discovery.MethodFinder +java.util.regex.Pattern$$Lambda$102/0x00000070010614c0 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor +org.junit.jupiter.engine.discovery.ClassOrderingVisitor +org.junit.jupiter.api.ClassOrdererContext +org.junit.jupiter.engine.discovery.MethodOrderingVisitor +org.junit.jupiter.api.MethodOrdererContext +java.lang.invoke.LambdaForm$DMH/0x0000007001034000 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$103/0x0000007001032558 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution +org.junit.platform.engine.support.discovery.SelectorResolver$Context +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$DefaultContext +org.junit.platform.engine.support.discovery.SelectorResolver$Match +org.junit.platform.engine.support.discovery.SelectorResolver$Match$$Lambda$104/0x0000007001033008 +org.junit.platform.engine.support.discovery.SelectorResolver$Match$Type +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$105/0x0000007001033668 +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$106/0x00000070010338c0 +java.util.ArrayDeque$$Lambda$107/0x0000007001061720 +org.junit.platform.engine.discovery.UniqueIdSelector +org.junit.platform.engine.support.discovery.SelectorResolver$Resolution +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$108/0x0000007001036000 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$109/0x0000007001036248 +org.junit.platform.engine.discovery.ClasspathResourceSelector +org.junit.platform.engine.discovery.ClasspathRootSelector +org.junit.platform.commons.util.ReflectionUtils +java.util.regex.Pattern$$Lambda$21/0x800000031 +org.junit.platform.commons.util.ClasspathScanner +java.nio.file.FileVisitor +org.junit.platform.commons.util.ReflectionUtils$$Lambda$111/0x0000007001036cf8 +org.junit.platform.commons.function.Try +java.lang.invoke.LambdaForm$DMH/0x0000007001034400 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$112/0x0000007001037168 +java.lang.invoke.LambdaForm$DMH/0x0000007001034800 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$113/0x0000007001037398 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$114/0x00000070010375d0 +org.junit.platform.commons.function.Try$Failure +org.junit.platform.commons.function.Try$Success +org.junit.platform.commons.function.Try$$Lambda$115/0x0000007001037ca8 +java.util.regex.MatchResult +java.util.regex.Matcher +java.util.regex.IntHashSet +java.util.regex.Pattern$1 +org.junit.platform.engine.discovery.ClassSelector$$Lambda$116/0x0000007001035000 +org.junit.platform.commons.util.ReflectionUtils$HierarchyTraversalMode +java.lang.PublicMethods +software.amazon.lambda.powertools.metrics.provider.MetricsProvider +java.util.LinkedHashMap$LinkedValues +java.util.LinkedHashMap$LinkedValueIterator +org.junit.platform.commons.util.ReflectionUtils$$Lambda$117/0x0000007001035888 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$118/0x0000007001035ad8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$119/0x0000007001035cf8 +java.util.stream.SortedOps +java.util.stream.ReferencePipeline$StatefulOp +java.util.stream.SortedOps$OfRef +org.junit.platform.commons.util.ReflectionUtils$$Lambda$120/0x0000007001034c00 +java.util.stream.SortedOps$AbstractRefSortingSink +java.util.stream.SortedOps$SizedRefSortingSink +java.util.Arrays$LegacyMergeSort +java.util.TimSort +org.junit.platform.commons.util.AnnotationUtils +java.lang.annotation.Inherited +sun.reflect.generics.parser.SignatureParser +sun.reflect.generics.tree.Tree +sun.reflect.generics.tree.TypeTree +sun.reflect.generics.tree.TypeArgument +sun.reflect.generics.tree.ReturnType +sun.reflect.generics.tree.TypeSignature +sun.reflect.generics.tree.BaseType +sun.reflect.generics.tree.FieldTypeSignature +sun.reflect.generics.tree.SimpleClassTypeSignature +sun.reflect.generics.tree.ClassTypeSignature +sun.reflect.generics.scope.Scope +sun.reflect.generics.scope.AbstractScope +sun.reflect.generics.scope.ClassScope +sun.reflect.generics.factory.GenericsFactory +sun.reflect.generics.factory.CoreReflectionFactory +sun.reflect.generics.visitor.TypeTreeVisitor +sun.reflect.generics.visitor.Reifier +java.lang.annotation.Target +java.lang.reflect.GenericArrayType +sun.reflect.annotation.AnnotationType +sun.reflect.annotation.AnnotationType$1 +java.lang.annotation.ElementType +java.lang.annotation.Retention +java.lang.annotation.Documented +java.lang.annotation.RetentionPolicy +sun.reflect.annotation.ExceptionProxy +sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy +sun.reflect.annotation.AnnotationParser$1 +java.lang.reflect.InvocationHandler +sun.reflect.annotation.AnnotationInvocationHandler +java.lang.reflect.Proxy +java.lang.ClassValue +java.lang.reflect.Proxy$1 +java.lang.ClassValue$Entry +java.lang.ClassValue$Identity +java.lang.ClassValue$Version +jdk.internal.loader.AbstractClassLoaderValue$Sub +java.lang.reflect.Proxy$$Lambda$121/0x000000700106b350 +java.lang.reflect.Proxy$ProxyBuilder +java.lang.reflect.Proxy$ProxyBuilder$$Lambda$122/0x000000700106b790 +java.lang.module.ModuleDescriptor$Modifier +java.lang.module.ModuleDescriptor$Builder +jdk.internal.module.Checks +java.lang.module.ModuleDescriptor$Builder$$Lambda$1/0x800000002 +java.lang.reflect.Proxy$$Lambda$124/0x000000700106b9c0 +java.lang.reflect.ProxyGenerator +java.lang.reflect.ProxyGenerator$ProxyMethod +java.util.StringJoiner +java.lang.reflect.ProxyGenerator$$Lambda$125/0x000000700106c110 +java.lang.reflect.ProxyGenerator$$Lambda$126/0x000000700106c350 +java.lang.reflect.ProxyGenerator$PrimitiveTypeInfo +jdk.internal.org.objectweb.asm.Edge +jdk.proxy1.$Proxy0 +java.lang.reflect.Proxy$ProxyBuilder$1 +sun.reflect.annotation.AnnotationParser$$Lambda$127/0x000000700106ce40 +java.lang.invoke.LambdaForm$DMH/0x000000700103c000 +jdk.proxy1.$Proxy1 +jdk.proxy1.$Proxy2 +org.apiguardian.api.API +org.apiguardian.api.API$Status +jdk.proxy2.$Proxy3 +java.lang.reflect.UndeclaredThrowableException +org.junit.platform.commons.annotation.Testable +jdk.proxy1.$Proxy4 +jdk.proxy2.$Proxy5 +java.lang.Class$AnnotationData +org.junit.jupiter.api.AfterEach +jdk.proxy2.$Proxy6 +java.util.LinkedHashMap$LinkedEntrySet +java.util.LinkedHashMap$LinkedEntryIterator +jdk.internal.misc.ScopedMemoryAccess$Scope +jdk.proxy2.$Proxy7 +java.lang.invoke.MethodHandle$1 +java.lang.invoke.LambdaForm$DMH/0x000000700103c400 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$128/0x00000070010395a0 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor +org.junit.jupiter.engine.descriptor.ClassTestDescriptor +org.junit.jupiter.engine.extension.ExtensionRegistry +org.junit.platform.engine.TestSource +org.junit.jupiter.engine.extension.ExtensionRegistrar +org.junit.jupiter.api.extension.TestInstanceFactoryContext +org.junit.jupiter.api.extension.ExtensionConfigurationException +org.junit.jupiter.api.extension.TestInstantiationException +org.junit.jupiter.engine.execution.ConditionEvaluator +org.junit.jupiter.engine.execution.ConditionEvaluationException +org.junit.jupiter.api.extension.ConditionEvaluationResult +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker +org.junit.jupiter.api.extension.ReflectiveInvocationContext +org.junit.jupiter.api.extension.InvocationInterceptor$Invocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain +org.junit.jupiter.engine.descriptor.DisplayNameUtils +org.junit.jupiter.api.DisplayNameGenerator$Standard +org.junit.jupiter.api.DisplayNameGenerator$Simple +org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores +org.junit.jupiter.api.DisplayNameGenerator$IndicativeSentences +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$129/0x000000700103ed38 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$130/0x000000700103ef80 +org.junit.platform.engine.support.descriptor.ClassSource +org.junit.jupiter.api.DisplayName +org.junit.jupiter.api.DisplayNameGeneration +java.lang.invoke.LambdaForm$DMH/0x000000700103c800 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$131/0x000000700103f7c8 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$132/0x000000700103fa08 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$133/0x000000700103fc48 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$134/0x000000700103d000 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$135/0x000000700103d248 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$136/0x000000700103d488 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$137/0x000000700103d6d8 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$138/0x000000700103d920 +org.junit.jupiter.engine.config.DefaultJupiterConfiguration$$Lambda$139/0x000000700103db40 +org.junit.jupiter.api.Tag +java.lang.annotation.Repeatable +org.junit.jupiter.api.Tags +jdk.proxy1.$Proxy8 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$140/0x0000007001080000 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$141/0x0000007001080228 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$142/0x0000007001080468 +org.junit.platform.engine.TestTag +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$143/0x00000070010808d0 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$144/0x0000007001080b10 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$145/0x0000007001080d30 +java.lang.invoke.LambdaForm$DMH/0x0000007001084000 +java.util.function.Function$$Lambda$146/0x000000700106e488 +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils +org.junit.jupiter.api.TestInstance +jdk.internal.reflect.ClassFileConstants +jdk.internal.reflect.AccessorGenerator +jdk.internal.reflect.MethodAccessorGenerator +jdk.internal.reflect.ByteVectorFactory +jdk.internal.reflect.ByteVector +jdk.internal.reflect.ByteVectorImpl +jdk.internal.reflect.ClassFileAssembler +jdk.internal.reflect.UTF8 +jdk.internal.reflect.Label +jdk.internal.reflect.Label$PatchInfo +jdk.internal.reflect.MethodAccessorGenerator$1 +jdk.internal.reflect.ClassDefiner +jdk.internal.reflect.ClassDefiner$1 +jdk.internal.reflect.GeneratedConstructorAccessor1 +java.lang.Class$1 +jdk.internal.reflect.BootstrapConstructorAccessorImpl +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils$$Lambda$147/0x0000007001081178 +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils$$Lambda$148/0x00000070010813b8 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$149/0x00000070010815e0 +java.lang.invoke.LambdaForm$DMH/0x0000007001084800 +org.junit.jupiter.engine.config.EnumConfigurationParameterConverter$$Lambda$150/0x0000007001081a20 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$151/0x0000007001081c68 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$152/0x0000007001081eb0 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$153/0x00000070010820d8 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$154/0x0000007001082320 +org.junit.platform.engine.SelectorResolutionResult +org.junit.platform.engine.SelectorResolutionResult$Status +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$155/0x0000007001082bb8 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$156/0x0000007001082e08 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$157/0x0000007001083040 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$158/0x0000007001083290 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$159/0x00000070010834e8 +java.util.stream.DistinctOps +java.util.stream.DistinctOps$1 +org.junit.platform.commons.util.CollectionUtils +org.junit.platform.commons.util.CollectionUtils$$Lambda$160/0x0000007001083948 +java.util.stream.DistinctOps$1$2 +org.junit.jupiter.api.BeforeEach +jdk.proxy2.$Proxy9 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$161/0x0000007001086000 +org.junit.platform.commons.support.ReflectionSupport +org.junit.platform.engine.discovery.NestedClassSelector +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$162/0x0000007001086688 +java.util.stream.Streams$ConcatSpliterator +java.util.stream.Streams$ConcatSpliterator$OfRef +java.util.stream.AbstractPipeline$$Lambda$163/0x0000007001070f38 +java.util.stream.StreamSpliterators$AbstractWrappingSpliterator +java.util.stream.StreamSpliterators$WrappingSpliterator +java.util.stream.Streams$2 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$164/0x00000070010868d0 +java.lang.invoke.LambdaForm$DMH/0x0000007001084c00 +java.util.stream.StreamSpliterators +java.util.stream.StreamSpliterators$WrappingSpliterator$$Lambda$165/0x0000007001071ac8 +org.junit.platform.engine.discovery.MethodSelector +org.junit.platform.engine.discovery.MethodSelector$$Lambda$166/0x0000007001086d40 +org.junit.platform.commons.util.ClassUtils +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$167/0x0000007001087188 +org.junit.platform.engine.discovery.IterationSelector +org.junit.platform.engine.discovery.DirectorySelector +org.junit.platform.engine.discovery.FileSelector +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$168/0x0000007001087a38 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$169/0x0000007001087c60 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$1 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$2 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$3 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$170/0x0000007001085b38 +java.lang.invoke.LambdaForm$DMH/0x0000007001088000 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$171/0x0000007001085d80 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$172/0x000000700108c000 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$173/0x000000700108c240 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$174/0x000000700108c488 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$175/0x000000700108c6b0 +org.junit.platform.commons.util.ClassUtils$$Lambda$176/0x000000700108c8f8 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$VoidMethodInterceptorCall +org.junit.jupiter.api.extension.Extension +org.junit.jupiter.api.extension.InvocationInterceptor +java.lang.invoke.LambdaForm$DMH/0x0000007001088400 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$177/0x000000700108da08 +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$$Lambda$178/0x000000700108de28 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$179/0x000000700108e050 +org.junit.jupiter.api.DisplayNameGenerator$$Lambda$180/0x000000700108e298 +org.junit.platform.engine.support.descriptor.MethodSource +org.junit.platform.commons.util.AnnotationUtils$$Lambda$181/0x000000700108e700 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$182/0x000000700108e940 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$183/0x000000700108eb90 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$184/0x000000700108edd0 +java.util.HashMap$KeySpliterator +org.junit.jupiter.engine.descriptor.Filterable +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$185/0x000000700108f1f8 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$186/0x000000700108f430 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$187/0x000000700108f678 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$188/0x000000700108f8b0 +org.junit.jupiter.api.ClassDescriptor +org.junit.jupiter.engine.discovery.AbstractAnnotatedDescriptorWrapper +org.junit.jupiter.engine.discovery.DefaultClassDescriptor +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$189/0x000000700108a258 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$MessageGenerator +java.lang.invoke.LambdaForm$DMH/0x0000007001088800 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$190/0x000000700108a698 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$191/0x000000700108a8c0 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$192/0x000000700108acf8 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$193/0x000000700108af50 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$194/0x000000700108b198 +java.lang.invoke.LambdaForm$DMH/0x0000007001088c00 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$195/0x000000700108b3b8 +org.junit.jupiter.api.TestClassOrder +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$196/0x000000700108b7f0 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$197/0x000000700108ba30 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$198/0x000000700108bc70 +org.junit.platform.engine.TestDescriptor$$Lambda$199/0x0000007001089000 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$200/0x0000007001089238 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$201/0x0000007001089470 +org.junit.jupiter.api.TestMethodOrder +org.junit.platform.commons.support.AnnotationSupport +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$202/0x0000007001089ab8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$203/0x0000007001089cf8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$204/0x0000007001090000 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$205/0x0000007001090240 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$206/0x0000007001090468 +java.lang.invoke.LambdaForm$DMH/0x0000007001094000 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$207/0x00000070010906b0 +org.junit.platform.engine.TestDescriptor$Type +org.junit.platform.engine.TestDescriptor$$Lambda$208/0x0000007001090d28 +org.junit.platform.launcher.core.EngineDiscoveryResultValidator$$Lambda$209/0x0000007001090f78 +org.junit.platform.launcher.core.EngineDiscoveryResultValidator$$Lambda$210/0x00000070010911a0 +org.junit.platform.launcher.EngineDiscoveryResult +org.junit.platform.launcher.EngineDiscoveryResult$Status +org.junit.platform.commons.util.ExceptionUtils +java.io.StringWriter +org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener$$Lambda$211/0x0000007001091c30 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$212/0x0000007001091e60 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$213/0x00000070010920b0 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$214/0x0000007001092308 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$215/0x0000007001092548 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$216/0x0000007001092768 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$217/0x00000070010929b8 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$218/0x0000007001092be0 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$219/0x0000007001092e18 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$220/0x0000007001093048 +org.junit.platform.launcher.core.LauncherDiscoveryResult +org.junit.platform.launcher.TestPlan$$Lambda$221/0x00000070010934a0 +org.junit.platform.launcher.TestPlan$$Lambda$222/0x00000070010936f0 +org.junit.platform.launcher.TestPlan$$Lambda$223/0x0000007001093918 +org.junit.platform.launcher.TestIdentifier +org.junit.platform.launcher.TestIdentifier$SerializedForm +java.io.ObjectStreamClass +java.io.ObjectStreamClass$Caches +java.io.ClassCache +java.io.ObjectStreamClass$Caches$1 +java.io.ClassCache$1 +java.io.ObjectStreamClass$Caches$2 +java.lang.ClassValue$ClassValueMap +java.io.Externalizable +java.io.ObjectStreamClass$2 +jdk.internal.reflect.UnsafeFieldAccessorFactory +jdk.internal.reflect.UnsafeQualifiedStaticFieldAccessorImpl +jdk.internal.reflect.UnsafeQualifiedStaticLongFieldAccessorImpl +java.util.ComparableTimSort +jdk.internal.reflect.SerializationConstructorAccessorImpl +jdk.internal.reflect.GeneratedSerializationConstructorAccessor1 +java.io.ObjectOutput +java.io.ObjectStreamConstants +java.io.ObjectOutputStream +java.io.ObjectInput +java.io.ObjectInputStream +java.lang.Class$$Lambda$224/0x0000007001075608 +java.util.stream.Collectors$$Lambda$32/0x800000047 +java.util.stream.Collectors$$Lambda$24/0x80000003f +java.util.stream.Collectors$$Lambda$27/0x800000042 +java.util.stream.Collectors$$Lambda$29/0x800000044 +java.lang.CloneNotSupportedException +java.io.ClassCache$CacheRef +java.io.ObjectStreamClass$FieldReflectorKey +java.io.ObjectStreamClass$FieldReflector +org.junit.platform.launcher.TestIdentifier$$Lambda$229/0x0000007001096000 +org.junit.platform.launcher.TestPlan$$Lambda$230/0x0000007001096240 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$231/0x0000007001096480 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$232/0x00000070010966b8 +software.amazon.lambda.powertools.metrics.ConfigurationPrecedenceTest +com.amazonaws.services.lambda.runtime.Context +com.amazonaws.services.lambda.runtime.RequestHandler +org.junitpioneer.jupiter.SetEnvironmentVariable$SetEnvironmentVariables +org.junitpioneer.jupiter.SetEnvironmentVariable +org.junitpioneer.jupiter.WritesEnvironmentVariable +org.junit.jupiter.api.extension.ExtendWith +sun.reflect.annotation.AnnotationParser$$Lambda$233/0x0000007001076168 +jdk.proxy2.$Proxy10 +jdk.proxy2.$Proxy11 +software.amazon.lambda.powertools.metrics.ConfigurationPrecedenceTest$HandlerWithDefaultMetricsAnnotation +software.amazon.lambda.powertools.metrics.ConfigurationPrecedenceTest$HandlerWithMetricsAnnotation +org.junit.jupiter.api.parallel.ResourceLock +jdk.proxy2.$Proxy12 +sun.reflect.annotation.AnnotationParser$$Lambda$234/0x0000007001076390 +org.junit.jupiter.api.extension.BeforeEachCallback +org.junit.jupiter.api.extension.AfterEachCallback +org.junit.jupiter.api.extension.BeforeAllCallback +org.junit.jupiter.api.extension.AfterAllCallback +org.junitpioneer.jupiter.AbstractEntryBasedExtension +org.junitpioneer.jupiter.EnvironmentVariableExtension +jdk.proxy2.$Proxy13 +org.junit.jupiter.api.parallel.ResourceAccessMode +jdk.proxy2.$Proxy14 +jdk.internal.reflect.GeneratedConstructorAccessor2 +org.junit.jupiter.api.parallel.ResourceLocks +jdk.internal.reflect.GeneratedConstructorAccessor3 +org.junit.jupiter.api.extension.Extensions +sun.reflect.annotation.AnnotationInvocationHandler$1 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$235/0x0000007001098aa0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$236/0x0000007001098cc8 +software.amazon.lambda.powertools.metrics.MetricsFactoryTest +jdk.internal.reflect.GeneratedConstructorAccessor4 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest +java.time.temporal.TemporalAccessor +java.time.temporal.Temporal +java.time.temporal.TemporalAdjuster +java.time.Instant +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLoggerTest +software.amazon.lambda.powertools.metrics.model.MetricUnit +software.amazon.cloudwatchlogs.emf.model.Unit +org.junit.jupiter.params.ParameterizedTest +jdk.proxy2.$Proxy15 +org.junit.jupiter.params.provider.MethodSource +org.junit.jupiter.params.provider.ArgumentsSource +jdk.proxy2.$Proxy16 +jdk.proxy2.$Proxy17 +org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider +org.junit.jupiter.params.ParameterizedTestExtension +jdk.internal.reflect.GeneratedConstructorAccessor5 +jdk.internal.reflect.GeneratedConstructorAccessor6 +org.junit.jupiter.params.provider.ArgumentsProvider +org.junit.jupiter.params.support.AnnotationConsumer +org.junit.jupiter.params.provider.AnnotationBasedArgumentsProvider +org.junit.jupiter.params.provider.MethodArgumentsProvider +jdk.proxy2.$Proxy18 +org.junit.jupiter.params.provider.ArgumentsSources +org.junit.platform.commons.util.ClassUtils$$Lambda$237/0x000000700109ba00 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor +java.util.function.BiPredicate +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter$WithoutIndexFiltering +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter$Mode +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest +jdk.internal.reflect.GeneratedConstructorAccessor7 +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithAnnotationOnWrongMethod +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithColdStartMetricsAnnotation +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithCustomFunctionName +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithDefaultMetricsAnnotation +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithMetricsAnnotation +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithServiceNameAndColdStart +software.amazon.lambda.powertools.metrics.provider.EmfMetricsProviderTest +software.amazon.lambda.powertools.metrics.model.DimensionSetTest +software.amazon.lambda.powertools.metrics.model.DimensionSet +software.amazon.lambda.powertools.metrics.Metrics +software.amazon.lambda.powertools.metrics.testutils.TestMetrics +software.amazon.lambda.powertools.metrics.model.MetricResolution +org.junit.platform.commons.util.ReflectionUtils$$Lambda$238/0x00000070010a0248 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$239/0x00000070010a0488 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$240/0x00000070010a06c8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$241/0x00000070010a0920 +java.lang.reflect.Executable$$Lambda$242/0x00000070010767f0 +software.amazon.lambda.powertools.metrics.testutils.TestContext +com.amazonaws.services.lambda.runtime.LambdaLogger +com.amazonaws.services.lambda.runtime.CognitoIdentity +com.amazonaws.services.lambda.runtime.ClientContext +software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider +org.apache.maven.surefire.api.util.TestsToRun +org.apache.maven.surefire.api.util.DefaultRunOrderCalculator +java.util.random.RandomGenerator +java.util.Random +org.apache.maven.surefire.api.util.CloseableIterator +org.apache.maven.surefire.api.util.TestsToRun$ClassesIterator +java.util.NoSuchElementException +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$243/0x00000070010a1ff8 +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$244/0x00000070010a2230 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$245/0x00000070010a2468 +org.junit.platform.launcher.core.CompositeTestExecutionListener$EagerTestExecutionListener +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$246/0x00000070010a28a0 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$247/0x00000070010a2af8 +org.junit.platform.engine.reporting.ReportEntry +java.lang.invoke.LambdaForm$DMH/0x00000070010a4000 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$248/0x00000070010a2f50 +org.junit.platform.launcher.core.StreamInterceptingTestExecutionListener +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$249/0x00000070010a3450 +org.junit.platform.engine.EngineExecutionListener$1 +org.junit.platform.launcher.core.IterationOrder +org.junit.platform.launcher.core.IterationOrder$1 +org.junit.platform.launcher.core.IterationOrder$2 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$250/0x00000070010a61f8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$251/0x00000070010a6430 +java.lang.invoke.LambdaForm$DMH/0x00000070010a4400 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$252/0x00000070010a6658 +org.junit.platform.launcher.core.CompositeEngineExecutionListener +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$253/0x00000070010a6b00 +org.junit.platform.launcher.core.ExecutionListenerAdapter +org.junit.platform.launcher.core.DelegatingEngineExecutionListener +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener +org.junit.platform.launcher.core.EngineDiscoveryErrorDescriptor +org.junit.platform.launcher.core.OutcomeDelayingEngineExecutionListener +org.junit.platform.engine.ExecutionRequest +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$254/0x00000070010a7c90 +org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext$State +org.junit.platform.engine.support.hierarchical.ThrowableCollector$Factory +org.junit.platform.engine.support.hierarchical.ThrowableCollector +org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector +org.junit.jupiter.engine.JupiterTestEngine$$Lambda$255/0x00000070010a4800 +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService$TestTask +org.junit.platform.engine.support.hierarchical.NodeTreeWalker +org.junit.platform.engine.support.hierarchical.LockManager +org.junit.platform.engine.support.hierarchical.ResourceLock +java.util.concurrent.locks.ReadWriteLock +org.junit.platform.engine.support.hierarchical.ExclusiveResource +org.junit.platform.engine.support.hierarchical.LockManager$$Lambda$256/0x00000070010a8858 +org.junit.platform.engine.support.hierarchical.LockManager$$Lambda$257/0x00000070010a8a98 +java.lang.invoke.LambdaForm$DMH/0x00000070010ac000 +java.lang.invoke.LambdaForm$MH/0x00000070010ac400 +java.util.Comparator$$Lambda$258/0x0000007001076e90 +java.util.Comparators$NaturalOrderComparator +java.lang.invoke.LambdaForm$DMH/0x00000070010ac800 +java.util.Comparator$$Lambda$259/0x00000070010777f0 +java.lang.invoke.LambdaForm$DMH/0x00000070010acc00 +java.util.Comparator$$Lambda$260/0x0000007001077a90 +org.junit.platform.engine.support.hierarchical.ExclusiveResource$LockMode +org.junit.platform.engine.support.hierarchical.LockManager$$Lambda$261/0x00000070010a8f20 +org.junit.platform.engine.support.hierarchical.SingleLock +org.junit.platform.engine.support.hierarchical.LockManager$$Lambda$262/0x00000070010a95c8 +java.util.concurrent.locks.ReentrantReadWriteLock +java.util.concurrent.locks.ReentrantReadWriteLock$Sync +java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync +java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter +java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock +java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock +org.junit.platform.engine.support.hierarchical.NodeUtils +org.junit.platform.engine.support.hierarchical.NodeUtils$1 +org.junit.platform.engine.support.hierarchical.NodeExecutionAdvisor +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$263/0x00000070010a9f08 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$264/0x00000070010aa140 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$265/0x00000070010aa380 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$1 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$266/0x00000070010aa9b8 +org.junit.platform.engine.support.hierarchical.Node$ExecutionMode +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$267/0x00000070010ab048 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$268/0x00000070010ab280 +org.junit.platform.commons.util.CollectionUtils$$Lambda$269/0x00000070010ab4b8 +org.junit.platform.engine.support.hierarchical.NodeTestTaskContext +org.junit.platform.engine.support.hierarchical.NodeTestTask +org.junit.platform.engine.support.hierarchical.Node$DynamicTestExecutor +java.lang.invoke.LambdaForm$DMH/0x00000070010ad000 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$270/0x00000070010abd70 +org.opentest4j.IncompleteExecutionException +org.opentest4j.TestAbortedException +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector$$Lambda$271/0x00000070010ae4c0 +org.junit.platform.commons.util.UnrecoverableExceptions +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector$$Lambda$272/0x00000070010ae920 +org.junit.platform.engine.support.hierarchical.ThrowableCollector$Executable +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$273/0x00000070010aed40 +org.junit.jupiter.engine.extension.MutableExtensionRegistry +org.junit.jupiter.api.extension.ExecutionCondition +org.junit.jupiter.engine.extension.DisabledCondition +org.junit.jupiter.engine.extension.TimeoutExtension +org.junit.jupiter.api.Timeout +org.junit.jupiter.api.extension.ExtensionContext$Namespace +org.junit.jupiter.engine.extension.RepeatedTestExtension +org.junit.jupiter.api.extension.TestTemplateInvocationContext +org.junit.jupiter.api.extension.ParameterResolver +org.junit.jupiter.engine.extension.TestInfoParameterResolver +org.junit.jupiter.api.TestInfo +org.junit.jupiter.engine.extension.TestReporterParameterResolver +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$274/0x00000070010b0440 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$275/0x00000070010b0678 +org.junit.jupiter.engine.extension.TempDirectory +org.junit.jupiter.engine.extension.TempDirectory$Scope +org.junit.jupiter.api.extension.AnnotatedElementContext +org.junit.jupiter.api.extension.ParameterResolutionException +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$276/0x00000070010b11c8 +org.junit.jupiter.engine.execution.DefaultExecutableInvoker +org.junit.jupiter.engine.descriptor.AbstractExtensionContext +org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext +org.junit.jupiter.api.extension.ExtensionContext$Store +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CloseAction +java.lang.invoke.LambdaForm$DMH/0x00000070010b4000 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$277/0x00000070010b2210 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore +org.junit.platform.engine.support.store.NamespacedHierarchicalStoreException +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$278/0x00000070010b28b8 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$279/0x00000070010b2af8 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$280/0x00000070010b2d18 +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext$Builder +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$281/0x00000070010b3190 +org.junit.platform.engine.support.hierarchical.Node$SkipResult +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$282/0x00000070010b35d8 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$283/0x00000070010b3810 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$284/0x00000070010b3a38 +org.junit.platform.launcher.TestPlan$$Lambda$285/0x00000070010b3c70 +org.junit.platform.launcher.TestPlan$$Lambda$286/0x00000070010b6000 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$287/0x00000070010b6228 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$288/0x00000070010b6460 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$289/0x00000070010b6688 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$290/0x00000070010b68c0 +org.junit.platform.engine.UniqueIdFormat$$Lambda$291/0x00000070010b6ae8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$292/0x00000070010b6d30 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$293/0x00000070010b6f88 +org.junit.platform.engine.support.hierarchical.Node$Invocation +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$294/0x00000070010b73b0 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$295/0x00000070010b75d8 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$296/0x00000070010b7800 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$297/0x00000070010b7a48 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor +java.util.concurrent.CancellationException +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$298/0x00000070010b5000 +org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService$$Lambda$299/0x00000070010b5238 +org.junit.jupiter.engine.descriptor.ExtensionUtils +java.util.function.ToIntFunction +java.lang.invoke.LambdaForm$DMH/0x00000070010b4400 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$300/0x00000070010b5670 +java.util.Comparator$$Lambda$301/0x0000007001078e48 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$302/0x00000070010b5890 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$303/0x00000070010b5ad0 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$304/0x00000070010b5d10 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$305/0x00000070010b4800 +com.fasterxml.jackson.core.Versioned +com.fasterxml.jackson.core.TreeCodec +com.fasterxml.jackson.core.ObjectCodec +com.fasterxml.jackson.databind.ObjectMapper +org.junit.platform.commons.util.ReflectionUtils$$Lambda$306/0x00000070010b8d00 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$307/0x00000070010b8f98 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$308/0x00000070010b91b8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$309/0x00000070010b9408 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$310/0x00000070010b9660 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$311/0x00000070010b98b8 +java.util.stream.SortedOps$RefSortingSink +java.util.stream.SortedOps$RefSortingSink$$Lambda$312/0x00000070010793a8 +org.junit.jupiter.api.extension.TestInstanceFactory +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$313/0x00000070010b9cf0 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$314/0x00000070010b9f48 +org.junit.jupiter.engine.extension.ExtensionRegistry$$Lambda$315/0x00000070010ba190 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$316/0x00000070010ba3b0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$317/0x00000070010ba600 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$318/0x00000070010ba828 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$319/0x00000070010baa70 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$320/0x00000070010bacb0 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils +org.junit.jupiter.api.BeforeAll +org.junit.platform.commons.util.AnnotationUtils$$Lambda$321/0x00000070010bb2f0 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$322/0x00000070010bb548 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$323/0x00000070010bb780 +org.junit.jupiter.api.AfterAll +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$324/0x00000070010bbbb8 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$325/0x00000070010bbdf0 +org.junit.jupiter.engine.execution.BeforeEachMethodAdapter +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$326/0x00000070010bc228 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$327/0x00000070010bc470 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$328/0x00000070010bc6a8 +org.junit.jupiter.engine.execution.AfterEachMethodAdapter +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$329/0x00000070010bcad0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$330/0x00000070010bcd18 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$331/0x00000070010bcf40 +org.junit.jupiter.engine.descriptor.ClassExtensionContext +org.junit.jupiter.engine.execution.TestInstancesProvider +org.junit.jupiter.api.extension.TestInstances +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$332/0x00000070010bd948 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$333/0x00000070010bdb80 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$334/0x00000070010bddc8 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$335/0x00000070010be010 +java.util.stream.AbstractSpinedBuffer +java.util.stream.SpinedBuffer +java.util.stream.StreamSpliterators$WrappingSpliterator$$Lambda$336/0x0000007001079cd0 +java.util.function.BooleanSupplier +java.util.stream.StreamSpliterators$WrappingSpliterator$$Lambda$337/0x000000700107a180 +org.junit.jupiter.api.Disabled +org.junit.jupiter.engine.extension.DisabledCondition$$Lambda$338/0x00000070010be460 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$339/0x00000070010be6a8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$340/0x00000070010be8d0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$341/0x00000070010beb28 +org.junit.platform.launcher.TestIdentifier$$Lambda$342/0x00000070010bed80 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$343/0x00000070010befc0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$344/0x00000070010bf208 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$345/0x00000070010bf450 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$346/0x00000070010bf670 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$347/0x00000070010bf8c0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$348/0x00000070010bfb00 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$349/0x00000070010bfd58 +org.apache.maven.surefire.api.report.SimpleReportEntry +org.apache.maven.surefire.api.util.internal.ClassMethod +org.apache.maven.surefire.report.ClassMethodIndexer$$Lambda$350/0x00000070010c0510 +org.apache.maven.surefire.api.util.internal.ImmutableMap +org.apache.maven.surefire.booter.spi.EventChannelEncoder$StackTrace +java.lang.StrictMath +java.nio.StringCharBuffer +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$351/0x00000070010c0f00 +org.junit.jupiter.engine.extension.TimeoutDuration +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$352/0x00000070010c1350 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$353/0x00000070010c1590 +org.junit.jupiter.api.Timeout$ThreadMode +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$354/0x00000070010c1a10 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$355/0x00000070010c1c50 +org.junit.jupiter.engine.execution.NamespaceAwareStore +org.junit.jupiter.api.extension.ExtensionContextException +org.junit.jupiter.api.extension.ExtensionContext$Store$CloseableResource +java.lang.invoke.LambdaForm$DMH/0x00000070010c4000 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$356/0x00000070010c2598 +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$357/0x00000070010c27c0 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CompositeKey +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$358/0x00000070010c2bf8 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$StoredValue +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$359/0x00000070010c3030 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$360/0x00000070010c3280 +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$361/0x00000070010c34c8 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$362/0x00000070010c36f0 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$MemoizingSupplier +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$363/0x00000070010c3b68 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$364/0x00000070010c3d90 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$MemoizingSupplier$Failure +org.junit.jupiter.api.io.TempDir +org.junit.platform.commons.util.AnnotationUtils$$Lambda$365/0x00000070010c6608 +java.util.function.Predicate$$Lambda$366/0x000000700107aa40 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$367/0x00000070010c6860 +org.junit.jupiter.engine.descriptor.ClassExtensionContext$$Lambda$368/0x00000070010c6a98 +org.junit.jupiter.engine.descriptor.MethodExtensionContext +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$369/0x00000070010c70a8 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$370/0x00000070010c72d0 +java.lang.invoke.LambdaForm$DMH/0x00000070010c4400 +java.lang.invoke.LambdaForm$MH/0x00000070010c4800 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$371/0x00000070010c74f8 +org.junit.jupiter.engine.descriptor.DefaultTestInstanceFactoryContext +org.junit.jupiter.api.extension.TestInstancePreConstructCallback +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$372/0x00000070010c7b78 +java.lang.invoke.LambdaForm$DMH/0x00000070010c4c00 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$373/0x00000070010c7db0 +org.junit.jupiter.engine.execution.ParameterResolutionUtils +org.junit.jupiter.api.extension.ParameterContext +org.junit.jupiter.engine.execution.ConstructorInvocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptorCall +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$$Lambda$374/0x00000070010c58a0 +org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation +java.util.ArrayList$ListItr +org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation +com.fasterxml.jackson.core.JacksonException +com.fasterxml.jackson.core.JsonProcessingException +com.fasterxml.jackson.databind.DatabindException +com.fasterxml.jackson.databind.JsonMappingException +com.fasterxml.jackson.core.TokenStreamFactory +com.fasterxml.jackson.core.JsonFactory +com.fasterxml.jackson.databind.MappingJsonFactory +com.fasterxml.jackson.databind.jsontype.SubtypeResolver +com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver +com.fasterxml.jackson.databind.DatabindContext +com.fasterxml.jackson.databind.SerializerProvider +com.fasterxml.jackson.databind.ser.DefaultSerializerProvider +com.fasterxml.jackson.databind.ser.DefaultSerializerProvider$Impl +com.fasterxml.jackson.databind.deser.DeserializerFactory +com.fasterxml.jackson.databind.deser.BasicDeserializerFactory +com.fasterxml.jackson.databind.deser.BeanDeserializerFactory +com.fasterxml.jackson.databind.DeserializationContext +com.fasterxml.jackson.databind.deser.DefaultDeserializationContext +com.fasterxml.jackson.databind.deser.DefaultDeserializationContext$Impl +com.fasterxml.jackson.databind.ser.SerializerFactory +com.fasterxml.jackson.databind.ser.BasicSerializerFactory +com.fasterxml.jackson.databind.ser.BeanSerializerFactory +com.fasterxml.jackson.databind.AnnotationIntrospector +com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector +com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy$Provider +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$Provider +com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator +com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator$Base +com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator +java.text.Format +java.text.DateFormat +com.fasterxml.jackson.databind.util.StdDateFormat +com.fasterxml.jackson.core.TreeNode +com.fasterxml.jackson.databind.JsonSerializable +com.fasterxml.jackson.databind.JsonSerializable$Base +com.fasterxml.jackson.databind.JsonNode +com.fasterxml.jackson.databind.node.BaseJsonNode +com.fasterxml.jackson.databind.node.ValueNode +com.fasterxml.jackson.databind.node.NullNode +com.fasterxml.jackson.core.JsonGenerator +com.fasterxml.jackson.databind.util.TokenBuffer +com.fasterxml.jackson.databind.introspect.ClassIntrospector +com.fasterxml.jackson.databind.introspect.BasicClassIntrospector +com.fasterxml.jackson.databind.Module$SetupContext +com.fasterxml.jackson.databind.introspect.VisibilityChecker +com.fasterxml.jackson.core.JsonParser +com.fasterxml.jackson.core.base.ParserMinimalBase +com.fasterxml.jackson.databind.node.TreeTraversingParser +com.fasterxml.jackson.core.util.BufferRecycler$Gettable +com.fasterxml.jackson.core.io.SegmentedStringWriter +com.fasterxml.jackson.core.util.ByteArrayBuilder +com.fasterxml.jackson.core.type.ResolvedType +com.fasterxml.jackson.databind.JavaType +com.fasterxml.jackson.databind.type.TypeBase +com.fasterxml.jackson.databind.type.ArrayType +com.fasterxml.jackson.databind.type.CollectionLikeType +com.fasterxml.jackson.databind.type.CollectionType +com.fasterxml.jackson.databind.type.MapLikeType +com.fasterxml.jackson.databind.type.MapType +com.fasterxml.jackson.databind.exc.MismatchedInputException +com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder +com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair +com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector +com.fasterxml.jackson.databind.introspect.Annotated +com.fasterxml.jackson.databind.introspect.AnnotatedMember +com.fasterxml.jackson.databind.introspect.TypeResolutionContext +com.fasterxml.jackson.databind.introspect.AnnotatedClass +com.fasterxml.jackson.databind.introspect.AnnotatedWithParams +com.fasterxml.jackson.databind.introspect.AnnotatedMethod +com.fasterxml.jackson.databind.introspect.VirtualAnnotatedMember +com.fasterxml.jackson.databind.util.Named +com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition +com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition +com.fasterxml.jackson.databind.BeanProperty +com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase +com.fasterxml.jackson.databind.ser.PropertyWriter +com.fasterxml.jackson.databind.ser.BeanPropertyWriter +com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter +com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter +com.fasterxml.jackson.databind.annotation.JsonSerialize +com.fasterxml.jackson.annotation.JsonView +com.fasterxml.jackson.annotation.JsonFormat +com.fasterxml.jackson.annotation.JsonTypeInfo +com.fasterxml.jackson.annotation.JsonRawValue +com.fasterxml.jackson.annotation.JsonUnwrapped +com.fasterxml.jackson.annotation.JsonBackReference +com.fasterxml.jackson.annotation.JsonManagedReference +com.fasterxml.jackson.databind.annotation.JsonDeserialize +com.fasterxml.jackson.annotation.JsonMerge +com.fasterxml.jackson.databind.ext.Java7Support +java.lang.IllegalAccessError +com.fasterxml.jackson.databind.ext.Java7SupportImpl +com.fasterxml.jackson.databind.util.ClassUtil +com.fasterxml.jackson.databind.util.ClassUtil$Ctor +java.beans.Transient +java.beans.ConstructorProperties +com.fasterxml.jackson.databind.util.LookupCache +com.fasterxml.jackson.databind.util.LRUMap +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Builder +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap +java.io.ObjectStreamException +java.io.InvalidObjectException +com.fasterxml.jackson.databind.util.internal.Linked +com.fasterxml.jackson.databind.util.internal.LinkedDeque +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$1 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$2 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$3 +java.util.concurrent.atomic.AtomicLongArray +java.lang.invoke.VarHandleLongs$Array +java.util.concurrent.atomic.AtomicReferenceArray +java.lang.invoke.VarHandleReferences$Array +com.fasterxml.jackson.databind.cfg.BaseSettings +java.util.TimeZone +sun.util.calendar.ZoneInfo +sun.util.calendar.ZoneInfoFile +sun.util.calendar.ZoneInfoFile$1 +java.io.DataInputStream +sun.util.calendar.ZoneInfoFile$ZoneOffsetTransitionRule +com.fasterxml.jackson.databind.type.TypeFactory +com.fasterxml.jackson.databind.type.SimpleType +com.fasterxml.jackson.databind.type.IdentityEqualityType +com.fasterxml.jackson.databind.type.ResolvedRecursiveType +com.fasterxml.jackson.databind.type.ReferenceType +com.fasterxml.jackson.databind.type.IterationType +com.fasterxml.jackson.databind.type.PlaceholderForType +com.fasterxml.jackson.databind.type.TypeParser +com.fasterxml.jackson.databind.type.TypeBindings +java.text.SimpleDateFormat +java.util.Calendar +java.util.GregorianCalendar +java.text.ParseException +java.text.AttributedCharacterIterator$Attribute +java.text.Format$Field +java.text.DateFormat$Field +sun.util.calendar.ZoneInfoFile$Checksum +java.util.spi.LocaleServiceProvider +sun.util.spi.CalendarProvider +sun.util.locale.provider.LocaleProviderAdapter +sun.util.locale.provider.LocaleProviderAdapter$Type +sun.util.locale.provider.LocaleProviderAdapter$1 +sun.util.locale.provider.ResourceBundleBasedAdapter +sun.util.locale.provider.JRELocaleProviderAdapter +sun.util.cldr.CLDRLocaleProviderAdapter +sun.util.locale.provider.LocaleDataMetaInfo +sun.util.cldr.CLDRBaseLocaleDataMetaInfo +sun.util.locale.LanguageTag +sun.util.locale.ParseStatus +sun.util.locale.StringTokenIterator +sun.util.locale.InternalLocaleBuilder +sun.util.locale.InternalLocaleBuilder$CaseInsensitiveChar +sun.util.locale.BaseLocale$Key +sun.util.locale.LocaleObjectCache +sun.util.locale.BaseLocale$Cache +sun.util.locale.LocaleObjectCache$CacheEntry +java.util.Locale$Cache +sun.util.cldr.CLDRLocaleProviderAdapter$$Lambda$58/0x800000063 +jdk.internal.module.ModulePatcher$PatchedModuleReader +sun.net.www.protocol.jrt.Handler +sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$60/0x800000065 +sun.util.locale.provider.AvailableLanguageTags +sun.util.locale.provider.CalendarProviderImpl +java.util.Calendar$Builder +sun.util.calendar.CalendarSystem +sun.util.calendar.CalendarSystem$GregorianHolder +sun.util.calendar.AbstractCalendar +sun.util.calendar.BaseCalendar +sun.util.calendar.Gregorian +sun.util.locale.provider.CalendarDataUtility +java.util.Locale$Builder +java.util.spi.CalendarDataProvider +sun.util.locale.provider.LocaleServiceProviderPool +java.text.spi.BreakIteratorProvider +java.text.spi.CollatorProvider +java.text.spi.DateFormatProvider +java.text.spi.DateFormatSymbolsProvider +java.text.spi.DecimalFormatSymbolsProvider +java.text.spi.NumberFormatProvider +java.util.spi.CurrencyNameProvider +java.util.spi.LocaleNameProvider +java.util.spi.TimeZoneNameProvider +sun.util.locale.provider.LocaleServiceProviderPool$LocalizedObjectGetter +sun.util.locale.provider.CalendarDataUtility$CalendarWeekParameterGetter +java.util.ResourceBundle$Control +java.util.ResourceBundle +java.util.ResourceBundle$Control$CandidateListCache +java.util.ResourceBundle$SingleFormatControl +java.util.ResourceBundle$NoFallbackControl +sun.util.cldr.CLDRLocaleProviderAdapter$$Lambda$59/0x800000064 +sun.util.locale.provider.CalendarDataProviderImpl +sun.util.cldr.CLDRCalendarDataProviderImpl +sun.util.locale.provider.LocaleResources +sun.util.resources.LocaleData +sun.util.resources.LocaleData$1 +sun.util.resources.Bundles$Strategy +sun.util.resources.LocaleData$LocaleDataStrategy +sun.util.resources.Bundles +sun.util.resources.Bundles$1 +jdk.internal.access.JavaUtilResourceBundleAccess +java.util.ResourceBundle$1 +java.util.ResourceBundle$2 +sun.util.resources.Bundles$CacheKey +java.util.ListResourceBundle +sun.util.resources.cldr.CalendarData +java.util.ResourceBundle$ResourceBundleProviderHelper +java.util.ResourceBundle$ResourceBundleProviderHelper$$Lambda$12/0x800000010 +sun.util.resources.Bundles$CacheKeyReference +sun.util.resources.Bundles$BundleReference +sun.util.locale.provider.LocaleResources$ResourceReference +sun.util.calendar.CalendarDate +sun.util.calendar.BaseCalendar$Date +sun.util.calendar.Gregorian$Date +sun.util.calendar.CalendarUtils +java.text.DateFormatSymbols +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$62/0x800000067 +sun.util.locale.provider.DateFormatSymbolsProviderImpl +sun.text.resources.cldr.FormatData +sun.text.resources.cldr.FormatData_en +java.text.NumberFormat +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$64/0x800000069 +sun.util.locale.provider.NumberFormatProviderImpl +java.text.DecimalFormatSymbols +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$63/0x800000068 +sun.util.locale.provider.DecimalFormatSymbolsProviderImpl +java.lang.StringLatin1$CharsSpliterator +java.util.stream.IntStream +java.util.stream.IntPipeline +java.util.stream.IntPipeline$Head +java.util.function.IntPredicate +java.text.DecimalFormatSymbols$$Lambda$8/0x80000000c +java.util.stream.IntPipeline$StatelessOp +java.util.stream.IntPipeline$10 +java.util.function.IntConsumer +java.util.stream.Sink$OfInt +java.util.stream.FindOps$FindSink$OfInt +java.util.OptionalInt +java.util.stream.FindOps$FindSink$OfInt$$Lambda$37/0x80000004c +java.util.stream.FindOps$FindSink$OfInt$$Lambda$35/0x80000004a +java.util.stream.FindOps$FindSink$OfInt$$Lambda$36/0x80000004b +java.util.stream.FindOps$FindSink$OfInt$$Lambda$34/0x800000049 +java.util.stream.Sink$ChainedInt +java.util.stream.IntPipeline$10$1 +java.lang.StringUTF16$CharsSpliterator +java.lang.CharacterData00 +java.text.DecimalFormat +java.text.FieldPosition +java.text.DigitList +java.math.RoundingMode +java.util.Date +com.fasterxml.jackson.core.Base64Variants +com.fasterxml.jackson.core.Base64Variant +com.fasterxml.jackson.core.Base64Variant$PaddingReadBehaviour +com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$RecordNaming +com.fasterxml.jackson.databind.cfg.CacheProvider +com.fasterxml.jackson.databind.cfg.DefaultCacheProvider +com.fasterxml.jackson.core.io.DataOutputAsStream +com.fasterxml.jackson.core.SerializableString +com.fasterxml.jackson.core.TSFBuilder +com.fasterxml.jackson.core.JsonFactoryBuilder +java.io.CharArrayReader +com.fasterxml.jackson.core.base.GeneratorBase +com.fasterxml.jackson.core.json.JsonGeneratorImpl +com.fasterxml.jackson.core.json.WriterBasedJsonGenerator +com.fasterxml.jackson.core.base.ParserBase +com.fasterxml.jackson.core.json.JsonParserBase +com.fasterxml.jackson.core.json.ReaderBasedJsonParser +com.fasterxml.jackson.core.json.UTF8DataInputJsonParser +com.fasterxml.jackson.core.json.UTF8JsonGenerator +com.fasterxml.jackson.core.io.UTF8Writer +com.fasterxml.jackson.core.async.NonBlockingInputFeeder +com.fasterxml.jackson.core.async.ByteArrayFeeder +com.fasterxml.jackson.core.json.async.NonBlockingJsonParserBase +com.fasterxml.jackson.core.json.async.NonBlockingUtf8JsonParserBase +com.fasterxml.jackson.core.json.async.NonBlockingJsonParser +com.fasterxml.jackson.core.async.ByteBufferFeeder +com.fasterxml.jackson.core.json.async.NonBlockingByteBufferJsonParser +com.fasterxml.jackson.core.util.JacksonFeature +com.fasterxml.jackson.core.JsonFactory$Feature +com.fasterxml.jackson.core.JsonParser$Feature +com.fasterxml.jackson.core.JsonGenerator$Feature +com.fasterxml.jackson.core.io.SerializedString +com.fasterxml.jackson.core.io.JsonStringEncoder +com.fasterxml.jackson.core.io.CharTypes +com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer +com.fasterxml.jackson.core.exc.StreamConstraintsException +com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer$TableInfo +com.fasterxml.jackson.core.util.JsonRecyclerPools +com.fasterxml.jackson.core.util.RecyclerPool +com.fasterxml.jackson.core.util.RecyclerPool$ThreadLocalPoolBase +com.fasterxml.jackson.core.util.JsonRecyclerPools$ThreadLocalPool +com.fasterxml.jackson.core.util.RecyclerPool$WithPool +com.fasterxml.jackson.core.StreamReadConstraints +com.fasterxml.jackson.core.StreamWriteConstraints +com.fasterxml.jackson.core.ErrorReportConfiguration +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$TableInfo +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$Bucket +com.fasterxml.jackson.databind.util.RootNameLookup +com.fasterxml.jackson.databind.introspect.ClassIntrospector$MixInResolver +com.fasterxml.jackson.databind.introspect.SimpleMixInResolver +com.fasterxml.jackson.databind.cfg.MapperConfig +com.fasterxml.jackson.databind.cfg.MapperConfigBase +com.fasterxml.jackson.databind.SerializationConfig +com.fasterxml.jackson.databind.BeanDescription +com.fasterxml.jackson.databind.introspect.BasicBeanDescription +com.fasterxml.jackson.databind.DeserializationConfig +com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver +com.fasterxml.jackson.databind.introspect.AnnotationCollector +com.fasterxml.jackson.databind.util.Annotations +com.fasterxml.jackson.databind.introspect.AnnotationCollector$EmptyCollector +com.fasterxml.jackson.databind.introspect.AnnotationCollector$NoAnnotations +com.fasterxml.jackson.databind.introspect.AnnotatedClass$Creators +com.fasterxml.jackson.databind.introspect.AnnotatedConstructor +com.fasterxml.jackson.databind.introspect.AnnotatedParameter +com.fasterxml.jackson.databind.cfg.ConfigOverrides +com.fasterxml.jackson.annotation.JacksonAnnotationValue +com.fasterxml.jackson.annotation.JsonInclude$Value +com.fasterxml.jackson.annotation.JsonInclude$Include +com.fasterxml.jackson.annotation.JsonSetter$Value +com.fasterxml.jackson.annotation.Nulls +com.fasterxml.jackson.databind.introspect.VisibilityChecker$Std +com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility +com.fasterxml.jackson.databind.cfg.CoercionConfigs +com.fasterxml.jackson.databind.type.LogicalType +com.fasterxml.jackson.databind.cfg.CoercionAction +com.fasterxml.jackson.databind.cfg.CoercionConfig +com.fasterxml.jackson.databind.cfg.MutableCoercionConfig +com.fasterxml.jackson.databind.cfg.CoercionInputShape +com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator +com.fasterxml.jackson.core.PrettyPrinter +com.fasterxml.jackson.annotation.JsonFormat$Value +com.fasterxml.jackson.annotation.JsonFormat$Shape +com.fasterxml.jackson.annotation.JsonFormat$Features +com.fasterxml.jackson.databind.cfg.ConfigOverride +com.fasterxml.jackson.databind.cfg.ConfigOverride$Empty +com.fasterxml.jackson.databind.cfg.ConfigFeature +com.fasterxml.jackson.databind.MapperFeature +com.fasterxml.jackson.core.util.Instantiatable +com.fasterxml.jackson.core.util.DefaultPrettyPrinter +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$Indenter +com.fasterxml.jackson.core.util.Separators +com.fasterxml.jackson.core.util.Separators$Spacing +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$NopIndenter +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$FixedSpaceIndenter +com.fasterxml.jackson.core.util.DefaultIndenter +com.fasterxml.jackson.databind.SerializationFeature +com.fasterxml.jackson.databind.cfg.DatatypeFeatures +com.fasterxml.jackson.databind.cfg.DatatypeFeatures$DefaultHolder +com.fasterxml.jackson.databind.cfg.DatatypeFeature +com.fasterxml.jackson.databind.cfg.EnumFeature +com.fasterxml.jackson.databind.cfg.JsonNodeFeature +com.fasterxml.jackson.databind.cfg.ContextAttributes +com.fasterxml.jackson.databind.cfg.ContextAttributes$Impl +com.fasterxml.jackson.databind.DeserializationFeature +com.fasterxml.jackson.databind.node.JsonNodeCreator +com.fasterxml.jackson.databind.node.JsonNodeFactory +com.fasterxml.jackson.databind.node.NumericNode +com.fasterxml.jackson.databind.node.FloatNode +com.fasterxml.jackson.databind.node.DoubleNode +com.fasterxml.jackson.databind.node.BigIntegerNode +com.fasterxml.jackson.databind.node.ShortNode +com.fasterxml.jackson.databind.node.IntNode +com.fasterxml.jackson.databind.node.DecimalNode +com.fasterxml.jackson.databind.node.LongNode +com.fasterxml.jackson.databind.node.TextNode +com.fasterxml.jackson.databind.node.BinaryNode +com.fasterxml.jackson.databind.node.POJONode +com.fasterxml.jackson.databind.node.MissingNode +com.fasterxml.jackson.databind.node.BooleanNode +com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable +com.fasterxml.jackson.databind.JsonSerializer +com.fasterxml.jackson.databind.jsonschema.SchemaAware +com.fasterxml.jackson.databind.ser.std.StdSerializer +com.fasterxml.jackson.databind.ser.std.NullSerializer +com.fasterxml.jackson.databind.ser.impl.FailingSerializer +com.fasterxml.jackson.databind.ser.std.ToEmptyObjectSerializer +com.fasterxml.jackson.databind.ser.impl.UnknownSerializer +com.fasterxml.jackson.databind.exc.InvalidDefinitionException +com.fasterxml.jackson.databind.exc.InvalidTypeIdException +com.fasterxml.jackson.databind.ser.ContextualSerializer +com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer +com.fasterxml.jackson.databind.node.ContainerNode +com.fasterxml.jackson.databind.node.ObjectNode +com.fasterxml.jackson.databind.ser.ResolvableSerializer +com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer +com.fasterxml.jackson.databind.ser.SerializerCache +com.fasterxml.jackson.databind.deser.NullValueProvider +com.fasterxml.jackson.databind.JsonDeserializer +com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer +com.fasterxml.jackson.databind.exc.PropertyBindingException +com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException +com.fasterxml.jackson.databind.exc.InvalidFormatException +com.fasterxml.jackson.databind.exc.ValueInstantiationException +com.fasterxml.jackson.databind.deser.UnresolvedForwardReference +com.fasterxml.jackson.databind.deser.ContextualDeserializer +com.fasterxml.jackson.databind.deser.AbstractDeserializer +com.fasterxml.jackson.databind.deser.ValueInstantiator$Gettable +com.fasterxml.jackson.databind.deser.std.StdDeserializer +com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer +com.fasterxml.jackson.databind.deser.std.EnumDeserializer +com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer +com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase +com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer +com.fasterxml.jackson.databind.deser.ResolvableDeserializer +com.fasterxml.jackson.databind.deser.std.EnumMapDeserializer +com.fasterxml.jackson.databind.deser.std.MapDeserializer +com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer +com.fasterxml.jackson.databind.deser.std.StringDeserializer +com.fasterxml.jackson.databind.deser.std.MapEntryDeserializer +com.fasterxml.jackson.databind.deser.std.TokenBufferDeserializer +com.fasterxml.jackson.databind.deser.SettableBeanProperty +com.fasterxml.jackson.databind.deser.CreatorProperty +com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicReferenceDeserializer +com.fasterxml.jackson.databind.deser.std.EnumSetDeserializer +com.fasterxml.jackson.databind.deser.std.CollectionDeserializer +com.fasterxml.jackson.databind.deser.std.ArrayBlockingQueueDeserializer +com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer +com.fasterxml.jackson.databind.deser.impl.ErrorThrowingDeserializer +com.fasterxml.jackson.annotation.ObjectIdGenerator +com.fasterxml.jackson.annotation.ObjectIdGenerators$Base +com.fasterxml.jackson.annotation.ObjectIdGenerators$PropertyGenerator +com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator +com.fasterxml.jackson.databind.deser.BeanDeserializerBase +com.fasterxml.jackson.databind.deser.BeanDeserializer +com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer +com.fasterxml.jackson.databind.deser.impl.MethodProperty +com.fasterxml.jackson.databind.deser.impl.FieldProperty +com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer +com.fasterxml.jackson.databind.deser.impl.SetterlessProperty +com.fasterxml.jackson.databind.deser.Deserializers +com.fasterxml.jackson.databind.PropertyName +com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig +com.fasterxml.jackson.databind.deser.BeanDeserializerModifier +com.fasterxml.jackson.databind.AbstractTypeResolver +com.fasterxml.jackson.databind.deser.ValueInstantiators +com.fasterxml.jackson.databind.deser.KeyDeserializers +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers +com.fasterxml.jackson.databind.KeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringCtorKeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringFactoryKeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$DelegatingKD +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$EnumKD +com.fasterxml.jackson.databind.deser.DeserializerCache +com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer +com.fasterxml.jackson.databind.ser.ContainerSerializer +com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase +com.fasterxml.jackson.databind.ser.std.CollectionSerializer +com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase +com.fasterxml.jackson.databind.ser.impl.IndexedStringListSerializer +com.fasterxml.jackson.databind.ser.impl.StringCollectionSerializer +com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer +com.fasterxml.jackson.databind.ser.std.EnumSetSerializer +com.fasterxml.jackson.databind.ser.std.MapSerializer +com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer +com.fasterxml.jackson.databind.ser.std.ArraySerializerBase +com.fasterxml.jackson.databind.ser.impl.StringArraySerializer +com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer +com.fasterxml.jackson.databind.ser.impl.IteratorSerializer +com.fasterxml.jackson.databind.ser.std.IterableSerializer +com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +com.fasterxml.jackson.databind.ser.std.EnumSerializer +com.fasterxml.jackson.databind.ser.std.JsonValueSerializer +com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase +com.fasterxml.jackson.databind.ser.std.ToStringSerializer +com.fasterxml.jackson.databind.ser.std.SerializableSerializer +com.fasterxml.jackson.databind.ser.std.DateTimeSerializerBase +com.fasterxml.jackson.databind.ser.std.CalendarSerializer +com.fasterxml.jackson.databind.ser.std.DateSerializer +com.fasterxml.jackson.databind.ser.std.ByteBufferSerializer +com.fasterxml.jackson.databind.ser.std.InetAddressSerializer +com.fasterxml.jackson.databind.ser.std.InetSocketAddressSerializer +com.fasterxml.jackson.databind.ser.std.TimeZoneSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializer +com.fasterxml.jackson.databind.ser.std.ReferenceTypeSerializer +com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator +com.fasterxml.jackson.databind.introspect.AnnotatedField +com.fasterxml.jackson.databind.ser.std.BeanSerializerBase +com.fasterxml.jackson.databind.ser.BeanSerializer +com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer +com.fasterxml.jackson.databind.ser.std.StringSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers +com.fasterxml.jackson.databind.ser.std.NumberSerializers$Base +com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntegerSerializer +com.fasterxml.jackson.core.JsonParser$NumberType +com.fasterxml.jackson.databind.ser.std.NumberSerializers$LongSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntLikeSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$ShortSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$DoubleSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$FloatSerializer +com.fasterxml.jackson.databind.ser.std.BooleanSerializer +com.fasterxml.jackson.databind.ser.std.BooleanSerializer$AsNumber +com.fasterxml.jackson.databind.ser.std.NumberSerializer$BigDecimalAsStringSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers +java.util.Currency +java.util.UUID +com.fasterxml.jackson.databind.ser.std.UUIDSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicBooleanSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicIntegerSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicLongSerializer +com.fasterxml.jackson.databind.ser.std.FileSerializer +com.fasterxml.jackson.databind.ser.std.ClassSerializer +com.fasterxml.jackson.databind.ser.std.TokenBufferSerializer +com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig +com.fasterxml.jackson.databind.ser.Serializers +com.fasterxml.jackson.databind.ser.BeanSerializerModifier +org.junit.jupiter.engine.execution.DefaultTestInstances +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$387/0x000000700111c2d0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$388/0x000000700111c518 +org.junit.jupiter.api.extension.TestInstancePostProcessor +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$389/0x000000700111c940 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$390/0x000000700111cb78 +org.junit.jupiter.api.Order +java.lang.invoke.LambdaForm$DMH/0x0000007001120000 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$391/0x000000700111cfc8 +org.junit.jupiter.api.extension.RegisterExtension +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$392/0x000000700111d408 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$393/0x000000700111d650 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$394/0x000000700111d8a0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$395/0x000000700111dae8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$396/0x000000700111dd30 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$397/0x000000700111df70 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$398/0x000000700111e1c0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$399/0x000000700111e400 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$400/0x000000700111e658 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$401/0x000000700111e8a8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$402/0x000000700111eae8 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$CallbackInvoker +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$403/0x000000700111ef38 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$404/0x000000700111f158 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$405/0x000000700111f380 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$406/0x000000700111f5b8 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$407/0x000000700111f808 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$408/0x000000700111fa30 +java.util.AbstractList$Itr +java.util.AbstractList$ListItr +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$409/0x000000700111fc50 +org.junit.jupiter.engine.execution.MethodInvocation +org.junit.jupiter.engine.extension.TimeoutExtension$TimeoutProvider +org.junit.jupiter.engine.extension.TimeoutConfiguration +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$410/0x00000070011246e8 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$411/0x0000007001124940 +org.junit.jupiter.engine.extension.TimeoutDurationParser +java.time.DateTimeException +java.time.format.DateTimeParseException +java.util.regex.Pattern$$Lambda$412/0x000000700107f270 +java.util.regex.Pattern$$Lambda$413/0x000000700107f4d0 +java.util.regex.Pattern$$Lambda$414/0x000000700107f730 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$415/0x0000007001124d98 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$416/0x0000007001124fc0 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$417/0x0000007001125208 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$418/0x0000007001125450 +org.junit.jupiter.api.extension.BeforeTestExecutionCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$419/0x0000007001125878 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$420/0x0000007001125a98 +org.junit.jupiter.engine.descriptor.MethodExtensionContext$$Lambda$421/0x0000007001125cc0 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$422/0x0000007001125f00 +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$423/0x0000007001126158 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$424/0x0000007001126380 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$425/0x00000070011265a8 +software.amazon.lambda.powertools.metrics.MetricsBuilder +org.crac.Resource +software.amazon.lambda.powertools.metrics.MetricsFactory +software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger +org.slf4j.LoggerFactory +org.slf4j.spi.SLF4JServiceProvider +java.util.ServiceConfigurationError +org.slf4j.event.LoggingEvent +org.slf4j.helpers.SubstituteServiceProvider +org.slf4j.IMarkerFactory +org.slf4j.spi.MDCAdapter +org.slf4j.ILoggerFactory +org.slf4j.helpers.SubstituteLoggerFactory +org.slf4j.Logger +java.util.concurrent.LinkedBlockingQueue +java.util.concurrent.LinkedBlockingQueue$Node +org.slf4j.helpers.BasicMarkerFactory +org.slf4j.Marker +org.slf4j.helpers.BasicMDCAdapter +java.lang.InheritableThreadLocal +org.slf4j.helpers.BasicMDCAdapter$1 +org.slf4j.helpers.ThreadLocalMapOfStacks +org.slf4j.helpers.NOP_FallbackServiceProvider +org.slf4j.helpers.NOPLoggerFactory +org.slf4j.helpers.NOPMDCAdapter +org.slf4j.helpers.Util +org.slf4j.simple.SimpleServiceProvider +org.slf4j.simple.SimpleLoggerFactory +org.slf4j.helpers.AbstractLogger +org.slf4j.helpers.LegacyAbstractLogger +org.slf4j.simple.SimpleLogger +org.slf4j.spi.LoggingEventBuilder +org.slf4j.simple.SimpleLoggerConfiguration +org.slf4j.simple.SimpleLoggerConfiguration$$Lambda$426/0x0000007001128230 +sun.net.ProgressMonitor +sun.net.ProgressMeteringPolicy +sun.net.DefaultProgressMeteringPolicy +org.slf4j.simple.OutputChoice +org.slf4j.simple.OutputChoice$OutputChoiceType +software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider +software.amazon.cloudwatchlogs.emf.environment.Environment +software.amazon.cloudwatchlogs.emf.config.EnvironmentConfigurationProvider +software.amazon.cloudwatchlogs.emf.config.Configuration +software.amazon.cloudwatchlogs.emf.config.SystemWrapper +java.lang.ProcessEnvironment +java.lang.ProcessEnvironment$ExternalData +java.lang.ProcessEnvironment$Variable +java.lang.ProcessEnvironment$Value +java.lang.ProcessEnvironment$StringEnvironment +software.amazon.cloudwatchlogs.emf.util.StringUtils +software.amazon.cloudwatchlogs.emf.environment.Environments +software.amazon.cloudwatchlogs.emf.environment.LambdaEnvironment +software.amazon.cloudwatchlogs.emf.sinks.ISink +software.amazon.cloudwatchlogs.emf.environment.AgentBasedEnvironment +software.amazon.cloudwatchlogs.emf.environment.DefaultEnvironment +software.amazon.cloudwatchlogs.emf.sinks.retry.RetryStrategy +software.amazon.cloudwatchlogs.emf.environment.EC2Environment +software.amazon.cloudwatchlogs.emf.exception.EMFClientException +software.amazon.cloudwatchlogs.emf.environment.ResourceFetcher +software.amazon.cloudwatchlogs.emf.environment.ECSEnvironment +java.net.UnknownHostException +software.amazon.cloudwatchlogs.emf.model.MetricsContext +software.amazon.cloudwatchlogs.emf.model.RootNode +com.fasterxml.jackson.databind.ser.FilterProvider +com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider +com.fasterxml.jackson.databind.ser.BeanPropertyFilter +com.fasterxml.jackson.databind.ser.PropertyFilter +com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter +software.amazon.cloudwatchlogs.emf.model.EmptyMetricsFilter +com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter$FilterExceptFilter +com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter$SerializeExceptFilter +software.amazon.cloudwatchlogs.emf.model.Metadata +java.time.InstantSource +java.time.Clock +software.amazon.cloudwatchlogs.emf.model.MetricDirective +java.util.Collections$SynchronizedList +java.util.Collections$SynchronizedRandomAccessList +software.amazon.cloudwatchlogs.emf.model.DimensionSet +software.amazon.cloudwatchlogs.emf.exception.DimensionSetExceededException +software.amazon.cloudwatchlogs.emf.logger.MetricsLogger +software.amazon.cloudwatchlogs.emf.exception.InvalidDimensionException +software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider$1 +java.util.concurrent.CompletionStage +java.util.concurrent.CompletableFuture +java.util.concurrent.CompletableFuture$AltResult +java.util.concurrent.ForkJoinPool +java.lang.invoke.VarHandleLongs$FieldInstanceReadOnly +java.lang.invoke.VarHandleLongs$FieldInstanceReadWrite +java.lang.invoke.VarHandleInts$FieldStaticReadOnly +java.lang.invoke.VarHandleInts$FieldStaticReadWrite +java.util.concurrent.ForkJoinPool$ForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$1 +java.util.concurrent.ForkJoinPool$DefaultCommonPoolForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$WorkQueue +java.util.concurrent.CompletableFuture$AsynchronousCompletionTask +java.util.concurrent.ForkJoinTask +java.util.concurrent.CompletableFuture$Completion +software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor +software.amazon.lambda.powertools.common.internal.SystemWrapper +software.amazon.lambda.powertools.metrics.internal.Validator +software.amazon.cloudwatchlogs.emf.util.Validator +software.amazon.cloudwatchlogs.emf.exception.InvalidNamespaceException +software.amazon.cloudwatchlogs.emf.exception.InvalidTimestampException +software.amazon.cloudwatchlogs.emf.exception.InvalidMetricException +org.assertj.core.api.InstanceOfAssertFactories +org.assertj.core.api.Assertions +org.assertj.core.api.NumberAssert +org.assertj.core.api.ComparableAssert +org.assertj.core.api.Descriptable +org.assertj.core.api.ExtensionPoints +org.assertj.core.api.Assert +org.assertj.core.api.AbstractAssert +org.assertj.core.api.AbstractObjectAssert +org.assertj.core.api.AbstractComparableAssert +org.assertj.core.api.AbstractBigIntegerAssert +org.assertj.core.api.BigIntegerAssert +org.assertj.core.data.TemporalOffset +org.assertj.core.data.TemporalUnitOffset +org.assertj.core.data.TemporalUnitWithinOffset +org.assertj.core.data.TemporalUnitLessThanOffset +org.assertj.core.configuration.ConfigurationProvider +org.assertj.core.api.AssertionsForClassTypes +org.assertj.core.api.EnumerableAssert +org.assertj.core.api.AbstractCharSequenceAssert +org.assertj.core.api.CharSequenceAssert +org.assertj.core.api.AbstractStringAssert +org.assertj.core.api.StringAssert +org.assertj.core.api.AbstractDateAssert +org.assertj.core.api.DateAssert +org.assertj.core.api.AbstractTemporalAssert +org.assertj.core.api.AbstractZonedDateTimeAssert +org.assertj.core.api.ZonedDateTimeAssert +org.assertj.core.api.AbstractShortAssert +org.assertj.core.api.ShortAssert +org.assertj.core.api.ArraySortedAssert +org.assertj.core.api.AbstractEnumerableAssert +org.assertj.core.api.AbstractArrayAssert +org.assertj.core.api.AbstractShortArrayAssert +org.assertj.core.api.ShortArrayAssert +org.assertj.core.api.AbstractYearMonthAssert +org.assertj.core.api.YearMonthAssert +org.assertj.core.api.AbstractInstantAssert +org.assertj.core.api.InstantAssert +org.assertj.core.api.AbstractDurationAssert +org.assertj.core.api.DurationAssert +org.assertj.core.api.AbstractPeriodAssert +org.assertj.core.api.PeriodAssert +org.assertj.core.api.AbstractThrowableAssert +org.assertj.core.api.ThrowableAssert +org.assertj.core.api.AbstractLocalDateTimeAssert +org.assertj.core.api.LocalDateTimeAssert +org.assertj.core.api.AbstractOffsetDateTimeAssert +org.assertj.core.api.OffsetDateTimeAssert +org.assertj.core.api.AbstractOffsetTimeAssert +org.assertj.core.api.OffsetTimeAssert +org.assertj.core.api.AbstractLocalTimeAssert +org.assertj.core.api.LocalTimeAssert +org.assertj.core.api.AbstractLocalDateAssert +org.assertj.core.api.LocalDateAssert +org.assertj.core.api.AbstractCharacterAssert +org.assertj.core.api.CharacterAssert +org.assertj.core.api.AbstractBigDecimalAssert +org.assertj.core.api.BigDecimalAssert +org.assertj.core.api.AbstractCharArrayAssert +org.assertj.core.api.CharArrayAssert +org.assertj.core.api.AbstractByteAssert +org.assertj.core.api.ByteAssert +org.assertj.core.api.AbstractBooleanAssert +org.assertj.core.api.BooleanAssert +org.assertj.core.api.AbstractBooleanArrayAssert +org.assertj.core.api.BooleanArrayAssert +org.assertj.core.api.AbstractUriAssert +org.assertj.core.api.UriAssert +org.assertj.core.api.AbstractUrlAssert +org.assertj.core.api.UrlAssert +org.assertj.core.api.AbstractByteArrayAssert +org.assertj.core.api.ByteArrayAssert +org.assertj.core.api.AbstractIntArrayAssert +org.assertj.core.api.IntArrayAssert +org.assertj.core.api.AbstractIntegerAssert +org.assertj.core.api.IntegerAssert +org.assertj.core.api.AbstractFloatArrayAssert +org.assertj.core.api.FloatArrayAssert +org.assertj.core.api.AbstractLongArrayAssert +org.assertj.core.api.LongArrayAssert +org.assertj.core.api.AbstractLongAssert +org.assertj.core.api.LongAssert +org.assertj.core.api.AbstractDoubleArrayAssert +org.assertj.core.api.DoubleArrayAssert +org.assertj.core.api.FloatingPointNumberAssert +org.assertj.core.api.AbstractDoubleAssert +org.assertj.core.api.DoubleAssert +org.assertj.core.api.AbstractFloatAssert +org.assertj.core.api.FloatAssert +org.assertj.core.api.AbstractInputStreamAssert +org.assertj.core.api.InputStreamAssert +org.assertj.core.api.AbstractFileAssert +org.assertj.core.api.FileAssert +org.assertj.core.api.ObjectAssert +org.assertj.core.description.Description +org.assertj.core.description.LazyTextDescription +org.assertj.core.description.TextDescription +org.assertj.core.api.AssertionInfo +org.assertj.core.internal.ComparisonStrategy +org.assertj.core.api.ObjectEnumerableAssert +org.assertj.core.api.IndexedObjectEnumerableAssert +org.assertj.core.api.AbstractIterableAssert +org.assertj.core.api.AbstractCollectionAssert +org.assertj.core.api.AbstractListAssert +org.assertj.core.api.FactoryBasedNavigableListAssert +org.assertj.core.api.ListAssert +org.assertj.core.internal.Objects +org.assertj.core.util.introspection.IntrospectionError +org.assertj.core.error.ErrorMessageFactory +org.assertj.core.internal.AbstractComparisonStrategy +org.assertj.core.internal.StandardComparisonStrategy +org.assertj.core.util.introspection.PropertySupport +org.assertj.core.internal.Failures +org.assertj.core.error.AssertionErrorCreator +org.assertj.core.util.Arrays +org.assertj.core.error.ConstructorInvoker +org.assertj.core.util.introspection.FieldSupport +org.assertj.core.error.GroupTypeDescription +org.assertj.core.internal.Conditions +org.assertj.core.api.WritableAssertionInfo +org.assertj.core.presentation.Representation +org.assertj.core.configuration.Configuration +org.assertj.core.configuration.PreferredAssumptionException +org.assertj.core.configuration.PreferredAssumptionException$1 +org.assertj.core.configuration.Services +org.assertj.core.util.Lists +org.assertj.core.util.Streams +org.assertj.core.util.Lists$$Lambda$427/0x00000070011aa8d0 +org.assertj.core.presentation.CompositeRepresentation +org.assertj.core.presentation.CompositeRepresentation$$Lambda$428/0x00000070011aad48 +java.util.Collections$ReverseComparator +java.util.Collections$ReverseComparator2 +org.assertj.core.presentation.StandardRepresentation +java.time.chrono.ChronoLocalDateTime +java.time.LocalDateTime +java.time.chrono.ChronoZonedDateTime +java.time.ZonedDateTime +java.time.OffsetDateTime +java.nio.file.DirectoryStream +org.assertj.core.api.ThrowableAssert$ThrowingCallable +software.amazon.lambda.powertools.metrics.MetricsBuilderTest$$Lambda$429/0x00000070011ab530 +org.assertj.core.internal.Throwables +org.assertj.core.internal.CommonValidations +org.junit.jupiter.api.extension.AfterTestExecutionCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$430/0x00000070011abe30 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$431/0x00000070011ac050 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$432/0x00000070011ac288 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$433/0x00000070011ac4b0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$434/0x00000070011ac6d8 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$435/0x00000070011ac8f8 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$436/0x00000070011acb50 +jdk.internal.reflect.UnsafeStaticObjectFieldAccessorImpl +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$437/0x00000070011acd78 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$438/0x00000070011acf98 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$439/0x00000070011ad1c0 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$440/0x00000070011ad3e8 +org.junit.jupiter.engine.descriptor.MethodExtensionContext$$Lambda$441/0x00000070011ad610 +org.junit.jupiter.api.extension.TestInstancePreDestroyCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$442/0x00000070011ada50 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$443/0x00000070011adc70 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$444/0x00000070011ade98 +java.util.concurrent.ConcurrentHashMap$EntrySpliterator +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$445/0x00000070011ae2f8 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$446/0x00000070011ae538 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue$$Lambda$447/0x00000070011ae788 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$448/0x00000070011ae9c8 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$449/0x00000070011aec00 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$450/0x00000070011aee28 +org.junit.platform.engine.TestExecutionResult +org.junit.platform.engine.TestExecutionResult$Status +org.junit.jupiter.api.extension.TestWatcher +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$451/0x00000070011af8c0 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$452/0x00000070011afaf8 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$453/0x00000070011afd30 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$454/0x00000070011b0000 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$455/0x00000070011b0250 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$456/0x00000070011b0490 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$457/0x00000070011b06d0 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$458/0x00000070011b0920 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$459/0x00000070011b0b58 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$460/0x00000070011b0d80 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$461/0x00000070011b0fb8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$462/0x00000070011b11e0 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$463/0x00000070011b1418 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$464/0x00000070011b1640 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$1 +java.lang.invoke.LambdaForm$DMH/0x00000070011b4000 +software.amazon.lambda.powertools.metrics.model.DimensionSet$$Lambda$465/0x00000070011b1aa0 +org.apache.commons.lang3.StringUtils +org.apache.commons.lang3.CharUtils +java.util.function.IntFunction +org.apache.commons.lang3.CharUtils$$Lambda$466/0x00000070011b20e8 +org.apache.commons.lang3.ArrayUtils +java.util.concurrent.ThreadLocalRandom +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger$$Lambda$467/0x00000070011b2510 +software.amazon.cloudwatchlogs.emf.logger.MetricsLogger$$Lambda$468/0x00000070011b2940 +software.amazon.cloudwatchlogs.emf.model.StorageResolution +software.amazon.cloudwatchlogs.emf.model.MetricDefinition +java.lang.invoke.LambdaForm$DMH/0x00000070011b4400 +software.amazon.cloudwatchlogs.emf.model.MetricDirective$$Lambda$469/0x00000070011b37d0 +java.lang.invoke.LambdaForm$DMH/0x00000070011b4800 +java.lang.invoke.LambdaForm$DMH/0x00000070011b4c00 +java.lang.invoke.LambdaForm$MH/0x00000070011b5000 +software.amazon.cloudwatchlogs.emf.sinks.ConsoleSink +software.amazon.cloudwatchlogs.emf.environment.LambdaEnvironment$$Lambda$470/0x00000070011b3c48 +java.util.concurrent.ConcurrentHashMap$ValueSpliterator +software.amazon.cloudwatchlogs.emf.model.MetricsContext$$Lambda$471/0x00000070011b6000 +com.fasterxml.jackson.core.util.BufferRecyclers +com.fasterxml.jackson.core.util.BufferRecycler +com.fasterxml.jackson.core.util.TextBuffer +com.fasterxml.jackson.core.io.ContentReference +com.fasterxml.jackson.core.io.IOContext +com.fasterxml.jackson.core.util.ReadConstrainedTextBuffer +com.fasterxml.jackson.core.exc.StreamWriteException +com.fasterxml.jackson.core.JsonGenerationException +com.fasterxml.jackson.core.JsonStreamContext +com.fasterxml.jackson.core.json.JsonWriteContext +com.fasterxml.jackson.core.StreamWriteCapability +com.fasterxml.jackson.core.util.JacksonFeatureSet +com.fasterxml.jackson.core.FormatFeature +com.fasterxml.jackson.core.json.JsonWriteFeature +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$Bucket +com.fasterxml.jackson.databind.util.TypeKey +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$$Lambda$472/0x00000070011b8a38 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntrySet +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntryIterator +com.fasterxml.jackson.databind.type.ClassStack +sun.reflect.generics.repository.AbstractRepository +sun.reflect.generics.repository.GenericDeclRepository +sun.reflect.generics.repository.ClassRepository +java.lang.reflect.TypeVariable +sun.reflect.generics.tree.FormalTypeParameter +sun.reflect.generics.tree.Signature +sun.reflect.generics.tree.ClassSignature +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WeightedValue +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Node +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$AddTask +com.fasterxml.jackson.databind.introspect.AnnotationCollector$OneCollector +com.fasterxml.jackson.annotation.JsonFilter +com.fasterxml.jackson.annotation.JacksonAnnotation +jdk.proxy2.$Proxy19 +com.fasterxml.jackson.databind.introspect.AnnotationCollector$NCollector +com.fasterxml.jackson.annotation.JacksonAnnotationsInside +jdk.proxy2.$Proxy20 +com.fasterxml.jackson.databind.introspect.AnnotationCollector$OneAnnotation +com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector +com.fasterxml.jackson.annotation.JsonAutoDetect +com.fasterxml.jackson.annotation.JsonIdentityInfo +com.fasterxml.jackson.databind.util.ArrayIterator +com.fasterxml.jackson.databind.introspect.CollectorBase +com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector +com.fasterxml.jackson.databind.introspect.AnnotationMap +com.fasterxml.jackson.databind.introspect.TypeResolutionContext$Basic +com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector$FieldBuilder +com.fasterxml.jackson.annotation.JsonProperty +com.fasterxml.jackson.annotation.JsonProperty$Access +jdk.proxy2.$Proxy21 +com.fasterxml.jackson.annotation.JsonKey +com.fasterxml.jackson.annotation.JsonValue +com.fasterxml.jackson.annotation.JsonAnyGetter +com.fasterxml.jackson.annotation.JsonAnySetter +com.fasterxml.jackson.core.util.InternCache +com.fasterxml.jackson.annotation.JsonGetter +com.fasterxml.jackson.annotation.JsonIgnore +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$WithMember +com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty +com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty$Type +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$Linked +com.fasterxml.jackson.annotation.JsonAutoDetect$1 +com.fasterxml.jackson.annotation.PropertyAccessor +com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector +com.fasterxml.jackson.databind.introspect.MemberKey +com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector$MethodBuilder +jdk.proxy2.$Proxy22 +com.fasterxml.jackson.databind.introspect.AnnotatedMethodMap +sun.reflect.generics.scope.MethodScope +sun.reflect.generics.repository.ConstructorRepository +sun.reflect.generics.repository.MethodRepository +sun.reflect.generics.tree.MethodTypeSignature +java.lang.reflect.ParameterizedType +sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl +sun.reflect.generics.reflectiveObjects.LazyReflectiveObjectGenerator +sun.reflect.generics.reflectiveObjects.TypeVariableImpl +com.fasterxml.jackson.databind.type.TypeBindings$TypeParamStash +sun.reflect.generics.tree.TypeVariableSignature +com.fasterxml.jackson.databind.type.TypeBindings$AsKey +com.fasterxml.jackson.annotation.JsonSetter +com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector +sun.reflect.generics.scope.ConstructorScope +sun.reflect.generics.tree.VoidDescriptor +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$5 +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$6 +java.util.LinkedList$ListItr +com.fasterxml.jackson.annotation.JacksonInject +com.fasterxml.jackson.databind.annotation.JsonNaming +com.fasterxml.jackson.annotation.JsonPropertyOrder +com.fasterxml.jackson.annotation.JsonPropertyDescription +com.fasterxml.jackson.databind.PropertyMetadata +com.fasterxml.jackson.databind.ext.OptionalHandlerFactory +org.w3c.dom.Node +org.w3c.dom.Document +com.fasterxml.jackson.databind.ext.Java7Handlers +com.fasterxml.jackson.databind.ext.Java7HandlersImpl +com.fasterxml.jackson.databind.ext.NioPathSerializer +com.fasterxml.jackson.databind.ext.NioPathDeserializer +java.net.InetAddress +com.fasterxml.jackson.databind.util.BeanUtil +com.fasterxml.jackson.databind.ObjectReader +com.fasterxml.jackson.databind.ObjectWriter +com.fasterxml.jackson.databind.ser.BeanSerializerBuilder +com.fasterxml.jackson.annotation.JsonIgnoreType +com.fasterxml.jackson.databind.ser.PropertyBuilder +com.fasterxml.jackson.annotation.JsonInclude +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$3 +com.fasterxml.jackson.annotation.JsonTypeId +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$2 +com.fasterxml.jackson.databind.BeanProperty$Std +com.fasterxml.jackson.databind.annotation.JsonTypeResolver +com.fasterxml.jackson.databind.ser.PropertyBuilder$1 +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$1 +com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Empty +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Single +com.fasterxml.jackson.databind.annotation.JsonAppend +com.fasterxml.jackson.annotation.JsonIgnoreProperties +com.fasterxml.jackson.annotation.JsonIgnoreProperties$Value +com.fasterxml.jackson.annotation.JsonIncludeProperties +com.fasterxml.jackson.annotation.JsonIncludeProperties$Value +com.fasterxml.jackson.databind.ser.std.MapProperty +com.fasterxml.jackson.databind.util.IgnorePropertiesUtil +com.fasterxml.jackson.databind.ser.AnyGetterWriter +com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer +com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer +com.fasterxml.jackson.databind.ser.std.StdKeySerializers +com.fasterxml.jackson.databind.ser.std.StdKeySerializer +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$StringKeySerializer +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Dynamic +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Default +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$EnumKeySerializer +com.fasterxml.jackson.annotation.JsonFormat$Feature +software.amazon.cloudwatchlogs.emf.model.Metadata$$Lambda$473/0x00000070011c9930 +com.fasterxml.jackson.annotation.OptBoolean +jdk.proxy2.$Proxy23 +com.fasterxml.jackson.databind.annotation.JsonSerialize$Inclusion +com.fasterxml.jackson.databind.annotation.JsonSerialize$Typing +com.fasterxml.jackson.databind.util.Converter +com.fasterxml.jackson.databind.util.Converter$None +com.fasterxml.jackson.databind.JsonSerializer$None +software.amazon.cloudwatchlogs.emf.serializers.InstantSerializer +jdk.proxy2.$Proxy24 +com.fasterxml.jackson.databind.JsonDeserializer$None +com.fasterxml.jackson.databind.KeyDeserializer$None +software.amazon.cloudwatchlogs.emf.serializers.InstantDeserializer +jdk.proxy2.$Proxy25 +jdk.internal.ValueBased +com.sun.proxy.jdk.proxy1.$Proxy26 +java.lang.FunctionalInterface +jdk.proxy1.$Proxy27 +com.fasterxml.jackson.databind.introspect.AnnotationCollector$TwoAnnotations +com.fasterxml.jackson.databind.annotation.NoClass +com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector$1 +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$SerializerAndMapResult +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Double +com.fasterxml.jackson.core.io.NumberOutput +jdk.proxy2.$Proxy28 +sun.reflect.generics.tree.BooleanSignature +software.amazon.cloudwatchlogs.emf.serializers.UnitSerializer +software.amazon.cloudwatchlogs.emf.serializers.UnitDeserializer +software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionFilter +jdk.proxy2.$Proxy29 +software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionSerializer +software.amazon.cloudwatchlogs.emf.model.MetricDirective$$Lambda$474/0x00000070011ceac8 +com.fasterxml.jackson.databind.BeanProperty$Bogus +jdk.internal.vm.annotation.IntrinsicCandidate +com.sun.proxy.jdk.proxy1.$Proxy30 +java.lang.Deprecated +jdk.proxy1.$Proxy31 +com.fasterxml.jackson.databind.introspect.MethodGenericTypeResolver +com.fasterxml.jackson.databind.ser.std.NumberSerializers$1 +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Multi +software.amazon.cloudwatchlogs.emf.model.MetricDirective$$Lambda$475/0x00000070011cf658 +software.amazon.cloudwatchlogs.emf.model.MetricDirective$$Lambda$476/0x00000070011cf890 +com.fasterxml.jackson.core.exc.StreamReadException +com.fasterxml.jackson.core.exc.InputCoercionException +com.fasterxml.jackson.core.JsonParseException +com.fasterxml.jackson.core.io.JsonEOFException +com.fasterxml.jackson.core.json.JsonReadContext +com.fasterxml.jackson.core.StreamReadCapability +com.fasterxml.jackson.core.JsonToken +com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer +com.fasterxml.jackson.databind.node.ArrayNode +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer$ObjectDeserializer +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer$ArrayDeserializer +com.fasterxml.jackson.databind.util.LinkedNode +com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer$ContainerStack +com.fasterxml.jackson.core.io.NumberInput +com.fasterxml.jackson.core.JsonParser$NumberTypeFP +com.fasterxml.jackson.core.StreamReadFeature +org.assertj.core.internal.Strings +org.assertj.core.internal.InputStreamsException +org.assertj.core.internal.Comparables +java.util.AbstractMap$SimpleEntry +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WriteThroughEntry +java.lang.invoke.LambdaForm$DMH/0x00000070011d8000 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$477/0x00000070011d54d0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$478/0x00000070011d5708 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$479/0x00000070011d5940 +org.apache.maven.surefire.api.util.internal.ObjectUtils +org.apache.maven.surefire.api.util.internal.ImmutableMap$Node +jdk.internal.reflect.GeneratedMethodAccessor1 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$480/0x00000070011d5fe8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$481/0x00000070011d6228 +java.util.stream.Nodes +java.util.stream.Node +java.util.stream.Nodes$EmptyNode +java.util.stream.Nodes$EmptyNode$OfRef +java.util.stream.Node$OfPrimitive +java.util.stream.Node$OfInt +java.util.stream.Nodes$EmptyNode$OfInt +java.util.stream.Node$OfLong +java.util.stream.Nodes$EmptyNode$OfLong +java.util.stream.Node$OfDouble +java.util.stream.Nodes$EmptyNode$OfDouble +java.util.stream.Node$Builder +java.util.stream.Nodes$ArrayNode +java.util.stream.Nodes$FixedNodeBuilder +org.junitpioneer.internal.PioneerUtils +org.junitpioneer.internal.PioneerUtils$$Lambda$482/0x00000070011d6650 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$483/0x00000070011d6890 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$484/0x00000070011d6ac8 +org.junitpioneer.jupiter.ClearEnvironmentVariable +org.junitpioneer.jupiter.ClearEnvironmentVariable$ClearEnvironmentVariables +org.junitpioneer.jupiter.EnvironmentVariableExtension$$Lambda$485/0x00000070011d7100 +org.junitpioneer.internal.PioneerUtils$$Lambda$486/0x00000070011d7340 +org.junitpioneer.internal.PioneerUtils$$Lambda$487/0x00000070011d7560 +org.junitpioneer.internal.PioneerUtils$$Lambda$488/0x00000070011d7790 +org.junitpioneer.jupiter.EnvironmentVariableExtension$$Lambda$489/0x00000070011d79d8 +org.junitpioneer.jupiter.EnvironmentVariableExtension$$Lambda$490/0x00000070011d7c18 +java.util.stream.Collectors$$Lambda$491/0x000000700114cf38 +java.util.stream.Collectors$$Lambda$492/0x000000700114d158 +java.util.stream.Collectors$$Lambda$493/0x000000700114d390 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$494/0x00000070011dc000 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$495/0x00000070011dc258 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$496/0x00000070011dc498 +java.time.chrono.ChronoLocalDate +java.time.LocalDate +java.time.temporal.TemporalField +java.time.temporal.ChronoField +java.time.temporal.ValueRange +java.time.LocalTime +java.time.Clock$SystemClock +java.time.ZoneId +java.time.ZoneOffset +java.time.ZoneRegion +java.time.zone.ZoneRulesProvider +java.time.zone.ZoneRulesProvider$1 +java.time.zone.TzdbZoneRulesProvider +java.time.zone.Ser +java.time.zone.ZoneRules +java.time.zone.ZoneOffsetTransitionRule +java.time.Month +java.time.DayOfWeek +java.time.zone.ZoneOffsetTransitionRule$TimeDefinition +java.time.zone.ZoneOffsetTransition +java.time.temporal.TemporalAdjusters +java.lang.invoke.LambdaForm$DMH/0x00000070011d8800 +java.time.temporal.TemporalAdjusters$$Lambda$497/0x00000070011507c8 +java.time.LocalDate$1 +java.time.chrono.Chronology +java.time.chrono.AbstractChronology +java.time.chrono.IsoChronology +java.time.zone.ZoneOffsetTransitionRule$1 +org.junit.platform.engine.reporting.ReportEntry$$Lambda$498/0x00000070011dc6e0 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$499/0x00000070011dc918 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$500/0x00000070011dcb50 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$501/0x00000070011dcd78 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$502/0x00000070011dcfb0 +org.apache.maven.surefire.api.report.TestOutputReportEntry +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup$$Lambda$503/0x00000070011dd628 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$504/0x00000070011dd860 +java.lang.invoke.LambdaForm$DMH/0x00000070011d8c00 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$505/0x00000070011dda98 +org.junitpioneer.jupiter.EnvironmentVariableUtils +org.junitpioneer.jupiter.EnvironmentVariableUtils$$Lambda$506/0x00000070011dded8 +jdk.internal.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl +jdk.internal.reflect.UnsafeQualifiedFieldAccessorImpl +jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl +org.aspectj.lang.JoinPoint +org.aspectj.lang.ProceedingJoinPoint +org.aspectj.lang.Signature +org.aspectj.runtime.internal.AroundClosure +software.amazon.lambda.powertools.metrics.ConfigurationPrecedenceTest$HandlerWithMetricsAnnotation$AjcClosure1 +org.aspectj.runtime.reflect.Factory +org.aspectj.lang.JoinPoint$StaticPart +org.aspectj.lang.JoinPoint$EnclosingStaticPart +org.aspectj.lang.reflect.MemberSignature +org.aspectj.lang.reflect.CodeSignature +org.aspectj.lang.reflect.MethodSignature +org.aspectj.lang.reflect.SourceLocation +org.aspectj.lang.reflect.ConstructorSignature +org.aspectj.lang.reflect.FieldSignature +org.aspectj.lang.reflect.AdviceSignature +org.aspectj.lang.reflect.InitializerSignature +org.aspectj.lang.reflect.CatchClauseSignature +org.aspectj.lang.reflect.LockSignature +org.aspectj.lang.reflect.UnlockSignature +org.aspectj.runtime.reflect.SignatureImpl +org.aspectj.runtime.reflect.MemberSignatureImpl +org.aspectj.runtime.reflect.CodeSignatureImpl +org.aspectj.runtime.reflect.MethodSignatureImpl +org.aspectj.runtime.reflect.SignatureImpl$Cache +org.aspectj.runtime.reflect.JoinPointImpl$StaticPartImpl +org.aspectj.runtime.reflect.SourceLocationImpl +org.aspectj.runtime.reflect.JoinPointImpl +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect +org.aspectj.lang.NoAspectBoundException +software.amazon.lambda.powertools.metrics.FlushMetrics +jdk.proxy2.$Proxy32 +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect$$Lambda$507/0x00000070011d98f0 +software.amazon.cloudwatchlogs.emf.logger.MetricsLogger$$Lambda$508/0x00000070011d9b28 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$$Lambda$509/0x00000070011d9d50 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup$$Lambda$510/0x00000070011e0000 +org.junitpioneer.jupiter.EnvironmentVariableUtils$$Lambda$511/0x00000070011e0238 +org.junitpioneer.jupiter.AbstractEntryBasedExtension$EntriesBackup$$Lambda$512/0x00000070011e0470 +software.amazon.lambda.powertools.metrics.ConfigurationPrecedenceTest$HandlerWithDefaultMetricsAnnotation$AjcClosure1 +software.amazon.lambda.powertools.metrics.MetricsFactoryTest$$Lambda$513/0x00000070011e08f8 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$514/0x00000070011e0b18 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$515/0x00000070011e0d40 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$516/0x00000070011e0f68 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$517/0x00000070011e1188 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$518/0x00000070011e13b0 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$519/0x00000070011e15d8 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$520/0x00000070011e1800 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$521/0x00000070011e1a20 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$522/0x00000070011e1c40 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$523/0x00000070011e1e60 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$524/0x00000070011e2080 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$525/0x00000070011e22a0 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$526/0x00000070011e24c0 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$527/0x00000070011e26e0 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$528/0x00000070011e2900 +jdk.internal.reflect.GeneratedConstructorAccessor8 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$529/0x00000070011e2b28 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$530/0x00000070011e2d48 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$531/0x00000070011e2f68 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$532/0x00000070011e3188 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$533/0x00000070011e33a8 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$534/0x00000070011e35d0 +software.amazon.lambda.powertools.metrics.internal.ValidatorTest$$Lambda$535/0x00000070011e37f0 +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger$$Lambda$536/0x00000070011e3a18 +software.amazon.cloudwatchlogs.emf.logger.MetricsLogger$$Lambda$537/0x00000070011e3c50 +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger$$Lambda$538/0x00000070011e6000 +org.assertj.core.internal.Numbers +org.assertj.core.internal.RealNumbers +org.assertj.core.internal.Doubles +org.assertj.core.internal.ComparatorBasedComparisonStrategy +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLoggerTest$$Lambda$539/0x00000070011e7018 +software.amazon.cloudwatchlogs.emf.Constants +org.assertj.core.internal.WholeNumbers +org.assertj.core.internal.Longs +jdk.internal.reflect.GeneratedMethodAccessor2 +jdk.internal.reflect.GeneratedMethodAccessor3 +jdk.internal.reflect.GeneratedMethodAccessor4 +jdk.internal.reflect.GeneratedMethodAccessor5 +jdk.internal.reflect.GeneratedMethodAccessor6 +jdk.internal.reflect.GeneratedMethodAccessor7 +jdk.internal.reflect.GeneratedMethodAccessor8 +jdk.internal.reflect.GeneratedMethodAccessor9 +jdk.internal.reflect.GeneratedMethodAccessor10 +jdk.internal.reflect.GeneratedMethodAccessor11 +jdk.internal.reflect.GeneratedMethodAccessor12 +jdk.internal.reflect.GeneratedMethodAccessor13 +org.assertj.core.internal.Integers +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLoggerTest$$Lambda$540/0x00000070011ec3a8 +software.amazon.cloudwatchlogs.emf.logger.MetricsLogger$$Lambda$541/0x00000070011ec5d0 +java.lang.invoke.LambdaForm$DMH/0x00000070011e9400 +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger$$Lambda$542/0x00000070011ec7f8 +software.amazon.cloudwatchlogs.emf.logger.MetricsLogger$$Lambda$543/0x00000070011eca30 +org.slf4j.event.Level +java.text.DontCareFieldPosition +java.text.Format$FieldDelegate +java.text.DontCareFieldPosition$1 +java.text.NumberFormat$Field +org.slf4j.helpers.MessageFormatter +org.slf4j.helpers.FormattingTuple +org.slf4j.simple.OutputChoice$1 +java.nio.file.attribute.FileAttribute +sun.nio.fs.UnixFileModeAttribute +sun.nio.fs.UnixChannelFactory +sun.nio.fs.UnixChannelFactory$Flags +java.nio.channels.ByteChannel +java.nio.channels.SeekableByteChannel +java.nio.channels.GatheringByteChannel +java.nio.channels.ScatteringByteChannel +java.nio.channels.InterruptibleChannel +java.nio.channels.spi.AbstractInterruptibleChannel +java.nio.channels.FileChannel +sun.nio.ch.FileChannelImpl +sun.nio.ch.IOUtil +sun.nio.ch.NativeThreadSet +sun.nio.ch.NativeDispatcher +sun.nio.ch.FileDispatcher +sun.nio.ch.FileDispatcherImpl +sun.nio.ch.FileChannelImpl$Closer +java.nio.channels.Channels +sun.nio.ch.ChannelInputStream +sun.nio.ch.NativeThread +sun.nio.ch.IOStatus +java.nio.channels.SelectableChannel +sun.nio.ch.Util +sun.nio.ch.Util$1 +sun.nio.ch.Util$BufferCache +java.nio.DirectByteBuffer$Deallocator +java.lang.invoke.LambdaForm$DMH/0x00000070011e9800 +org.assertj.core.internal.Strings$$Lambda$544/0x00000070011ed6d8 +org.assertj.core.internal.Strings$$Lambda$545/0x00000070011ed930 +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger$$Lambda$546/0x00000070011edb50 +org.assertj.core.api.AssertionsForInterfaceTypes +org.assertj.core.api.GenericComparableAssert +org.assertj.core.api.AbstractUniversalComparableAssert +org.assertj.core.api.UniversalComparableAssert +org.assertj.core.api.AbstractMapAssert +org.assertj.core.api.MapAssert +org.assertj.core.api.AbstractMapSizeAssert +org.assertj.core.api.MapSizeAssert +org.assertj.core.internal.Maps +java.lang.InstantiationException +org.assertj.core.data.MapEntry +org.assertj.core.internal.ErrorMessages +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$547/0x00000070011f1640 +org.junit.jupiter.api.RepeatedTest +org.junit.jupiter.params.ParameterizedTestMethodContext +org.junit.jupiter.params.ParameterizedTestMethodContext$Resolver +org.junit.jupiter.params.aggregator.ArgumentsAccessor +org.junit.jupiter.params.aggregator.AggregateWith +org.junit.jupiter.params.ParameterizedTestMethodContext$ResolverType +org.junit.jupiter.params.ParameterizedTestMethodContext$ResolverType$1 +org.junit.jupiter.params.ParameterizedTestMethodContext$ResolverType$2 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$548/0x00000070011f2dc0 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$549/0x00000070011f2fe8 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$550/0x00000070011f3210 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$551/0x00000070011f3458 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$552/0x00000070011f36a0 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$553/0x00000070011f38f0 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$554/0x00000070011f3b30 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$555/0x00000070011f3d68 +org.junit.platform.engine.ConfigurationParameters$$Lambda$556/0x00000070011f3fa8 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$557/0x00000070011f41f0 +org.junit.jupiter.params.ParameterizedTestNameFormatter +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$558/0x00000070011f4630 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$559/0x00000070011f4870 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$560/0x00000070011f4ab8 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$561/0x00000070011f4d00 +org.junit.jupiter.params.provider.Arguments +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$562/0x00000070011f5148 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$563/0x00000070011f5388 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$564/0x00000070011f55d0 +org.junit.jupiter.params.ParameterizedTestExtension$$Lambda$565/0x00000070011f5818 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$566/0x00000070011f5a40 +org.junit.jupiter.params.support.AnnotationConsumerInitializer +org.junit.jupiter.params.support.AnnotationConsumerInitializer$AnnotationConsumingMethodSignature +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$567/0x00000070011f62b0 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$568/0x00000070011f64f0 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$569/0x00000070011f6740 +java.util.stream.ReduceOps$1 +java.util.stream.ReduceOps$1ReducingSink +java.lang.invoke.LambdaForm$DMH/0x00000070011e9c00 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$570/0x00000070011f6988 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$571/0x00000070011f6be0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$572/0x00000070011f6e30 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$573/0x00000070011f7088 +sun.reflect.generics.tree.BottomSignature +sun.reflect.generics.tree.Wildcard +java.lang.reflect.WildcardType +sun.reflect.generics.reflectiveObjects.WildcardTypeImpl +org.junit.platform.commons.util.ReflectionUtils$$Lambda$574/0x00000070011f72e0 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$575/0x00000070011f7530 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$576/0x00000070011f7788 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$577/0x00000070011f79c8 +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext$$Lambda$578/0x00000070011f7bf0 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$579/0x00000070011f8000 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$580/0x00000070011f8248 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$581/0x00000070011f8490 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$582/0x00000070011f86d8 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$583/0x00000070011f8918 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$584/0x00000070011f8b58 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$585/0x00000070011f8d80 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$586/0x00000070011f8fc8 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$587/0x00000070011f9220 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$588/0x00000070011f9448 +org.junit.jupiter.params.provider.Arguments$$Lambda$589/0x00000070011f9868 +org.junit.jupiter.params.ParameterizedTestInvocationContext +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor$$Lambda$590/0x00000070011fa158 +org.junit.jupiter.params.ParameterizedTestNameFormatter$$Lambda$591/0x00000070011fa378 +java.util.stream.ReferencePipeline$$Lambda$592/0x0000007001156f20 +org.junit.jupiter.api.Named +java.util.stream.Streams$RangeIntSpliterator +org.junit.jupiter.params.ParameterizedTestNameFormatter$$Lambda$593/0x00000070011fa7b8 +java.util.stream.IntPipeline$1 +java.util.stream.IntPipeline$1$1 +org.junit.jupiter.params.ParameterizedTestNameFormatter$$Lambda$594/0x00000070011fa9e0 +java.text.MessageFormat +java.text.MessageFormat$Field +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$595/0x00000070011fac20 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$596/0x00000070011fae58 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$597/0x00000070011fb080 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$598/0x00000070011fb2b8 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor$$Lambda$599/0x00000070011fb4e0 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DynamicTaskState +org.junit.platform.engine.support.hierarchical.NodeTestTask$DynamicTaskState$$Lambda$600/0x00000070011fb908 +org.junit.jupiter.params.ParameterizedTestParameterResolver +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor$$Lambda$601/0x00000070011fbd88 +org.junit.jupiter.engine.execution.DefaultParameterContext +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$602/0x00000070011fc290 +org.junit.jupiter.api.TestReporter +org.junit.jupiter.params.ParameterizedTestParameterResolver$$Lambda$603/0x00000070011fc6e8 +org.junit.jupiter.params.converter.ConvertWith +org.junit.jupiter.params.ParameterizedTestMethodContext$ResolverType$1$$Lambda$604/0x00000070011fcb28 +org.junit.jupiter.params.converter.ArgumentConverter +org.junit.jupiter.params.ParameterizedTestMethodContext$ResolverType$1$$Lambda$605/0x00000070011fcf68 +org.junit.jupiter.params.ParameterizedTestMethodContext$ResolverType$1$$Lambda$606/0x00000070011fd1a8 +org.junit.jupiter.params.ParameterizedTestMethodContext$Converter +org.junit.jupiter.params.ParameterizedTestMethodContext$ResolverType$1$$Lambda$607/0x00000070011fd620 +org.junit.jupiter.params.converter.DefaultArgumentConverter +org.junit.jupiter.params.converter.ArgumentConversionException +org.junit.jupiter.params.converter.StringToObjectConverter +org.junit.jupiter.params.converter.StringToBooleanConverter +org.junit.jupiter.params.converter.StringToCharacterConverter +org.junit.jupiter.params.converter.StringToNumberConverter +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$608/0x00000070011fe7b0 +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$609/0x00000070011fe9f0 +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$610/0x00000070011fec30 +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$611/0x00000070011fee70 +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$612/0x00000070011ff0b0 +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$613/0x00000070011ff2f0 +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$614/0x00000070011ff530 +org.junit.jupiter.params.converter.StringToNumberConverter$$Lambda$615/0x00000070011ff770 +org.junit.jupiter.params.converter.StringToClassConverter +org.junit.jupiter.params.converter.StringToEnumConverter +org.junit.jupiter.params.converter.StringToJavaTimeConverter +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$616/0x0000007001200248 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$617/0x0000007001200488 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$618/0x00000070012006c8 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$619/0x0000007001200908 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$620/0x0000007001200b48 +java.time.MonthDay +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$621/0x0000007001200d88 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$622/0x0000007001200fc8 +java.time.OffsetTime +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$623/0x0000007001201208 +java.time.chrono.ChronoPeriod +java.time.Period +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$624/0x0000007001201448 +java.time.Year +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$625/0x0000007001201688 +java.time.YearMonth +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$626/0x00000070012018c8 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$627/0x0000007001201b08 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$628/0x0000007001201d48 +org.junit.jupiter.params.converter.StringToJavaTimeConverter$$Lambda$629/0x0000007001201f88 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$630/0x0000007001202410 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$631/0x0000007001202650 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$632/0x0000007001202890 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$633/0x0000007001202ad0 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$634/0x0000007001202d10 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$635/0x0000007001202f50 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$636/0x0000007001203190 +org.junit.jupiter.params.converter.StringToCommonJavaTypesConverter$$Lambda$637/0x00000070012033d0 +org.junit.jupiter.params.converter.FallbackStringToObjectConverter +org.junit.jupiter.params.converter.FallbackStringToObjectConverter$$Lambda$638/0x0000007001203858 +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$639/0x0000007001203a98 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$640/0x0000007001203cc0 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$641/0x0000007001203f18 +org.junit.jupiter.params.ParameterizedTestParameterResolver$$Lambda$642/0x0000007001204140 +org.junit.jupiter.params.ParameterizedTestParameterResolver$$Lambda$643/0x0000007001204398 +org.junit.jupiter.params.ParameterizedTestParameterResolver$CloseableArgument +org.junit.jupiter.params.ParameterizedTestParameterResolver$$Lambda$644/0x0000007001204810 +org.junit.jupiter.params.ParameterizedTestParameterResolver$$Lambda$645/0x0000007001204a50 +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext$$Lambda$646/0x0000007001204c88 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor$$Lambda$647/0x0000007001204ec8 +jdk.internal.reflect.GeneratedConstructorAccessor9 +jdk.internal.reflect.GeneratedMethodAccessor14 +jdk.internal.reflect.GeneratedMethodAccessor15 +jdk.internal.reflect.GeneratedMethodAccessor16 +jdk.internal.reflect.GeneratedMethodAccessor17 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$$Lambda$648/0x0000007001205100 +software.amazon.lambda.powertools.metrics.internal.EmfMetricsLoggerTest$$Lambda$649/0x0000007001205328 +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithAnnotationOnWrongMethod$AjcClosure1 +com.amazonaws.services.lambda.runtime.RequestStreamHandler +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithMetricsAnnotation$AjcClosure1 +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithDefaultMetricsAnnotation$AjcClosure1 +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithColdStartMetricsAnnotation$AjcClosure1 +org.assertj.core.api.AbstractObjectArrayAssert +org.assertj.core.api.ObjectArrayAssert +org.assertj.core.internal.ObjectArrays +org.assertj.core.internal.Arrays +org.assertj.core.internal.Iterables +org.assertj.core.internal.Predicates +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithCustomFunctionName$AjcClosure1 +software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest$HandlerWithServiceNameAndColdStart$AjcClosure1 +org.assertj.core.api.CollectionAssert +org.assertj.core.api.AbstractIterableSizeAssert +org.assertj.core.api.IterableSizeAssert +org.assertj.core.util.Lists$$Lambda$650/0x000000700120fda0 +org.assertj.core.internal.StandardComparisonStrategy$$Lambda$651/0x000000700120a000 +org.assertj.core.util.Preconditions +org.assertj.core.internal.IterableDiff +org.assertj.core.internal.IterableDiff$$Lambda$652/0x000000700120a678 +org.assertj.core.util.IterableUtil +software.amazon.lambda.powertools.metrics.model.DimensionSetTest$$Lambda$653/0x000000700120aad8 +org.junit.platform.launcher.core.OutcomeDelayingEngineExecutionListener$Outcome +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$654/0x000000700120b140 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$655/0x000000700120b378 +java.lang.invoke.LambdaForm$DMH/0x0000007001209400 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$656/0x000000700120b5a0 +org.junit.platform.launcher.core.DefaultLauncherSession$ClosedLauncher +org.apache.maven.surefire.api.suite.RunResult +org.apache.maven.surefire.booter.ForkedBooter$6 +org.apache.maven.surefire.booter.ForkedBooter$7 +java.util.concurrent.locks.AbstractQueuedSynchronizer$SharedNode +java.util.concurrent.locks.AbstractQueuedSynchronizer$ExclusiveNode +org.apache.maven.surefire.booter.ForkedBooter$1 +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory$2 +java.util.IdentityHashMap$IdentityHashMapIterator +java.util.IdentityHashMap$KeyIterator diff --git a/powertools-metrics/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-metrics/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..38e53da3e --- /dev/null +++ b/powertools-metrics/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.metrics.internal.MetricsUserAgentInterceptor diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java new file mode 100644 index 000000000..492ecfc1e --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java @@ -0,0 +1,207 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +/** + * Tests to verify the hierarchy of precedence for configuration: + * 1. @FlushMetrics annotation + * 2. MetricsBuilder + * 3. Environment variables + */ +class ConfigurationPrecedenceTest { + + private static final PrintStream STANDARD_OUT = System.out; + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() throws Exception { + System.setOut(new PrintStream(outputStreamCaptor)); + + // Reset LambdaHandlerProcessor's serviceName + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(STANDARD_OUT); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); + field.setAccessible(true); + field.set(null, null); + + field = MetricsFactory.class.getDeclaredField("provider"); + field.setAccessible(true); + field.set(null, new software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider()); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace") + @SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService") + void annotationShouldOverrideBuilderAndEnvironment() throws Exception { + // Given + // Configure with builder first + MetricsBuilder.builder() + .withNamespace("BuilderNamespace") + .withService("BuilderService") + .build(); + + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + // Annotation values should take precedence + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("AnnotationNamespace"); + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("AnnotationService"); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace") + @SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService") + void builderShouldOverrideEnvironment() throws Exception { + // Given + // Configure with builder + MetricsBuilder.builder() + .withNamespace("BuilderNamespace") + .withService("BuilderService") + .build(); + + RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + // Builder values should take precedence over environment variables + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("BuilderNamespace"); + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("BuilderService"); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace") + @SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService") + void environmentVariablesShouldBeUsedWhenNoOverrides() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + // Environment variable values should be used + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("EnvNamespace"); + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("EnvService"); + } + + @Test + void shouldUseDefaultsWhenNoConfiguration() throws Exception { + // Given + MetricsBuilder.builder() + .withNamespace("TestNamespace") + .build(); + + RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + // Default values should be used + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("TestNamespace"); + // Service dimension should not be present when service is undefined + assertThat(rootNode.has("Service")).isFalse(); + } + + private static final class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(namespace = "AnnotationNamespace", service = "AnnotationService") + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; + } + } + + private static final class HandlerWithDefaultMetricsAnnotation + implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; + } + } + +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java new file mode 100644 index 000000000..12ac46e43 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java @@ -0,0 +1,192 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.metrics.internal.RequestScopedMetricsProxy; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; +import software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider; + +class MetricsBuilderTest { + + private static final PrintStream STANDARD_OUT = System.out; + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() { + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(STANDARD_OUT); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); + field.setAccessible(true); + field.set(null, null); + + field = MetricsFactory.class.getDeclaredField("provider"); + field.setAccessible(true); + field.set(null, new software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider()); + } + + @Test + void shouldBuildWithCustomNamespace() throws Exception { + // When + Metrics metrics = MetricsBuilder.builder() + .withNamespace("CustomNamespace") + .build(); + + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("CustomNamespace"); + } + + @Test + void shouldBuildWithCustomService() throws Exception { + // When + Metrics metrics = MetricsBuilder.builder() + .withService("CustomService") + .withNamespace("TestNamespace") + .build(); + + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("CustomService"); + } + + @Test + void shouldBuildWithRaiseOnEmptyMetrics() { + // When + Metrics metrics = MetricsBuilder.builder() + .withRaiseOnEmptyMetrics(true) + .withNamespace("TestNamespace") + .build(); + + // Then + assertThat(metrics).isNotNull(); + assertThatThrownBy(metrics::flush) + .isInstanceOf(IllegalStateException.class) + .hasMessage("No metrics were emitted"); + } + + @Test + void shouldBuildWithDefaultDimension() throws Exception { + // When + Metrics metrics = MetricsBuilder.builder() + .withDefaultDimension("Environment", "Test") + .withNamespace("TestNamespace") + .build(); + + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Environment")).isTrue(); + assertThat(rootNode.get("Environment").asText()).isEqualTo("Test"); + } + + @Test + void shouldBuildWithMultipleDefaultDimensions() throws Exception { + // When + Metrics metrics = MetricsBuilder.builder() + .withDefaultDimensions(DimensionSet.of("Environment", "Test", "Region", "us-west-2")) + .withNamespace("TestNamespace") + .build(); + + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Environment")).isTrue(); + assertThat(rootNode.get("Environment").asText()).isEqualTo("Test"); + assertThat(rootNode.has("Region")).isTrue(); + assertThat(rootNode.get("Region").asText()).isEqualTo("us-west-2"); + } + + @Test + void shouldBuildWithCustomMetricsProvider() throws Exception { + // Given + MetricsProvider testProvider = new TestMetricsProvider(); + + // When + Metrics metrics = MetricsBuilder.builder() + .withMetricsProvider(testProvider) + .build(); + + // Then + assertThat(metrics) + .isInstanceOf(RequestScopedMetricsProxy.class); + + java.lang.reflect.Field providerField = metrics.getClass().getDeclaredField("provider"); + providerField.setAccessible(true); + MetricsProvider actualProvider = (MetricsProvider) providerField.get(metrics); + assertThat(actualProvider).isSameAs(testProvider); + } + + @Test + void shouldOverrideServiceWithDefaultDimensions() throws Exception { + // When + Metrics metrics = MetricsBuilder.builder() + .withService("OriginalService") + .withDefaultDimensions(DimensionSet.of("Service", "OverriddenService")) + .withNamespace("TestNamespace") + .build(); + + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("OverriddenService"); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java new file mode 100644 index 000000000..c9b183a1a --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java @@ -0,0 +1,224 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Method; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.internal.RequestScopedMetricsProxy; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; +import software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider; + +class MetricsFactoryTest { + + private static final String TEST_NAMESPACE = "TestNamespace"; + private static final String TEST_SERVICE = "TestService"; + + private static final PrintStream STANDARD_OUT = System.out; + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() throws Exception { + System.setOut(new PrintStream(outputStreamCaptor)); + + // Reset LambdaHandlerProcessor's serviceName + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(STANDARD_OUT); + System.clearProperty(LambdaConstants.XRAY_TRACE_HEADER); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); + field.setAccessible(true); + field.set(null, null); + + field = MetricsFactory.class.getDeclaredField("provider"); + field.setAccessible(true); + field.set(null, new software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider()); + } + + @Test + void shouldGetMetricsInstance() { + // When + Metrics metrics = MetricsFactory.getMetricsInstance(); + + // Then + assertThat(metrics).isNotNull(); + } + + @Test + void shouldReturnSameInstanceOnMultipleCalls() { + // When + Metrics firstInstance = MetricsFactory.getMetricsInstance(); + Metrics secondInstance = MetricsFactory.getMetricsInstance(); + + // Then + assertThat(firstInstance).isSameAs(secondInstance); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = TEST_NAMESPACE) + void shouldUseNamespaceFromEnvironmentVariable() throws Exception { + // When + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo(TEST_NAMESPACE); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = TEST_SERVICE) + void shouldUseServiceNameFromEnvironmentVariable() throws Exception { + // When + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.setNamespace("TestNamespace"); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo(TEST_SERVICE); + } + + @Test + void shouldSetCustomMetricsProvider() throws Exception { + // Given + MetricsProvider testProvider = new TestMetricsProvider(); + + // When + MetricsFactory.setMetricsProvider(testProvider); + Metrics metrics = MetricsFactory.getMetricsInstance(); + + // Then + assertThat(metrics) + .isInstanceOf(RequestScopedMetricsProxy.class); + + java.lang.reflect.Field providerField = metrics.getClass().getDeclaredField("provider"); + providerField.setAccessible(true); + MetricsProvider actualProvider = (MetricsProvider) providerField.get(metrics); + assertThat(actualProvider).isSameAs(testProvider); + } + + @Test + void shouldThrowExceptionWhenSettingNullProvider() { + // When/Then + assertThatThrownBy(() -> MetricsFactory.setMetricsProvider(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Metrics provider cannot be null"); + } + + @Test + void shouldNotSetServiceDimensionWhenServiceUndefined() throws Exception { + // Given - no POWERTOOLS_SERVICE_NAME set, so it will use the default undefined value + + // When + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.setNamespace("TestNamespace"); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + // Service dimension should not be present + assertThat(rootNode.has("Service")).isFalse(); + } + + @Test + void shouldIsolateMetricsByTraceId() throws Exception { + // GIVEN + Metrics metrics = MetricsFactory.getMetricsInstance(); + + // WHEN - Simulate Lambda invocation 1 with trace ID 1 + System.setProperty(LambdaConstants.XRAY_TRACE_HEADER, "Root=1-trace-id-1"); + metrics.setNamespace("TestNamespace"); + metrics.addDimension("userId", "user123"); + metrics.addMetric("ProcessedOrder", 1, MetricUnit.COUNT); + metrics.flush(); + + // WHEN - Simulate Lambda invocation 2 with trace ID 2 + System.setProperty(LambdaConstants.XRAY_TRACE_HEADER, "Root=1-trace-id-2"); + metrics.setNamespace("TestNamespace"); + metrics.addDimension("userId", "user456"); + metrics.addMetric("ProcessedOrder", 1, MetricUnit.COUNT); + metrics.flush(); + + // THEN - Verify each invocation has isolated metrics + String emfOutput = outputStreamCaptor.toString().trim(); + String[] jsonLines = emfOutput.split("\n"); + assertThat(jsonLines).hasSize(2); + + JsonNode output1 = objectMapper.readTree(jsonLines[0]); + JsonNode output2 = objectMapper.readTree(jsonLines[1]); + + assertThat(output1.get("userId").asText()).isEqualTo("user123"); + assertThat(output2.get("userId").asText()).isEqualTo("user456"); + } + + @Test + void shouldUseDefaultKeyWhenNoTraceId() throws Exception { + // GIVEN - No trace ID set + System.clearProperty(LambdaConstants.XRAY_TRACE_HEADER); + + // WHEN + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.setNamespace("TestNamespace"); + metrics.addMetric("TestMetric", 1, MetricUnit.COUNT); + metrics.flush(); + + // THEN - Should work without X-Ray trace ID + String emfOutput = outputStreamCaptor.toString().trim(); + assertThat(emfOutput).isNotEmpty(); + + JsonNode rootNode = objectMapper.readTree(emfOutput); + assertThat(rootNode.get("TestMetric").asDouble()).isEqualTo(1.0); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java deleted file mode 100644 index 7f234a4d6..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics; - -import static java.util.Collections.emptyMap; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNullPointerException; -import static org.mockito.Mockito.mockStatic; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.Map; -import java.util.function.Consumer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; - -class MetricsLoggerTest { - - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - private final PrintStream originalOut = System.out; - private final ObjectMapper mapper = new ObjectMapper(); - - @BeforeAll - static void beforeAll() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - } - } - - @BeforeEach - void setUp() { - System.setOut(new PrintStream(out)); - } - - @AfterEach - void tearDown() { - System.setOut(originalOut); - } - - @Test - void singleMetricsCaptureUtilityWithDefaultDimension() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - - MetricsUtils.defaultDimensions(DimensionSet.of("Service", "Booking")); - - MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, "test", - metricsLogger -> - { - }); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "Booking") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - }); - } - } - - @Test - void singleMetricsCaptureUtility() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - - MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, "test", - metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - }); - } - } - - @Test - void singleMetricsCaptureUtilityWithDefaultNameSpace() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - - MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, - metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=GlobalName"); - }); - } - } - - @Test - void metricsLoggerCaptureUtilityWithDefaultNameSpace() { - testLogger(MetricsUtils::withMetricsLogger); - } - - @Test - void deprecatedMetricLoggerCaptureUtilityWithDefaultNameSpace() { - testLogger(MetricsUtils::withMetricLogger); - } - - @Test - void shouldThrowExceptionWhenDefaultDimensionIsNull() { - assertThatNullPointerException() - .isThrownBy(() -> MetricsUtils.defaultDimensionSet(null)) - .withMessage("Null dimension set not allowed"); - } - - private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - - methodToTest.accept(metricsLogger -> - { - metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); - metricsLogger.putMetric("Metric1", 1, Unit.COUNT); - }); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=GlobalName"); - }); - } - } - - private Map<String, Object> readAsJson(String s) { - try { - return mapper.readValue(s, Map.class); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - return emptyMap(); - } -} \ No newline at end of file diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java new file mode 100644 index 000000000..fcd677c02 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java @@ -0,0 +1,126 @@ +package software.amazon.lambda.powertools.metrics; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +@ExtendWith(MockitoExtension.class) +class RequestHandlerTest { + + // For developer convenience, no exceptions should be thrown when using a plain Lambda Context mock + @Mock + Context lambdaContext; + + private static final PrintStream STDOUT = System.out; + private ByteArrayOutputStream outputStreamCaptor; + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() throws Exception { + outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + + // Reset LambdaHandlerProcessor's serviceName + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(STDOUT); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); + field.setAccessible(true); + field.set(null, null); + + field = MetricsFactory.class.getDeclaredField("provider"); + field.setAccessible(true); + field.set(null, new software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider()); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "TestNamespace") + @SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "TestService") + void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, lambdaContext); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + String[] jsonLines = emfOutput.split("\n"); + + // First JSON object should be the cold start metric + JsonNode coldStartNode = objectMapper.readTree(jsonLines[0]); + assertThat(coldStartNode.has("ColdStart")).isTrue(); + assertThat(coldStartNode.get("ColdStart").asDouble()).isEqualTo(1.0); + assertThat(coldStartNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("TestNamespace"); + assertThat(coldStartNode.has("Service")).isTrue(); + assertThat(coldStartNode.get("Service").asText()).isEqualTo("TestService"); + + // Second JSON object should be the regular metric + JsonNode regularNode = objectMapper.readTree(jsonLines[1]); + assertThat(regularNode.has("test-metric")).isTrue(); + assertThat(regularNode.get("test-metric").asDouble()).isEqualTo(100.0); + assertThat(regularNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("TestNamespace"); + assertThat(regularNode.has("Service")).isTrue(); + assertThat(regularNode.get("Service").asText()).isEqualTo("TestService"); + } + + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_DISABLED", value = "true") + @Test + void shouldNotCaptureMetricsWhenDisabled() { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, lambdaContext); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + assertThat(emfOutput).isEmpty(); + } + + static class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(captureColdStart = true) + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; + } + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java deleted file mode 100644 index 761c20caa..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.defaultDimensions; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsEnabledDefaultDimensionHandler implements RequestHandler<Object, Object> { - - static { - defaultDimensions(DimensionSet.of("CustomDimension", "booking")); - } - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - - withSingleMetric("Metric2", 1, Unit.COUNT, log -> - { - }); - - return null; - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java deleted file mode 100644 index d968f94f5..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.metrics.Metrics; -import software.amazon.lambda.powertools.metrics.MetricsUtils; - -public class PowertoolsMetricsEnabledDefaultNoDimensionHandler implements RequestHandler<Object, Object> { - - static { - MetricsUtils.defaultDimensions(); - } - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - - withSingleMetric("Metric2", 1, Unit.COUNT, log -> - { - }); - - return null; - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java deleted file mode 100644 index 7cfee533d..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsEnabledHandler implements RequestHandler<Object, Object> { - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - - - withSingleMetric("Metric2", 1, Unit.COUNT, - log -> log.setDimensions(DimensionSet.of("Dimension1", "Value1"))); - - return null; - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java deleted file mode 100644 index 1600f4a64..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import java.io.InputStream; -import java.io.OutputStream; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsEnabledStreamHandler implements RequestStreamHandler { - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public void handleRequest(InputStream input, OutputStream output, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java deleted file mode 100644 index 42e0b3ad4..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsExceptionWhenNoMetricsHandler implements RequestHandler<Object, Object> { - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking", raiseOnEmptyMetrics = true) - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetadata("MetaData", "MetaDataValue"); - - return null; - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java deleted file mode 100644 index 04b02e166..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsNoDimensionsHandler implements RequestHandler<Object, Object> { - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("CoolMetric", 1); - metricsLogger.setDimensions(new DimensionSet()); - - return null; - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java deleted file mode 100644 index c08ce2f86..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsNoExceptionWhenNoMetricsHandler implements RequestHandler<Object, Object> { - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetadata("MetaData", "MetaDataValue"); - - return null; - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java deleted file mode 100644 index bc8a6e949..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import java.util.stream.IntStream; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsTooManyDimensionsHandler implements RequestHandler<Object, Object> { - - @Override - @Metrics - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - - metricsLogger.setDimensions(IntStream.range(1, 15) - .mapToObj(value -> DimensionSet.of("Dimension" + value, "DimensionValue" + value)) - .toArray(DimensionSet[]::new)); - - return null; - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java deleted file mode 100644 index da9028a70..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.metrics.handlers; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class PowertoolsMetricsWithExceptionInHandler implements RequestHandler<Object, Object> { - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("CoolMetric", 1); - throw new IllegalStateException("Whoops, unexpected exception"); - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java new file mode 100644 index 000000000..5e597e835 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java @@ -0,0 +1,739 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.time.Instant; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider; +import software.amazon.cloudwatchlogs.emf.model.MetricsContext; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +class EmfMetricsLoggerTest { + + private Metrics metrics; + private final ObjectMapper objectMapper = new ObjectMapper(); + private static final PrintStream standardOut = System.out; + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + + @BeforeEach + void setUp() throws Exception { + // Reset LambdaHandlerProcessor's serviceName + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); + + metrics = new EmfMetricsLogger(new EnvironmentProvider(), new MetricsContext()); + metrics.setNamespace("TestNamespace"); + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @AfterEach + void tearDown() { + System.setOut(standardOut); + } + + @ParameterizedTest + @MethodSource("unitConversionTestCases") + void shouldConvertMetricUnits(MetricUnit inputUnit, Unit expectedUnit) throws Exception { + // Given + // We access using reflection here for simplicity (even though this is not best practice) + Method convertUnitMethod = EmfMetricsLogger.class.getDeclaredMethod("convertUnit", MetricUnit.class); + convertUnitMethod.setAccessible(true); + + // When + Unit actualUnit = (Unit) convertUnitMethod.invoke(metrics, inputUnit); + + // Then + assertThat(actualUnit).isEqualTo(expectedUnit); + } + + private static Stream<Arguments> unitConversionTestCases() { + return Stream.of( + Arguments.of(MetricUnit.SECONDS, Unit.SECONDS), + Arguments.of(MetricUnit.MICROSECONDS, Unit.MICROSECONDS), + Arguments.of(MetricUnit.MILLISECONDS, Unit.MILLISECONDS), + Arguments.of(MetricUnit.BYTES, Unit.BYTES), + Arguments.of(MetricUnit.KILOBYTES, Unit.KILOBYTES), + Arguments.of(MetricUnit.MEGABYTES, Unit.MEGABYTES), + Arguments.of(MetricUnit.GIGABYTES, Unit.GIGABYTES), + Arguments.of(MetricUnit.TERABYTES, Unit.TERABYTES), + Arguments.of(MetricUnit.BITS, Unit.BITS), + Arguments.of(MetricUnit.KILOBITS, Unit.KILOBITS), + Arguments.of(MetricUnit.MEGABITS, Unit.MEGABITS), + Arguments.of(MetricUnit.GIGABITS, Unit.GIGABITS), + Arguments.of(MetricUnit.TERABITS, Unit.TERABITS), + Arguments.of(MetricUnit.PERCENT, Unit.PERCENT), + Arguments.of(MetricUnit.COUNT, Unit.COUNT), + Arguments.of(MetricUnit.BYTES_SECOND, Unit.BYTES_SECOND), + Arguments.of(MetricUnit.KILOBYTES_SECOND, Unit.KILOBYTES_SECOND), + Arguments.of(MetricUnit.MEGABYTES_SECOND, Unit.MEGABYTES_SECOND), + Arguments.of(MetricUnit.GIGABYTES_SECOND, Unit.GIGABYTES_SECOND), + Arguments.of(MetricUnit.TERABYTES_SECOND, Unit.TERABYTES_SECOND), + Arguments.of(MetricUnit.BITS_SECOND, Unit.BITS_SECOND), + Arguments.of(MetricUnit.KILOBITS_SECOND, Unit.KILOBITS_SECOND), + Arguments.of(MetricUnit.MEGABITS_SECOND, Unit.MEGABITS_SECOND), + Arguments.of(MetricUnit.GIGABITS_SECOND, Unit.GIGABITS_SECOND), + Arguments.of(MetricUnit.TERABITS_SECOND, Unit.TERABITS_SECOND), + Arguments.of(MetricUnit.COUNT_SECOND, Unit.COUNT_SECOND), + Arguments.of(MetricUnit.NONE, Unit.NONE)); + } + + @Test + void shouldCreateMetricWithDefaultResolution() throws Exception { + // When + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("_aws")).isTrue(); + assertThat(rootNode.get("test-metric").asDouble()).isEqualTo(100); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Metrics").get(0).get("Unit").asText()) + .isEqualTo("Count"); + } + + @Test + void shouldCreateMetricWithHighResolution() throws Exception { + // When + metrics.addMetric("test-metric", 100, MetricUnit.COUNT, MetricResolution.HIGH); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("_aws")).isTrue(); + assertThat(rootNode.get("test-metric").asDouble()).isEqualTo(100); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Metrics").get(0).get("Unit").asText()) + .isEqualTo("Count"); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Metrics").get(0).get("StorageResolution") + .asInt()).isEqualTo(1); + } + + @Test + void shouldAddDimension() throws Exception { + // When + metrics.clearDefaultDimensions(); // Clear default Service dimension first for easier assertions + metrics.addDimension("CustomDimension", "CustomValue"); + metrics.addMetric("test-metric", 100); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("CustomDimension")).isTrue(); + assertThat(rootNode.get("CustomDimension").asText()).isEqualTo("CustomValue"); + + // Check that the dimension is in the CloudWatchMetrics section + JsonNode dimensions = rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); + boolean hasDimension = false; + for (JsonNode dimension : dimensions) { + if ("CustomDimension".equals(dimension.asText())) { + hasDimension = true; + break; + } + } + assertThat(hasDimension).isTrue(); + } + + @Test + void shouldSetCustomTimestamp() throws Exception { + // Given + Instant customTimestamp = Instant.now(); + + // When + metrics.setTimestamp(customTimestamp); + metrics.addMetric("test-metric", 100); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("_aws")).isTrue(); + assertThat(rootNode.get("_aws").has("Timestamp")).isTrue(); + assertThat(rootNode.get("_aws").get("Timestamp").asLong()).isEqualTo(customTimestamp.toEpochMilli()); + } + + @Test + void shouldAddDimensionSet() throws Exception { + // Given + DimensionSet dimensionSet = DimensionSet.of("Dim1", "Value1", "Dim2", "Value2"); + + // When + metrics.clearDefaultDimensions(); // Clear default Service dimension first for easier assertions + metrics.addDimension(dimensionSet); + metrics.addMetric("test-metric", 100); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Dim1")).isTrue(); + assertThat(rootNode.get("Dim1").asText()).isEqualTo("Value1"); + assertThat(rootNode.has("Dim2")).isTrue(); + assertThat(rootNode.get("Dim2").asText()).isEqualTo("Value2"); + + // Check that the dimensions are in the CloudWatchMetrics section + JsonNode dimensions = rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); + boolean hasDim1 = false; + boolean hasDim2 = false; + for (JsonNode dimension : dimensions) { + String dimName = dimension.asText(); + if ("Dim1".equals(dimName)) { + hasDim1 = true; + } else if ("Dim2".equals(dimName)) { + hasDim2 = true; + } + } + assertThat(hasDim1).isTrue(); + assertThat(hasDim2).isTrue(); + } + + @Test + void shouldThrowExceptionWhenDimensionSetIsNull() { + // When/Then + assertThatThrownBy(() -> metrics.addDimension(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("DimensionSet cannot be null"); + } + + @Test + void shouldAddMetadata() throws Exception { + // When + metrics.addMetadata("CustomMetadata", "MetadataValue"); + metrics.addMetric("test-metric", 100); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + // The metadata is added to the _aws section in the EMF output + assertThat(rootNode.has("CustomMetadata")).isTrue(); + assertThat(rootNode.get("CustomMetadata").asText()).isEqualTo("MetadataValue"); + } + + @Test + void shouldClearMetadataAfterFlush() throws Exception { + // Given - Add metadata and flush first time + metrics.addMetadata("RequestId", "req-123"); + metrics.addMetadata("UserAgent", "test-agent"); + metrics.addMetric("FirstMetric", 1.0); + metrics.flush(); + + // Capture first flush output and reset for second flush + String firstFlushOutput = outputStreamCaptor.toString().trim(); + outputStreamCaptor.reset(); + + // When - Add another metric and flush again using the SAME metrics instance + metrics.addMetric("SecondMetric", 2.0); + metrics.flush(); + + // Then - Verify first flush had metadata + JsonNode firstRootNode = objectMapper.readTree(firstFlushOutput); + assertThat(firstRootNode.has("RequestId")).isTrue(); + assertThat(firstRootNode.get("RequestId").asText()).isEqualTo("req-123"); + assertThat(firstRootNode.has("UserAgent")).isTrue(); + assertThat(firstRootNode.get("UserAgent").asText()).isEqualTo("test-agent"); + assertThat(firstRootNode.has("FirstMetric")).isTrue(); + + // Verify second flush does NOT have metadata from first flush + // The EMF library automatically clears metadata after flush + String secondFlushOutput = outputStreamCaptor.toString().trim(); + JsonNode secondRootNode = objectMapper.readTree(secondFlushOutput); + + // Metadata should be cleared after first flush by the EMF library + assertThat(secondRootNode.has("RequestId")).isFalse(); + assertThat(secondRootNode.has("UserAgent")).isFalse(); + assertThat(secondRootNode.has("SecondMetric")).isTrue(); + } + + @Test + void shouldInheritMetadataInFlushMetricsMethod() throws Exception { + // Given - Add metadata to the main metrics instance + metrics.addMetadata("PersistentMetadata", "should-inherit"); + metrics.addMetadata("GlobalContext", "main-instance"); + + // When - Use flushMetrics to create a separate metrics context + metrics.flushMetrics(separateMetrics -> { + separateMetrics.addMetric("SeparateMetric", 1.0); + // Don't add any metadata to the separate instance + }); + + // Then - The separate metrics context SHOULD inherit metadata from main instance + String flushMetricsOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(flushMetricsOutput); + + // The separate metrics should have inherited metadata (this is expected behavior) + assertThat(rootNode.has("PersistentMetadata")).isTrue(); + assertThat(rootNode.get("PersistentMetadata").asText()).isEqualTo("should-inherit"); + assertThat(rootNode.has("GlobalContext")).isTrue(); + assertThat(rootNode.get("GlobalContext").asText()).isEqualTo("main-instance"); + assertThat(rootNode.has("SeparateMetric")).isTrue(); + } + + @Test + void shouldSetDefaultDimensions() throws Exception { + // Given + DimensionSet dimensionSet = DimensionSet.of("Service", "TestService", "Environment", "Test"); + + // When + metrics.setDefaultDimensions(dimensionSet); + metrics.addMetric("test-metric", 100); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("TestService"); + assertThat(rootNode.has("Environment")).isTrue(); + assertThat(rootNode.get("Environment").asText()).isEqualTo("Test"); + } + + @Test + void shouldGetDefaultDimensions() { + // Given + DimensionSet dimensionSet = DimensionSet.of("Service", "TestService", "Environment", "Test"); + + // When + metrics.setDefaultDimensions(dimensionSet); + DimensionSet dimensions = metrics.getDefaultDimensions(); + + // Then + assertThat(dimensions.getDimensions()).containsEntry("Service", "TestService"); + assertThat(dimensions.getDimensions()).containsEntry("Environment", "Test"); + } + + @Test + void shouldThrowExceptionWhenDefaultDimensionSetIsNull() { + // When/Then + assertThatThrownBy(() -> metrics.setDefaultDimensions(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("DimensionSet cannot be null"); + } + + @Test + void shouldSetNamespace() throws Exception { + // When + metrics.setNamespace("CustomNamespace"); + metrics.addMetric("test-metric", 100); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("CustomNamespace"); + } + + @Test + void shouldRaiseExceptionOnEmptyMetrics() { + // When + metrics.setRaiseOnEmptyMetrics(true); + + // Then + assertThatThrownBy(() -> metrics.flush()) + .isInstanceOf(IllegalStateException.class) + .hasMessage("No metrics were emitted"); + } + + @Test + void shouldLogWarningOnEmptyMetrics() throws Exception { + // Given + File logFile = new File("target/metrics-test.log"); + + // When + // Flushing without adding metrics + metrics.flush(); + + // Then + // Read the log file and check for the warning + String logContent = Files.readString(logFile.toPath(), StandardCharsets.UTF_8); + assertThat(logContent).contains("No metrics were emitted"); + // No EMF output should be generated + assertThat(outputStreamCaptor.toString().trim()).isEmpty(); + } + + @Test + void shouldClearDefaultDimensions() throws Exception { + // Given + metrics.setDefaultDimensions(DimensionSet.of("Service", "TestService", "Environment", "Test")); + + // When + metrics.clearDefaultDimensions(); + metrics.addMetric("test-metric", 100); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("Service")).isFalse(); + assertThat(rootNode.has("Environment")).isFalse(); + } + + @Test + void shouldCaptureColdStartMetric() throws Exception { + // Given + Context testContext = new TestLambdaContext(); + + // When + metrics.captureColdStartMetric(testContext); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("ColdStart")).isTrue(); + assertThat(rootNode.get("ColdStart").asDouble()).isEqualTo(1.0); + assertThat(rootNode.has("function_request_id")).isTrue(); + assertThat(rootNode.get("function_request_id").asText()).isEqualTo(testContext.getAwsRequestId()); + } + + @Test + void shouldCaptureColdStartMetricWithDimensions() throws Exception { + // Given + DimensionSet dimensions = DimensionSet.of("CustomDim", "CustomValue"); + + // When + metrics.captureColdStartMetric(dimensions); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("ColdStart")).isTrue(); + assertThat(rootNode.get("ColdStart").asDouble()).isEqualTo(1.0); + assertThat(rootNode.has("CustomDim")).isTrue(); + assertThat(rootNode.get("CustomDim").asText()).isEqualTo("CustomValue"); + } + + @Test + void shouldCaptureColdStartMetricWithoutDimensions() throws Exception { + // When + metrics.captureColdStartMetric(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("ColdStart")).isTrue(); + assertThat(rootNode.get("ColdStart").asDouble()).isEqualTo(1.0); + } + + @Test + void shouldReuseNamespaceForColdStartMetric() throws Exception { + // Given + String customNamespace = "CustomNamespace"; + metrics.setNamespace(customNamespace); + + Context testContext = new TestLambdaContext(); + + DimensionSet dimensions = DimensionSet.of("CustomDim", "CustomValue"); + + // When + metrics.captureColdStartMetric(testContext, dimensions); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("ColdStart")).isTrue(); + assertThat(rootNode.get("ColdStart").asDouble()).isEqualTo(1.0); + assertThat(rootNode.has("CustomDim")).isTrue(); + assertThat(rootNode.get("CustomDim").asText()).isEqualTo("CustomValue"); + assertThat(rootNode.has("function_request_id")).isTrue(); + assertThat(rootNode.get("function_request_id").asText()).isEqualTo(testContext.getAwsRequestId()); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo(customNamespace); + } + + @Test + void shouldFlushMetrics() throws Exception { + // Given + metrics.setNamespace("MainNamespace"); + metrics.setDefaultDimensions(DimensionSet.of("CustomDim", "CustomValue")); + metrics.addDimension(DimensionSet.of("CustomDim2", "CustomValue2")); + metrics.addMetadata("CustomMetadata", "MetadataValue"); + + // When + metrics.flushMetrics(m -> { + m.addMetric("metric-one", 200, MetricUnit.COUNT); + m.addMetric("metric-two", 100, MetricUnit.COUNT); + }); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("metric-one")).isTrue(); + assertThat(rootNode.get("metric-one").asDouble()).isEqualTo(200.0); + assertThat(rootNode.has("metric-two")).isTrue(); + assertThat(rootNode.get("metric-two").asDouble()).isEqualTo(100); + assertThat(rootNode.has("CustomDim")).isTrue(); + assertThat(rootNode.get("CustomDim").asText()).isEqualTo("CustomValue"); + assertThat(rootNode.get("CustomDim2")).isNull(); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("MainNamespace"); + assertThat(rootNode.has("CustomMetadata")).isTrue(); + assertThat(rootNode.get("CustomMetadata").asText()).isEqualTo("MetadataValue"); + } + + @Test + void shouldFlushSingleMetric() throws Exception { + // Given + DimensionSet dimensions = DimensionSet.of("CustomDim", "CustomValue"); + + // When + metrics.flushSingleMetric("single-metric", 200, MetricUnit.COUNT, "SingleNamespace", dimensions); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("single-metric")).isTrue(); + assertThat(rootNode.get("single-metric").asDouble()).isEqualTo(200.0); + assertThat(rootNode.has("CustomDim")).isTrue(); + assertThat(rootNode.get("CustomDim").asText()).isEqualTo("CustomValue"); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("SingleNamespace"); + } + + @Test + void shouldFlushSingleMetricWithoutDimensions() throws Exception { + // When + metrics.flushSingleMetric("single-metric", 200, MetricUnit.COUNT, "SingleNamespace"); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("single-metric")).isTrue(); + assertThat(rootNode.get("single-metric").asDouble()).isEqualTo(200.0); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("SingleNamespace"); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_DISABLED", value = "true") + void shouldNotFlushMetricsWhenDisabled() { + // When + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + metrics.flush(); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + assertThat(emfOutput).isEmpty(); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_DISABLED", value = "true") + void shouldNotCaptureColdStartMetricWhenDisabled() { + // Given + Context testContext = new TestLambdaContext(); + + // When + metrics.captureColdStartMetric(testContext); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + assertThat(emfOutput).isEmpty(); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_DISABLED", value = "true") + void shouldNotFlushSingleMetricWhenDisabled() { + // Given + DimensionSet dimensions = DimensionSet.of("CustomDim", "CustomValue"); + + // When + metrics.flushSingleMetric("single-metric", 200, MetricUnit.COUNT, "SingleNamespace", dimensions); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + assertThat(emfOutput).isEmpty(); + } + + @Test + void shouldClearCustomDimensionsAfterFlush() throws Exception { + // Given - Set up default dimensions that should persist + DimensionSet defaultDimensions = DimensionSet.of("Service", "TestService", "Environment", "Test"); + metrics.setDefaultDimensions(defaultDimensions); + + // First invocation - add custom dimensions and flush + DimensionSet customDimensions = DimensionSet.of("EXAMPLE_KEY", "EXAMPLE_VALUE"); + metrics.addDimension(customDimensions); + metrics.addMetric("SERL", 1.0); + metrics.flush(); + + // Capture first flush output + String firstFlushOutput = outputStreamCaptor.toString().trim(); + outputStreamCaptor.reset(); // Clear for second flush + + // Second invocation - should NOT have custom dimensions from first invocation + metrics.addMetric("Expected", 1.0); + metrics.flush(); + + // Then - Verify first flush had both default and custom dimensions + JsonNode firstRootNode = objectMapper.readTree(firstFlushOutput); + assertThat(firstRootNode.has("Service")).isTrue(); + assertThat(firstRootNode.get("Service").asText()).isEqualTo("TestService"); + assertThat(firstRootNode.has("Environment")).isTrue(); + assertThat(firstRootNode.get("Environment").asText()).isEqualTo("Test"); + assertThat(firstRootNode.has("EXAMPLE_KEY")).isTrue(); + assertThat(firstRootNode.get("EXAMPLE_KEY").asText()).isEqualTo("EXAMPLE_VALUE"); + assertThat(firstRootNode.has("SERL")).isTrue(); + + // Verify second flush has ONLY default dimensions (custom dimensions should be cleared) + String secondFlushOutput = outputStreamCaptor.toString().trim(); + JsonNode secondRootNode = objectMapper.readTree(secondFlushOutput); + + // Default dimensions should still be present + assertThat(secondRootNode.has("Service")).isTrue(); + assertThat(secondRootNode.get("Service").asText()).isEqualTo("TestService"); + assertThat(secondRootNode.has("Environment")).isTrue(); + assertThat(secondRootNode.get("Environment").asText()).isEqualTo("Test"); + + // Custom dimensions should be cleared (this is the failing assertion that demonstrates the bug) + assertThat(secondRootNode.has("EXAMPLE_KEY")).isFalse(); + assertThat(secondRootNode.has("Expected")).isTrue(); + + // Verify dimensions in CloudWatchMetrics section + JsonNode secondDimensions = secondRootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); + boolean hasExampleKey = false; + boolean hasService = false; + boolean hasEnvironment = false; + + for (JsonNode dimension : secondDimensions) { + String dimName = dimension.asText(); + if ("EXAMPLE_KEY".equals(dimName)) { + hasExampleKey = true; + } else if ("Service".equals(dimName)) { + hasService = true; + } else if ("Environment".equals(dimName)) { + hasEnvironment = true; + } + } + + // Default dimensions should be in CloudWatchMetrics + assertThat(hasService).isTrue(); + assertThat(hasEnvironment).isTrue(); + // Custom dimension should NOT be in CloudWatchMetrics (this should fail initially) + assertThat(hasExampleKey).isFalse(); + } + + @Test + void shouldHandleEmptyCustomDimensionsGracefully() throws Exception { + // Given - Only default dimensions, no custom dimensions + metrics.setDefaultDimensions(DimensionSet.of("Service", "TestService")); + + // When - Flush without adding custom dimensions + metrics.addMetric("TestMetric", 1.0); + metrics.flush(); + outputStreamCaptor.reset(); + + // Second flush + metrics.addMetric("TestMetric2", 2.0); + metrics.flush(); + + // Then - Should work normally with only default dimensions + String output = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(output); + + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("TestService"); + assertThat(rootNode.has("TestMetric2")).isTrue(); + } + + @Test + void shouldClearCustomDimensionsWhenNoDefaultDimensionsSet() throws Exception { + // Given - No default dimensions set + metrics.clearDefaultDimensions(); + + // When - Add custom dimensions and flush + metrics.addDimension("CustomDim", "CustomValue"); + metrics.addMetric("Metric1", 1.0); + metrics.flush(); + outputStreamCaptor.reset(); + + // Second flush without custom dimensions + metrics.addMetric("Metric2", 2.0); + metrics.flush(); + + // Then - Custom dimensions should be cleared + String output = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(output); + + assertThat(rootNode.has("CustomDim")).isFalse(); + assertThat(rootNode.has("Metric2")).isTrue(); + + // Verify no custom dimensions in CloudWatchMetrics section + JsonNode dimensionsArray = rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions"); + boolean hasCustomDim = false; + if (dimensionsArray != null && dimensionsArray.size() > 0) { + JsonNode dimensions = dimensionsArray.get(0); + if (dimensions != null) { + for (JsonNode dimension : dimensions) { + if ("CustomDim".equals(dimension.asText())) { + hasCustomDim = true; + break; + } + } + } + } + assertThat(hasCustomDim).isFalse(); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java index eaddfa75d..119e094a9 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java @@ -14,426 +14,315 @@ package software.amazon.lambda.powertools.metrics.internal; -import static java.util.Collections.emptyMap; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Method; +import java.util.HashMap; import java.util.Map; + import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; -import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.metrics.MetricsUtils; -import software.amazon.lambda.powertools.metrics.ValidationException; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsColdStartEnabledHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledDefaultDimensionHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledDefaultNoDimensionHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledStreamHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsExceptionWhenNoMetricsHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsNoDimensionsHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsNoExceptionWhenNoMetricsHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsTooManyDimensionsHandler; -import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsWithExceptionInHandler; - -public class LambdaMetricsAspectTest { - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - private final PrintStream originalOut = System.out; - private final ObjectMapper mapper = new ObjectMapper(); - @Mock - private Context context; - private RequestHandler<Object, Object> requestHandler; - - - @BeforeAll - static void beforeAll() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - } - } +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.FlushMetrics; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.MetricsFactory; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; + +class LambdaMetricsAspectTest { + + private static final PrintStream STANDARD_OUT = System.out; + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach - void setUp() throws IllegalAccessException { - openMocks(this); - setupContext(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - System.setOut(new PrintStream(out)); + void setUp() throws Exception { + System.setOut(new PrintStream(outputStreamCaptor)); + + // Reset LambdaHandlerProcessor's serviceName + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); } @AfterEach - void tearDown() { - System.setOut(originalOut); + void tearDown() throws Exception { + System.setOut(STANDARD_OUT); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); + field.setAccessible(true); + field.set(null, null); } @Test - public void metricsWithoutColdStart() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { - - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - - MetricsUtils.defaultDimensions(null); - requestHandler = new PowertoolsMetricsEnabledHandler(); - requestHandler.handleRequest("input", context); - - assertThat(out.toString().split("\n")) - .hasSize(2) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s[0]); - - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); - - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.has("test-metric")).isTrue(); + assertThat(rootNode.get("test-metric").asDouble()).isEqualTo(100.0); + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("CustomNamespace"); + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("CustomService"); } @Test - public void metricsWithDefaultDimensionSpecified() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { - - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - - requestHandler = new PowertoolsMetricsEnabledDefaultDimensionHandler(); - - requestHandler.handleRequest("input", context); - - assertThat(out.toString().split("\n")) - .hasSize(2) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s[0]); - - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsEntry("CustomDimension", "booking") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); - - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("CustomDimension", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace") + @SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService") + void shouldOverrideEnvironmentVariablesWithAnnotation() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("CustomNamespace"); + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("CustomService"); } @Test - public void metricsWithDefaultNoDimensionSpecified() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { - - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - - requestHandler = new PowertoolsMetricsEnabledDefaultNoDimensionHandler(); - - requestHandler.handleRequest("input", context); + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace") + @SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService") + void shouldUseEnvironmentVariablesWhenNoAnnotationOverrides() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("EnvNamespace"); + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("EnvService"); + } - assertThat(out.toString().split("\n")) - .hasSize(2) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s[0]); + @Test + void shouldCaptureColdStartMetricWhenConfigured() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithColdStartMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); + // When + handler.handleRequest(input, context); - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + String[] emfOutputs = emfOutput.split("\\n"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); + // There should be two EMF outputs - one for cold start and one for the handler metrics + assertThat(emfOutputs).hasSize(2); - logAsJson = readAsJson(s[1]); + JsonNode coldStartNode = objectMapper.readTree(emfOutputs[0]); + assertThat(coldStartNode.has("ColdStart")).isTrue(); + assertThat(coldStartNode.get("ColdStart").asDouble()).isEqualTo(1.0); - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + JsonNode metricsNode = objectMapper.readTree(emfOutputs[1]); + assertThat(metricsNode.has("test-metric")).isTrue(); } @Test - public void metricsWithColdStart() { - - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_FUNCTION_NAME", value = "EnvFunctionName") + void shouldNotIncludeServiceDimensionInColdStartMetricWhenServiceUndefined() throws Exception { + // Given - no service name set, so it will use the default undefined value + RequestHandler<Map<String, Object>, String> handler = new HandlerWithColdStartMetricsAnnotation(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); - MetricsUtils.defaultDimensions(null); - requestHandler = new PowertoolsMetricsColdStartEnabledHandler(); + // When + handler.handleRequest(input, context); - requestHandler.handleRequest("input", context); + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + String[] emfOutputs = emfOutput.split("\\n"); - assertThat(out.toString().split("\n")) - .hasSize(2) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s[0]); + // There should be two EMF outputs - one for cold start and one for the handler metrics + assertThat(emfOutputs).hasSize(2); - assertThat(logAsJson) - .doesNotContainKey("Metric1") - .containsEntry("ColdStart", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); + JsonNode coldStartNode = objectMapper.readTree(emfOutputs[0]); + assertThat(coldStartNode.has("ColdStart")).isTrue(); + assertThat(coldStartNode.get("ColdStart").asDouble()).isEqualTo(1.0); - logAsJson = readAsJson(s[1]); + // Service dimension should not be present in cold start metrics + assertThat(coldStartNode.has("Service")).isFalse(); - assertThat(logAsJson) - .doesNotContainKey("ColdStart") - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + // FunctionName dimension should be present + assertThat(coldStartNode.has("FunctionName")).isTrue(); + assertThat(coldStartNode.get("FunctionName").asText()).isEqualTo("EnvFunctionName"); } @Test - public void noColdStartMetricsWhenColdStartDone() { - - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - - MetricsUtils.defaultDimensions(null); - requestHandler = new PowertoolsMetricsColdStartEnabledHandler(); - - requestHandler.handleRequest("input", context); - requestHandler.handleRequest("input", context); - - assertThat(out.toString().split("\n")) - .hasSize(3) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s[0]); - - assertThat(logAsJson) - .doesNotContainKey("Metric1") - .containsEntry("ColdStart", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .doesNotContainKey("ColdStart") - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - - logAsJson = readAsJson(s[2]); - - assertThat(logAsJson) - .doesNotContainKey("ColdStart") - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithCustomFunctionName(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + String[] emfOutputs = emfOutput.split("\\n"); + + // There should be two EMF outputs - one for cold start and one for the handler metrics + assertThat(emfOutputs).hasSize(2); + + JsonNode coldStartNode = objectMapper.readTree(emfOutputs[0]); + assertThat(coldStartNode.has("FunctionName")).isTrue(); + assertThat(coldStartNode.get("FunctionName").asText()).isEqualTo("CustomFunction"); + + // Check that FunctionName is in the dimensions + JsonNode dimensions = coldStartNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); + boolean hasFunctionName = false; + for (JsonNode dimension : dimensions) { + if ("FunctionName".equals(dimension.asText())) { + hasFunctionName = true; + break; + } } + assertThat(hasFunctionName).isTrue(); } @Test - public void metricsWithStreamHandler() throws IOException { - - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); + void shouldUseServiceNameWhenProvidedForColdStartMetric() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithServiceNameAndColdStart(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(emfOutput); + + // Should use the service name from annotation + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("CustomService"); + } - MetricsUtils.defaultDimensions(null); - RequestStreamHandler streamHandler = new PowertoolsMetricsEnabledStreamHandler(); + @Test + void shouldHaveNoEffectOnNonHandlerMethod() { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithAnnotationOnWrongMethod(); + Context context = new TestLambdaContext(); + Map<String, Object> input = new HashMap<>(); - streamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); + // When + handler.handleRequest(input, context); - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); + // Then + String emfOutput = outputStreamCaptor.toString().trim(); - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + // Should be empty because we do not flush any metrics manually + assertThat(emfOutput).isEmpty(); } - @Test - public void exceptionWhenNoMetricsEmitted() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - - MetricsUtils.defaultDimensions(null); - requestHandler = new PowertoolsMetricsExceptionWhenNoMetricsHandler(); - - assertThatExceptionOfType(ValidationException.class) - .isThrownBy(() -> requestHandler.handleRequest("input", context)) - .withMessage("No metrics captured, at least one metrics must be emitted"); + static class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(namespace = "CustomNamespace", service = "CustomService") + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; } } - @Test - public void noExceptionWhenNoMetricsEmitted() { - - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - - MetricsUtils.defaultDimensions(null); - requestHandler = new PowertoolsMetricsNoExceptionWhenNoMetricsHandler(); - - requestHandler.handleRequest("input", context); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Service", "booking") - .doesNotContainKey("_aws"); - }); + static class HandlerWithDefaultMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; } } - @Test - public void allowWhenNoDimensionsSet() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - MetricsUtils.defaultDimensions(null); - - requestHandler = new PowertoolsMetricsNoDimensionsHandler(); - requestHandler.handleRequest("input", context); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - assertThat(logAsJson) - .containsEntry("CoolMetric", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + static class HandlerWithColdStartMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(captureColdStart = true, namespace = "TestNamespace") + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; } } - @Test - public void exceptionWhenTooManyDimensionsSet() { - - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - - MetricsUtils.defaultDimensions(null); - - requestHandler = new PowertoolsMetricsTooManyDimensionsHandler(); - - assertThatExceptionOfType(ValidationException.class) - .isThrownBy(() -> requestHandler.handleRequest("input", context)) - .withMessage("Number of Dimensions must be in range of 0-9. Actual size: 14."); + static class HandlerWithCustomFunctionName implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(captureColdStart = true, functionName = "CustomFunction", namespace = "TestNamespace") + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; } } - @Test - public void metricsPublishedEvenHandlerThrowsException() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - - MetricsUtils.defaultDimensions(null); - requestHandler = new PowertoolsMetricsWithExceptionInHandler(); - - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> requestHandler.handleRequest("input", context)) - .withMessage("Whoops, unexpected exception"); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - assertThat(logAsJson) - .containsEntry("CoolMetric", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + static class HandlerWithServiceNameAndColdStart implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(service = "CustomService", captureColdStart = true, namespace = "TestNamespace") + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; } } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - when(context.getAwsRequestId()).thenReturn("123ABC"); - } + static class HandlerWithAnnotationOnWrongMethod implements RequestHandler<Map<String, Object>, String> { + @Override + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + someOtherMethod(); + return "OK"; + } - private Map<String, Object> readAsJson(String s) { - try { - return mapper.readValue(s, Map.class); - } catch (JsonProcessingException e) { - e.printStackTrace(); + @FlushMetrics + public void someOtherMethod() { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); } - return emptyMap(); } } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptorTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptorTest.java new file mode 100644 index 000000000..673f9f7d4 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.metrics.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class MetricsUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/METRICS/"); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java new file mode 100644 index 000000000..848222bae --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java @@ -0,0 +1,277 @@ +package software.amazon.lambda.powertools.metrics.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.Instant; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mock.Strictness; +import org.mockito.junit.jupiter.MockitoExtension; + +import software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; + +/** + * Tests for RequestScopedMetricsProxy focusing on lazy vs eager initialization behavior. + * + * CRITICAL: These tests ensure configuration methods (setNamespace, setDefaultDimensions, + * setRaiseOnEmptyMetrics) do NOT eagerly create instances, while metrics operations + * (addMetric, addDimension, flush) DO eagerly create instances. + */ +@ExtendWith(MockitoExtension.class) +class RequestScopedMetricsProxyTest { + + @Mock(strictness = Strictness.LENIENT) + private MetricsProvider mockProvider; + + @Mock(strictness = Strictness.LENIENT) + private Metrics mockMetrics; + + private RequestScopedMetricsProxy proxy; + + @BeforeEach + void setUp() { + when(mockProvider.getMetricsInstance()).thenReturn(mockMetrics); + proxy = new RequestScopedMetricsProxy(mockProvider); + } + + @AfterEach + void tearDown() { + System.clearProperty(LambdaConstants.XRAY_TRACE_HEADER); + } + + // ========== LAZY INITIALIZATION TESTS (Configuration Methods) ========== + + @Test + void setNamespace_shouldNotEagerlyCreateInstance() { + // WHEN + proxy.setNamespace("TestNamespace"); + + // THEN - Provider should NOT be called (lazy initialization) + verify(mockProvider, never()).getMetricsInstance(); + } + + @Test + void setDefaultDimensions_shouldNotEagerlyCreateInstance() { + // WHEN + proxy.setDefaultDimensions(DimensionSet.of("key", "value")); + + // THEN - Provider should NOT be called (lazy initialization) + verify(mockProvider, never()).getMetricsInstance(); + } + + @Test + void setDefaultDimensions_shouldThrowExceptionWhenNull() { + // When/Then + assertThatThrownBy(() -> proxy.setDefaultDimensions(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("DimensionSet cannot be null"); + } + + @Test + void setRaiseOnEmptyMetrics_shouldNotEagerlyCreateInstance() { + // WHEN + proxy.setRaiseOnEmptyMetrics(true); + + // THEN - Provider should NOT be called (lazy initialization) + verify(mockProvider, never()).getMetricsInstance(); + } + + // ========== EAGER INITIALIZATION TESTS (Metrics Operations) ========== + + @Test + void addMetric_shouldEagerlyCreateInstance() { + // WHEN + proxy.addMetric("test", 1, MetricUnit.COUNT, MetricResolution.HIGH); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).addMetric("test", 1, MetricUnit.COUNT, MetricResolution.HIGH); + } + + @Test + void addDimension_shouldEagerlyCreateInstance() { + // WHEN + proxy.addDimension(DimensionSet.of("key", "value")); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).addDimension(any(DimensionSet.class)); + } + + @Test + void addMetadata_shouldEagerlyCreateInstance() { + // WHEN + proxy.addMetadata("key", "value"); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).addMetadata("key", "value"); + } + + @Test + void flush_shouldAlwaysCreateInstance() { + // WHEN + proxy.flush(); + + // THEN - Provider SHOULD be called even if no metrics added + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).flush(); + } + + // ========== CONFIGURATION APPLIED ON FIRST METRICS OPERATION ========== + + @Test + void firstMetricsOperation_shouldApplyStoredConfiguration() { + // GIVEN - Set configuration without creating instance + proxy.setNamespace("TestNamespace"); + proxy.setDefaultDimensions(DimensionSet.of("Service", "TestService")); + proxy.setRaiseOnEmptyMetrics(true); + verify(mockProvider, never()).getMetricsInstance(); + + // WHEN - First metrics operation + proxy.addMetric("test", 1, MetricUnit.COUNT); + + // THEN - Instance created and configuration applied + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).setNamespace("TestNamespace"); + verify(mockMetrics).setDefaultDimensions(any(DimensionSet.class)); + verify(mockMetrics).setRaiseOnEmptyMetrics(true); + verify(mockMetrics).addMetric("test", 1, MetricUnit.COUNT, MetricResolution.STANDARD); + } + + // ========== THREAD ISOLATION TESTS ========== + + @Test + void shouldShareInstanceAcrossThreadsWithSameTraceId() throws Exception { + // GIVEN - Set trace ID + System.setProperty(LambdaConstants.XRAY_TRACE_HEADER, "Root=1-test-trace-id"); + + // WHEN - Parent thread adds metric + proxy.addMetric("metric1", 1, MetricUnit.COUNT); + verify(mockProvider, times(1)).getMetricsInstance(); + + // WHEN - Child thread adds metric (same trace ID) + Thread thread2 = new Thread(() -> { + proxy.addMetric("metric2", 2, MetricUnit.COUNT); + }); + thread2.start(); + thread2.join(); + + // THEN - Only one instance created (same trace ID = shared instance) + verify(mockProvider, times(1)).getMetricsInstance(); + } + + @Test + void flush_shouldRemoveThreadLocalInstance() { + // GIVEN - Create instance + proxy.addMetric("test", 1, MetricUnit.COUNT); + verify(mockProvider, times(1)).getMetricsInstance(); + + // WHEN - Flush + proxy.flush(); + + // WHEN - Add another metric (should create new instance) + proxy.addMetric("test2", 2, MetricUnit.COUNT); + + // THEN - Provider called twice (instance was removed after flush) + verify(mockProvider, times(2)).getMetricsInstance(); + } + + // ========== EDGE CASES ========== + + @Test + void multipleConfigurationCalls_shouldUpdateAtomicReferences() { + // WHEN + proxy.setNamespace("Namespace1"); + proxy.setNamespace("Namespace2"); + proxy.setNamespace("Namespace3"); + + // THEN - No instance created + verify(mockProvider, never()).getMetricsInstance(); + + // WHEN - First metrics operation + proxy.addMetric("test", 1, MetricUnit.COUNT); + + // THEN - Only last namespace applied + verify(mockMetrics).setNamespace("Namespace3"); + verify(mockMetrics, never()).setNamespace("Namespace1"); + verify(mockMetrics, never()).setNamespace("Namespace2"); + } + + @Test + void configurationAfterInstanceCreation_shouldApplyImmediately() { + // GIVEN - Instance already created + proxy.addMetric("test", 1, MetricUnit.COUNT); + + // WHEN - Set configuration + proxy.setNamespace("NewNamespace"); + + // THEN - Applied immediately to existing instance + verify(mockMetrics).setNamespace("NewNamespace"); + } + + @Test + void setTimestamp_shouldEagerlyCreateInstance() { + // When + proxy.setTimestamp(Instant.now()); + + // Then + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).setTimestamp(any()); + } + + @Test + void getDefaultDimensions_shouldNotEagerlyCreateInstance() { + // WHEN + DimensionSet result = proxy.getDefaultDimensions(); + + // THEN - Provider should NOT be called (returns stored config or empty) + verify(mockProvider, never()).getMetricsInstance(); + assertThat(result).isNotNull(); + } + + @Test + void captureColdStartMetric_shouldEagerlyCreateInstance() { + // WHEN + proxy.captureColdStartMetric(DimensionSet.of("key", "value")); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).captureColdStartMetric(any(DimensionSet.class)); + } + + @Test + void flushMetrics_shouldEagerlyCreateInstance() { + // WHEN + proxy.flushMetrics(m -> m.addMetric("test", 1, MetricUnit.COUNT)); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).flushMetrics(any()); + } + + @Test + void clearDefaultDimensions_shouldEagerlyCreateInstance() { + // WHEN + proxy.clearDefaultDimensions(); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).clearDefaultDimensions(); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/ValidatorTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/ValidatorTest.java new file mode 100644 index 000000000..e5d780f9f --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/ValidatorTest.java @@ -0,0 +1,224 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.internal; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.Instant; + +import org.junit.jupiter.api.Test; + +class ValidatorTest { + + @Test + void shouldThrowExceptionWhenNamespaceIsNull() { + // When/Then + assertThatThrownBy(() -> Validator.validateNamespace(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Namespace must be specified before flushing metrics"); + } + + @Test + void shouldThrowExceptionWhenNamespaceIsEmpty() { + // When/Then + assertThatThrownBy(() -> Validator.validateNamespace("")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Namespace must be specified before flushing metrics"); + } + + @Test + void shouldThrowExceptionWhenNamespaceIsBlank() { + // When/Then + assertThatThrownBy(() -> Validator.validateNamespace(" ")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Namespace must be specified before flushing metrics"); + } + + @Test + void shouldThrowExceptionWhenNamespaceExceedsMaxLength() { + // Given + String tooLongNamespace = "a".repeat(256); + + // When/Then + assertThatThrownBy(() -> Validator.validateNamespace(tooLongNamespace)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Namespace exceeds maximum length of 255"); + } + + @Test + void shouldThrowExceptionWhenNamespaceContainsInvalidCharacters() { + // When/Then + assertThatThrownBy(() -> Validator.validateNamespace("Invalid Namespace")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Namespace contains invalid characters"); + } + + @Test + void shouldAcceptValidNamespace() { + // When/Then + assertThatCode(() -> Validator.validateNamespace("Valid.Namespace_123#/")) + .doesNotThrowAnyException(); + } + + @Test + void shouldThrowExceptionWhenDimensionKeyIsNull() { + // When/Then + assertThatThrownBy(() -> Validator.validateDimension(null, "Value")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension key cannot be null or empty"); + } + + @Test + void shouldThrowExceptionWhenDimensionKeyIsEmpty() { + // When/Then + assertThatThrownBy(() -> Validator.validateDimension("", "Value")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension key cannot be null or empty"); + } + + @Test + void shouldThrowExceptionWhenDimensionValueIsNull() { + // When/Then + assertThatThrownBy(() -> Validator.validateDimension("Key", null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension value cannot be null or empty"); + } + + @Test + void shouldThrowExceptionWhenDimensionValueIsEmpty() { + // When/Then + assertThatThrownBy(() -> Validator.validateDimension("Key", "")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension value cannot be null or empty"); + } + + @Test + void shouldThrowExceptionWhenDimensionKeyContainsWhitespace() { + // When/Then + assertThatThrownBy(() -> Validator.validateDimension("Key With Space", "Value")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension key cannot contain whitespaces: Key With Space"); + } + + @Test + void shouldThrowExceptionWhenDimensionValueContainsWhitespace() { + // When/Then + assertThatThrownBy(() -> Validator.validateDimension("Key", "Value With Space")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension value cannot contain whitespaces: Value With Space"); + } + + @Test + void shouldThrowExceptionWhenDimensionKeyStartsWithColon() { + // When/Then + assertThatThrownBy(() -> Validator.validateDimension(":Key", "Value")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension key cannot start with colon: :Key"); + } + + @Test + void shouldThrowExceptionWhenDimensionKeyExceedsMaxLength() { + // Given + String longKey = "a".repeat(251); // MAX_DIMENSION_NAME_LENGTH + 1 + + // When/Then + assertThatThrownBy(() -> Validator.validateDimension(longKey, "Value")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension name exceeds maximum length of 250: " + longKey); + } + + @Test + void shouldThrowExceptionWhenDimensionValueExceedsMaxLength() { + // Given + String longValue = "a".repeat(1025); // MAX_DIMENSION_VALUE_LENGTH + 1 + + // When/Then + assertThatThrownBy(() -> Validator.validateDimension("Key", longValue)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension value exceeds maximum length of 1024: " + longValue); + } + + @Test + void shouldThrowExceptionWhenDimensionKeyContainsNonAsciiCharacters() { + // Given + String keyWithNonAscii = "Key\u0080"; // Non-ASCII character + + // When/Then + assertThatThrownBy(() -> Validator.validateDimension(keyWithNonAscii, "Value")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension name has invalid characters: " + keyWithNonAscii); + } + + @Test + void shouldThrowExceptionWhenDimensionValueContainsNonAsciiCharacters() { + // Given + String valueWithNonAscii = "Value\u0080"; // Non-ASCII character + + // When/Then + assertThatThrownBy(() -> Validator.validateDimension("Key", valueWithNonAscii)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Dimension value has invalid characters: " + valueWithNonAscii); + } + + @Test + void shouldAcceptValidDimension() { + // When/Then + assertThatCode(() -> Validator.validateDimension("ValidKey", "ValidValue")) + .doesNotThrowAnyException(); + } + + @Test + void shouldThrowExceptionWhenTimestampIsNull() { + // When/Then + assertThatThrownBy(() -> Validator.validateTimestamp(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Timestamp cannot be null"); + } + + @Test + void shouldThrowExceptionWhenTimestampIsTooFarInFuture() { + // Given + Instant futureTooFar = Instant.now().plusSeconds(Validator.MAX_TIMESTAMP_FUTURE_AGE_SECONDS + 1); + + // When/Then + assertThatThrownBy(() -> Validator.validateTimestamp(futureTooFar)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Timestamp cannot be more than " + Validator.MAX_TIMESTAMP_FUTURE_AGE_SECONDS + + " seconds in the future"); + } + + @Test + void shouldThrowExceptionWhenTimestampIsTooFarInPast() { + // Given + Instant pastTooFar = Instant.now().minusSeconds(Validator.MAX_TIMESTAMP_PAST_AGE_SECONDS + 1); + + // When/Then + assertThatThrownBy(() -> Validator.validateTimestamp(pastTooFar)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Timestamp cannot be more than " + Validator.MAX_TIMESTAMP_PAST_AGE_SECONDS + + " seconds in the past"); + } + + @Test + void shouldAcceptValidTimestamp() { + // Given + Instant validTimestamp = Instant.now(); + + // When/Then + assertThatCode(() -> Validator.validateTimestamp(validTimestamp)) + .doesNotThrowAnyException(); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/model/DimensionSetTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/model/DimensionSetTest.java new file mode 100644 index 000000000..ac059f117 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/model/DimensionSetTest.java @@ -0,0 +1,166 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +class DimensionSetTest { + + @Test + void shouldCreateEmptyDimensionSet() { + // When + DimensionSet dimensionSet = DimensionSet.of(Collections.emptyMap()); + + // Then + assertThat(dimensionSet.getDimensions()).isEmpty(); + assertThat(dimensionSet.getDimensionKeys()).isEmpty(); + } + + @Test + void shouldCreateDimensionSetWithSingleKeyValue() { + // When + DimensionSet dimensionSet = DimensionSet.of("Key", "Value"); + + // Then + assertThat(dimensionSet.getDimensions()).containsExactly(Map.entry("Key", "Value")); + assertThat(dimensionSet.getDimensionKeys()).containsExactly("Key"); + } + + @Test + void shouldCreateDimensionSetWithTwoKeyValues() { + // When + DimensionSet dimensionSet = DimensionSet.of("Key1", "Value1", "Key2", "Value2"); + + // Then + assertThat(dimensionSet.getDimensions()) + .containsEntry("Key1", "Value1") + .containsEntry("Key2", "Value2"); + assertThat(dimensionSet.getDimensionKeys()).containsExactly("Key1", "Key2"); + } + + @Test + void shouldCreateDimensionSetWithThreeKeyValues() { + // When + DimensionSet dimensionSet = DimensionSet.of( + "Key1", "Value1", + "Key2", "Value2", + "Key3", "Value3"); + + // Then + assertThat(dimensionSet.getDimensions()) + .containsEntry("Key1", "Value1") + .containsEntry("Key2", "Value2") + .containsEntry("Key3", "Value3"); + assertThat(dimensionSet.getDimensionKeys()).containsExactly("Key1", "Key2", "Key3"); + } + + @Test + void shouldCreateDimensionSetWithFourKeyValues() { + // When + DimensionSet dimensionSet = DimensionSet.of( + "Key1", "Value1", + "Key2", "Value2", + "Key3", "Value3", + "Key4", "Value4"); + + // Then + assertThat(dimensionSet.getDimensions()) + .containsEntry("Key1", "Value1") + .containsEntry("Key2", "Value2") + .containsEntry("Key3", "Value3") + .containsEntry("Key4", "Value4"); + assertThat(dimensionSet.getDimensionKeys()).containsExactly("Key1", "Key2", "Key3", "Key4"); + } + + @Test + void shouldCreateDimensionSetWithFiveKeyValues() { + // When + DimensionSet dimensionSet = DimensionSet.of( + "Key1", "Value1", + "Key2", "Value2", + "Key3", "Value3", + "Key4", "Value4", + "Key5", "Value5"); + + // Then + assertThat(dimensionSet.getDimensions()) + .containsEntry("Key1", "Value1") + .containsEntry("Key2", "Value2") + .containsEntry("Key3", "Value3") + .containsEntry("Key4", "Value4") + .containsEntry("Key5", "Value5"); + assertThat(dimensionSet.getDimensionKeys()).containsExactly("Key1", "Key2", "Key3", "Key4", "Key5"); + } + + @Test + void shouldCreateDimensionSetFromMap() { + // Given + Map<String, String> dimensions = Map.of( + "Key1", "Value1", + "Key2", "Value2"); + + // When + DimensionSet dimensionSet = DimensionSet.of(dimensions); + + // Then + assertThat(dimensionSet.getDimensions()).isEqualTo(dimensions); + assertThat(dimensionSet.getDimensionKeys()).containsExactlyInAnyOrder("Key1", "Key2"); + } + + @Test + void shouldGetDimensionValue() { + // Given + DimensionSet dimensionSet = DimensionSet.of("Key1", "Value1", "Key2", "Value2"); + + // When + String value = dimensionSet.getDimensionValue("Key1"); + + // Then + assertThat(value).isEqualTo("Value1"); + } + + @Test + void shouldReturnNullForNonExistentDimension() { + // Given + DimensionSet dimensionSet = DimensionSet.of("Key1", "Value1"); + + // When + String value = dimensionSet.getDimensionValue("NonExistentKey"); + + // Then + assertThat(value).isNull(); + } + + @Test + void shouldThrowExceptionWhenExceedingMaxDimensions() { + // Given + // Create a dimension set with 30 dimensions (30 is maximum) + DimensionSet dimensionSet = new DimensionSet(); + for (int i = 1; i <= 30; i++) { + dimensionSet.addDimension("Key" + i, "Value" + i); + } + + // When/Then + assertThatThrownBy(() -> dimensionSet.addDimension("Key31", "Value31")) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Cannot exceed 30 dimensions per dimension set"); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProviderTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProviderTest.java new file mode 100644 index 000000000..2b2268ea8 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProviderTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.provider; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.internal.EmfMetricsLogger; + +class EmfMetricsProviderTest { + + @Test + void shouldCreateEmfMetricsLogger() { + // Given + EmfMetricsProvider provider = new EmfMetricsProvider(); + + // When + Metrics metrics = provider.getMetricsInstance(); + + // Then + assertThat(metrics) + .isNotNull() + .isInstanceOf(EmfMetricsLogger.class); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetrics.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetrics.java new file mode 100644 index 000000000..4a2e33a78 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetrics.java @@ -0,0 +1,91 @@ +package software.amazon.lambda.powertools.metrics.testutils; + +import java.time.Instant; +import java.util.Collections; +import java.util.function.Consumer; + +import com.amazonaws.services.lambda.runtime.Context; + +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +public class TestMetrics implements Metrics { + @Override + public void addMetric(String name, double value, MetricUnit unit) { + // Test placeholder + } + + @Override + public void flush() { + // Test placeholder + } + + @Override + public void addMetric(String key, double value, MetricUnit unit, MetricResolution resolution) { + // Test placeholder + } + + @Override + public void addDimension(DimensionSet dimensionSet) { + // Test placeholder + } + + @Override + public void addMetadata(String key, Object value) { + // Test placeholder + } + + @Override + public void setDefaultDimensions(DimensionSet dimensionSet) { + // Test placeholder + } + + @Override + public DimensionSet getDefaultDimensions() { + // Test placeholder + return DimensionSet.of(Collections.emptyMap()); + } + + @Override + public void setNamespace(String namespace) { + // Test placeholder + } + + @Override + public void setRaiseOnEmptyMetrics(boolean raiseOnEmptyMetrics) { + // Test placeholder + } + + @Override + public void clearDefaultDimensions() { + // Test placeholder + } + + @Override + public void setTimestamp(Instant timestamp) { + // Test placeholder + } + + @Override + public void captureColdStartMetric(Context context, DimensionSet dimensions) { + // Test placeholder + } + + @Override + public void captureColdStartMetric(DimensionSet dimensions) { + // Test placeholder + } + + @Override + public void flushMetrics(Consumer<Metrics> metricsConsumer) { + // Test placeholder + } + + @Override + public void flushSingleMetric(String name, double value, MetricUnit unit, String namespace, + DimensionSet dimensions) { + // Test placeholder + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetricsProvider.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetricsProvider.java new file mode 100644 index 000000000..4a5fc23dd --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetricsProvider.java @@ -0,0 +1,11 @@ +package software.amazon.lambda.powertools.metrics.testutils; + +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; + +public class TestMetricsProvider implements MetricsProvider { + @Override + public Metrics getMetricsInstance() { + return new TestMetrics(); + } +} diff --git a/powertools-metrics/src/test/resources/simplelogger.properties b/powertools-metrics/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..6c626ab4d --- /dev/null +++ b/powertools-metrics/src/test/resources/simplelogger.properties @@ -0,0 +1,7 @@ +org.slf4j.simpleLogger.logFile=target/metrics-test.log +org.slf4j.simpleLogger.defaultLogLevel=warn +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS +org.slf4j.simpleLogger.showThreadName=false +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index fab72a9b7..124e22186 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -14,101 +14,40 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> <artifactId>powertools-parameters</artifactId> - <name>Powertools for AWS Lambda (Java) library Parameters</name> - - <description> - Set of utilities to retrieve parameters from Secrets Manager or SSM Parameter Store - </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> + <name>Powertools for AWS Lambda (Java) - Parameters</name> + <description>Set of utilities to retrieve parameters - common interface</description> <dependencies> <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>ssm</artifactId> - <exclusions> - <exclusion> - <groupId>software.amazon.awssdk</groupId> - <artifactId>apache-client</artifactId> - </exclusion> - <exclusion> - <groupId>software.amazon.awssdk</groupId> - <artifactId>netty-nio-client</artifactId> - </exclusion> - </exclusions> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> </dependency> <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>secretsmanager</artifactId> - <exclusions> - <exclusion> - <groupId>software.amazon.awssdk</groupId> - <artifactId>apache-client</artifactId> - </exclusion> - <exclusion> - <groupId>software.amazon.awssdk</groupId> - <artifactId>netty-nio-client</artifactId> - </exclusion> - </exclusions> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>dynamodb</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>appconfigdata</artifactId> - </dependency> - <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - <scope>compile</scope> - </dependency> + <!-- Test dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> @@ -120,16 +59,6 @@ <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> @@ -151,17 +80,68 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> </environmentVariables> </configuration> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> </plugins> </build> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-parameters</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml new file mode 100644 index 000000000..406f715d3 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parent</artifactId> + <version>2.9.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>powertools-parameters-appconfig</artifactId> + <name>Powertools for AWS Lambda (Java) library Parameters - AppConfig</name> + <description>AppConfig implementation for the Parameters module</description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>appconfigdata</artifactId> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>apache-client</artifactId> + </exclusion> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>netty-nio-client</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + + <!-- Required explicitly for @Captor ArgumentCaptor --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-parameters-appconfig</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_REGION>eu-central-1</AWS_REGION> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParam.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParam.java new file mode 100644 index 000000000..eb959be76 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParam.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.appconfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import software.amazon.lambda.powertools.parameters.transform.Transformer; + +/** + * Use this annotation to inject AWS AppConfig parameters into fields in your application. You + * can also use {@code AppConfigProviderBuilder} to obtain AppConfig values directly, rather than + * injecting them implicitly. + * Both {@code environment} and {@code application} fields are necessary. + * + * @see AppConfigProviderBuilder + * @see <a href="https://docs.aws.amazon.com/appconfig>AWS AppConfig</a> + * @see <a href="https://docs.powertools.aws.dev/lambda/java/utilities/parameters/">Powertools for AWS Lambda (Java) parameters documentation</a> + * + * <pre> + * @AppConfigParam(key = "my-param", environment = "my-env", application = "my-app") + * String appConfigParam; + * </pre> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface AppConfigParam { + String key(); + + /** + * <b>Mandatory</b>. Provide an environment to the {@link AppConfigProvider} + */ + String environment(); + + /** + * <b>Mandatory</b>. Provide an application to the {@link AppConfigProvider} + */ + String application(); + + /** + * <b>Optional</b> Provide a Transformer to transform the returned parameter values. + */ + Class<? extends Transformer> transformer() default Transformer.class; +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParametersAspect.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParametersAspect.java new file mode 100644 index 000000000..7ab4cf23e --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParametersAspect.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.appconfig; + +import java.util.function.BiFunction; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; + +/** + * Provides the AppConfig parameter aspect. This aspect is responsible for injecting + * parameters from AWS AppConfig into fields annotated with @AppConfigParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class AppConfigParametersAspect extends BaseParamAspect { + + private static BiFunction<String, String, AppConfigProvider> providerBuilder = + (String env, String app) -> AppConfigProvider.builder() + .withEnvironment(env) + .withApplication(app) + .build(); + + + @Pointcut("get(* *) && @annotation(appConfigParamAnnotation)") + public void getParam(AppConfigParam appConfigParamAnnotation) { + } + + @Around("getParam(appConfigParamAnnotation)") + public Object injectParam(final ProceedingJoinPoint joinPoint, final AppConfigParam appConfigParamAnnotation) { + + AppConfigProvider provider = providerBuilder.apply + (appConfigParamAnnotation.environment(), appConfigParamAnnotation.application()); + + return getAndTransform(appConfigParamAnnotation.key(), appConfigParamAnnotation.transformer(), provider, + (FieldSignature) joinPoint.getSignature()); + } + +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java new file mode 100644 index 000000000..06d00ffbe --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java @@ -0,0 +1,131 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.appconfig; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; +import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest; +import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationResponse; +import software.amazon.awssdk.services.appconfigdata.model.StartConfigurationSessionRequest; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.ParamProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Implements a {@link ParamProvider} on top of the AppConfig service. AppConfig provides + * a mechanism to retrieve and update configuration of applications over time. + * AppConfig requires the user to create an application, environment, and configuration profile. + * The configuration profile's value can then be retrieved, by key name, through this provider. + * <p> + * Because AppConfig is designed to handle rollouts of configuration over time, we must first + * establish a session for each key we wish to retrieve, and then poll the session for the latest + * value when the user re-requests it. This means we must hold a keyed set of session tokens + * and values. + * + * @see <a href="https://docs.powertools.aws.dev/lambda/java/utilities/parameters/">Parameters provider documentation</a> + * @see <a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-working.html">AppConfig documentation</a> + */ +public class AppConfigProvider extends BaseProvider { + + private final AppConfigDataClient client; + private final String application; + private final String environment; + private final Map<String, EstablishedSession> establishedSessions = new ConcurrentHashMap<>(); + + AppConfigProvider(CacheManager cacheManager, TransformationManager transformationManager, + AppConfigDataClient client, String environment, String application) { + super(cacheManager, transformationManager); + this.client = client; + this.application = application; + this.environment = environment; + } + + /** + * Create a builder that can be used to configure and create a {@link AppConfigProvider}. + * + * @return a new instance of {@link AppConfigProviderBuilder} + */ + public static AppConfigProviderBuilder builder() { + return new AppConfigProviderBuilder(); + } + + /** + * Retrieve the parameter value from the AppConfig parameter store.<br /> + * + * @param key key of the parameter. This ties back to AppConfig's 'profile' concept + * @return the value of the parameter identified by the key + */ + @Override + protected String getValue(String key) { + // Start a configuration session if we don't already have one for the key requested + // so that we can the initial token. If we already have a session, we can take + // the next request token from there. + EstablishedSession establishedSession = establishedSessions.getOrDefault(key, null); + String sessionToken = establishedSession != null ? establishedSession.nextSessionToken + : client.startConfigurationSession(StartConfigurationSessionRequest.builder() + .applicationIdentifier(this.application) + .environmentIdentifier(this.environment) + .configurationProfileIdentifier(key) + .build()) + .initialConfigurationToken(); + + // Get the configuration using the token + GetLatestConfigurationResponse response = client.getLatestConfiguration(GetLatestConfigurationRequest.builder() + .configurationToken(sessionToken) + .build()); + + // Get the next session token we'll use next time we are asked for this key + String nextSessionToken = response.nextPollConfigurationToken(); + + // Get the value of the key. Note that AppConfig will return an empty value if the configuration has not changed + // since we last asked for it in this session - in this case we return the value we stashed at last request. + // https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-code-samples-using-API-read-configuration.html + SdkBytes configFromApi = response.configuration(); + String value; + if (configFromApi != null && configFromApi.asByteArray().length != 0) { + value = configFromApi.asUtf8String(); + } else if (establishedSession != null) { + value = establishedSession.lastConfigurationValue; + } else { + value = null; + } + // Update the cache so we can get the next value later + establishedSessions.put(key, new EstablishedSession(nextSessionToken, value)); + + return value; + } + + @Override + protected Map<String, String> getMultipleValues(String path) { + // Retrieving multiple values is not supported with the AppConfig provider. + throw new RuntimeException( + "Retrieving multiple parameter values is not supported with the AWS App Config Provider"); + } + + private static final class EstablishedSession { + private final String nextSessionToken; + private final String lastConfigurationValue; + + private EstablishedSession(String nextSessionToken, String value) { + this.nextSessionToken = nextSessionToken; + this.lastConfigurationValue = value; + } + } + +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java new file mode 100644 index 000000000..b6f6cd809 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java @@ -0,0 +1,126 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.appconfig; + +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.ParamProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Implements a {@link ParamProvider} on top of the AppConfig service. AppConfig provides + */ +public class AppConfigProviderBuilder { + private AppConfigDataClient client; + private CacheManager cacheManager; + private TransformationManager transformationManager; + private String environment; + private String application; + + /** + * Create a {@link AppConfigProvider} instance. + * + * @return a {@link AppConfigProvider} + */ + public AppConfigProvider build() { + if (cacheManager == null) { + cacheManager = new CacheManager(); + } + if (environment == null) { + throw new IllegalStateException("No environment provided; please provide one"); + } + if (application == null) { + throw new IllegalStateException("No application provided; please provide one"); + } + if (transformationManager == null) { + transformationManager = new TransformationManager(); + } + // Create a AppConfigDataClient if we haven't been given one + if (client == null) { + client = AppConfigDataClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(BaseProvider.PARAMETERS)).build()) + .build(); + } + + return new AppConfigProvider(cacheManager, transformationManager, client, environment, application); + } + + /** + * Set custom {@link AppConfigDataClient} to pass to the {@link AppConfigProvider}. <br/> + * Use it if you want to customize the region or any other part of the client. + * + * @param client Custom client + * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) + */ + public AppConfigProviderBuilder withClient(AppConfigDataClient client) { + this.client = client; + return this; + } + + /** + * <b>Mandatory</b>. Provide an environment to the {@link AppConfigProvider} + * + * @param environment the AppConfig environment + * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) + */ + public AppConfigProviderBuilder withEnvironment(String environment) { + this.environment = environment; + return this; + } + + /** + * <b>Mandatory</b>. Provide an application to the {@link AppConfigProvider} + * + * @param application the application to pull configuration from + * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) + */ + public AppConfigProviderBuilder withApplication(String application) { + this.application = application; + return this; + } + + /** + * Provide a CacheManager to the {@link AppConfigProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) + */ + public AppConfigProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * Provide a transformationManager to the {@link AppConfigProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) + */ + public AppConfigProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptor.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptor.java new file mode 100644 index 000000000..01fc8d096 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.appconfig.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-parameters-appconfig module is on the classpath. + */ +public final class ParametersAppconfigUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("parameters-appconfig"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/jni-config.json b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/jni-config.json new file mode 100644 index 000000000..2c4de0562 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/jni-config.json @@ -0,0 +1,26 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.String", + "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"org.apache.maven.surefire.booter.ForkedBooter", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"sun.instrument.InstrumentationImpl", + "methods":[{"name":"<init>","parameterTypes":["long","boolean","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/reflect-config.json b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/reflect-config.json new file mode 100644 index 000000000..b9ae934d6 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/reflect-config.json @@ -0,0 +1,369 @@ +[ +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ARCFOURCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESedeCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String"] }, {"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedParameterizedType", + "methods":[{"name":"getAnnotatedActualTypeArguments","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedTypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedWildcardType", + "methods":[{"name":"getAnnotatedUpperBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.TypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.security.AlgorithmParametersSpi" +}, +{ + "name":"java.security.KeyStoreSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"javax.security.auth.x500.X500Principal", + "fields":[{"name":"thisX500Name"}], + "methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.Unit" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apache.maven.surefire.junitplatform.JUnitPlatformProvider", + "methods":[{"name":"<init>","parameterTypes":["org.apache.maven.surefire.api.provider.ProviderParameters"] }] +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.junit.internal.AssumptionViolatedException" +}, +{ + "name":"org.junit.jupiter.api.Test", + "queryAllPublicMethods":true +}, +{ + "name":"org.junit.platform.commons.annotation.Testable", + "queryAllPublicMethods":true +}, +{ + "name":"org.junit.platform.launcher.LauncherSession", + "methods":[{"name":"getLauncher","parameterTypes":[] }] +}, +{ + "name":"org.junit.platform.launcher.core.LauncherFactory", + "methods":[{"name":"openSession","parameterTypes":[] }] +}, +{ + "name":"scala.util.Properties" +}, +{ + "name":"software.amazon.awssdk.awscore.AwsClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.awssdk.core.SdkClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"serviceName","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.services.appconfigdata.AppConfigDataClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getLatestConfiguration","parameterTypes":["java.util.function.Consumer"] }, {"name":"getLatestConfiguration","parameterTypes":["software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest"] }, {"name":"serviceClientConfiguration","parameterTypes":[] }, {"name":"startConfigurationSession","parameterTypes":["java.util.function.Consumer"] }, {"name":"startConfigurationSession","parameterTypes":["software.amazon.awssdk.services.appconfigdata.model.StartConfigurationSessionRequest"] }] +}, +{ + "name":"software.amazon.awssdk.utils.SdkAutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"close","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.BaseProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"get","parameterTypes":["java.lang.String"] }, {"name":"get","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"getMultiple","parameterTypes":["java.lang.String"] }, {"name":"now","parameterTypes":[] }, {"name":"resetToDefaults","parameterTypes":[] }, {"name":"withMaxAge","parameterTypes":["int","java.time.temporal.ChronoUnit"] }, {"name":"withTransformation","parameterTypes":["java.lang.Class"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.ParamProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.lambda.powertools.parameters.appconfig.AppConfigParamAspectTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"parameterInjectedByProvider","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.appconfig.AppConfigParamAspectTest$MyInjectedClass", + "fields":[{"name":"myParameter"}] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.appconfig.AppConfigParametersAspect", + "fields":[{"name":"providerBuilder"}] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.appconfig.AppConfigProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getMultipleValues","parameterTypes":["java.lang.String"] }, {"name":"getValue","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.appconfig.AppConfigProviderTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getMultipleValuesThrowsException","parameterTypes":[] }, {"name":"getValueNoValueExists","parameterTypes":[] }, {"name":"getValueRetrievesValue","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"multipleKeysRetrievalWorks","parameterTypes":[] }, {"name":"testAppConfigProviderBuilderMissingApplication_throwsException","parameterTypes":[] }, {"name":"testAppConfigProviderBuilderMissingEnvironment_throwsException","parameterTypes":[] }, {"name":"testAppConfigProvider_withoutParameter_shouldHaveDefaultTransformationManager","parameterTypes":[] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.IssuerAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.NetscapeCertTypeExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.PrivateKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.appconfig.internal.ParametersAppconfigUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/resource-config.json b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/resource-config.json new file mode 100644 index 000000000..63bcd679e --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/resource-config.json @@ -0,0 +1,59 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.configuration.Configuration\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.presentation.Representation\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.engine.TestEngine\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherSessionListener\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.PostDiscoveryFilter\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qjunit-platform.properties\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.AnnotationEngine\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.DoNotMockEnforcer\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.InstantiatorProvider2\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.MemberAccessor\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.MockMaker\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.MockResolver\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.MockitoLogger\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.PluginSwitch\\E" + }, { + "pattern":"\\Qmockito-extensions/org.mockito.plugins.StackTraceCleanerProvider\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/services/appconfigdata/execution.interceptors\\E" + }, { + "pattern":"\\Qversion.properties\\E" + }, { + "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/nfc.nrm\\E" + }]}, + "bundles":[] +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..c37ecc083 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.parameters.appconfig.internal.ParametersAppconfigUserAgentInterceptor diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java new file mode 100644 index 000000000..a32cc20a5 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.appconfig; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.BiFunction; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class AppConfigParamAspectTest { + + @Test + void parameterInjectedByProvider() throws Exception { + // Setup our aspect to return a mocked AppConfigProvider + String environment = "myEnvironment"; + String appName = "myApp"; + String key = "myKey"; + String value = "myValue"; + AppConfigProvider provider = Mockito.mock(AppConfigProvider.class); + BiFunction<String, String, AppConfigProvider> providerBuilder = (String env, String app) -> { + if (env.equals(environment) && app.equals(appName)) { + return provider; + } + throw new RuntimeException("Whoops! Asked for an app/env that we weren't configured for"); + }; + writeStaticField(AppConfigParametersAspect.class, "providerBuilder", providerBuilder, true); + + // Setup our mocked AppConfigProvider to return a value for our test data + Mockito.when(provider.get(key)).thenReturn(value); + + // Create an instance of a class and let the AppConfigParametersAspect inject it + MyInjectedClass obj = new MyInjectedClass(); + assertThat(obj.myParameter).isEqualTo(value); + } + + class MyInjectedClass { + @AppConfigParam(application = "myApp", environment = "myEnvironment", key = "myKey") + public String myParameter; + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java similarity index 76% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java rename to powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java index f467dca72..7f06ed412 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java @@ -12,19 +12,23 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.appconfig; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; -import static org.mockito.MockitoAnnotations.openMocks; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest; @@ -34,11 +38,12 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -public class AppConfigProviderTest { +@ExtendWith(MockitoExtension.class) +class AppConfigProviderTest { - private final String environmentName = "test"; - private final String applicationName = "fakeApp"; - private final String defaultTestKey = "key1"; + private static final String ENVIRONMENT_NAME = "test"; + private static final String DEFAULT_TEST_KEY = "key1"; + private static final String APPLICATION_NAME = "fakeApp"; @Mock AppConfigDataClient client; @@ -51,19 +56,16 @@ public class AppConfigProviderTest { private AppConfigProvider provider; @BeforeEach - public void init() { - openMocks(this); - + void init() { provider = AppConfigProvider.builder() .withClient(client) - .withApplication(applicationName) - .withEnvironment(environmentName) + .withApplication(APPLICATION_NAME) + .withEnvironment(ENVIRONMENT_NAME) .withCacheManager(new CacheManager()) .withTransformationManager(new TransformationManager()) .build(); } - /** * Tests repeated calls to the AppConfigProvider for the same key behave correctly. This is more complicated than * it seems, as the service itself will return no-data if the value of a property remains unchanged since the @@ -71,7 +73,7 @@ public void init() { * subsequent calls should once again return the new data. */ @Test - public void getValueRetrievesValue() { + void getValueRetrievesValue() { // Arrange StartConfigurationSessionResponse firstSession = StartConfigurationSessionResponse.builder() .initialConfigurationToken("token1") @@ -90,24 +92,32 @@ public void getValueRetrievesValue() { GetLatestConfigurationResponse thirdResponse = GetLatestConfigurationResponse.builder() .nextPollConfigurationToken("token4") .build(); + // Forth response returns empty, which means the provider should yield the previous value again + GetLatestConfigurationResponse forthResponse = GetLatestConfigurationResponse.builder() + .nextPollConfigurationToken("token5") + .configuration(SdkBytes.fromUtf8String("")) + .build(); Mockito.when(client.startConfigurationSession(startSessionRequestCaptor.capture())) .thenReturn(firstSession); Mockito.when(client.getLatestConfiguration(getLatestConfigurationRequestCaptor.capture())) - .thenReturn(firstResponse, secondResponse, thirdResponse); + .thenReturn(firstResponse, secondResponse, thirdResponse, forthResponse); // Act - String returnedValue1 = provider.getValue(defaultTestKey); - String returnedValue2 = provider.getValue(defaultTestKey); - String returnedValue3 = provider.getValue(defaultTestKey); + String returnedValue1 = provider.getValue(DEFAULT_TEST_KEY); + String returnedValue2 = provider.getValue(DEFAULT_TEST_KEY); + String returnedValue3 = provider.getValue(DEFAULT_TEST_KEY); + String returnedValue4 = provider.getValue(DEFAULT_TEST_KEY); // Assert assertThat(returnedValue1).isEqualTo(firstResponse.configuration().asUtf8String()); assertThat(returnedValue2).isEqualTo(secondResponse.configuration().asUtf8String()); assertThat(returnedValue3).isEqualTo(secondResponse.configuration() .asUtf8String()); // Third response is mocked to return null and should re-use previous value - assertThat(startSessionRequestCaptor.getValue().applicationIdentifier()).isEqualTo(applicationName); - assertThat(startSessionRequestCaptor.getValue().environmentIdentifier()).isEqualTo(environmentName); - assertThat(startSessionRequestCaptor.getValue().configurationProfileIdentifier()).isEqualTo(defaultTestKey); + assertThat(returnedValue4).isEqualTo(secondResponse.configuration() + .asUtf8String()); // Forth response is mocked to return empty and should re-use previous value + assertThat(startSessionRequestCaptor.getValue().applicationIdentifier()).isEqualTo(APPLICATION_NAME); + assertThat(startSessionRequestCaptor.getValue().environmentIdentifier()).isEqualTo(ENVIRONMENT_NAME); + assertThat(startSessionRequestCaptor.getValue().configurationProfileIdentifier()).isEqualTo(DEFAULT_TEST_KEY); assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo( firstSession.initialConfigurationToken()); assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo( @@ -117,8 +127,7 @@ public void getValueRetrievesValue() { } @Test - public void getValueNoValueExists() { - + void getValueNoValueExists() { // Arrange StartConfigurationSessionResponse session = StartConfigurationSessionResponse.builder() .initialConfigurationToken("token1") @@ -132,11 +141,10 @@ public void getValueNoValueExists() { .thenReturn(response); // Act - String returnedValue = provider.getValue(defaultTestKey); - + String returnedValue = provider.getValue(DEFAULT_TEST_KEY); // Assert - assertThat(returnedValue).isEqualTo(null); + assertThat(returnedValue).isNull(); } /** @@ -144,7 +152,7 @@ public void getValueNoValueExists() { * work as expected. This means two separate configuration sessions should be established with AppConfig. */ @Test - public void multipleKeysRetrievalWorks() { + void multipleKeysRetrievalWorks() { // Arrange String param1Key = "key1"; StartConfigurationSessionResponse param1Session = StartConfigurationSessionResponse.builder() @@ -182,50 +190,45 @@ public void multipleKeysRetrievalWorks() { param1Session.initialConfigurationToken()); assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo( param2Session.initialConfigurationToken()); - } @Test - public void getMultipleValuesThrowsException() { - + void getMultipleValuesThrowsException() { // Act & Assert assertThatRuntimeException().isThrownBy(() -> provider.getMultipleValues("path")) .withMessage("Retrieving multiple parameter values is not supported with the AWS App Config Provider"); } @Test - public void testAppConfigProviderBuilderMissingCacheManager_throwsException() { - + void testAppConfigProviderBuilderMissingEnvironment_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() - .withEnvironment(environmentName) - .withApplication(applicationName) - .withClient(client) - .build()) - .withMessage("No CacheManager provided; please provide one"); + .withCacheManager(new CacheManager()) + .withApplication(APPLICATION_NAME) + .withClient(client) + .build()) + .withMessage("No environment provided; please provide one"); } @Test - public void testAppConfigProviderBuilderMissingEnvironment_throwsException() { - + void testAppConfigProviderBuilderMissingApplication_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() - .withCacheManager(new CacheManager()) - .withApplication(applicationName) - .withClient(client) - .build()) - .withMessage("No environment provided; please provide one"); + .withCacheManager(new CacheManager()) + .withEnvironment(ENVIRONMENT_NAME) + .withClient(client) + .build()) + .withMessage("No application provided; please provide one"); } @Test - public void testAppConfigProviderBuilderMissingApplication_throwsException() { - - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() - .withCacheManager(new CacheManager()) - .withEnvironment(environmentName) - .withClient(client) - .build()) - .withMessage("No application provided; please provide one"); + void testAppConfigProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + // Act + AppConfigProvider appConfigProvider = AppConfigProvider.builder() + .withEnvironment("test") + .withApplication("app") + .build(); + // Assert + assertDoesNotThrow(() -> appConfigProvider.withTransformation(json)); } } diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptorTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptorTest.java new file mode 100644 index 000000000..124b71ef3 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.appconfig.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class ParametersAppconfigUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/PARAMETERS-APPCONFIG/"); + } +} \ No newline at end of file diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml new file mode 100644 index 000000000..eb5604046 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parent</artifactId> + <version>2.9.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>powertools-parameters-dynamodb</artifactId> + <name>Powertools for AWS Lambda (Java) library Parameters - DynamoDB</name> + <description>DynamoDB implementation for the Parameters module</description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>dynamodb</artifactId> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>apache-client</artifactId> + </exclusion> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>netty-nio-client</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + + <!-- Required explicitly for @Captor ArgumentCaptor --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-parameters-dynamodb</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_REGION>eu-central-1</AWS_REGION> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParam.java similarity index 52% rename from powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java rename to powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParam.java index 7ffb0310c..946786cb4 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParam.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.dynamodb; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -21,25 +21,31 @@ import software.amazon.lambda.powertools.parameters.transform.Transformer; /** - * {@code Param} is used to signal that the annotated field should be - * populated with a value retrieved from a parameter store through a {@link ParamProvider}. + * Inject a parameter from the DynamoDB Parameter Store into a field. You can also use + * {@code DynamoDbProviderBuilder} to obtain DynamoDB values directly, rather than injecting them implicitly. * - * <p>By default {@code Param} use {@link SSMProvider} as parameter provider. This can be overridden specifying - * the annotation variable {@code Param(provider = <Class-of-the-provider>)}.<br/> - * The library provide a provider for AWS System Manager Parameters Store ({@link SSMProvider}) and a provider - * for AWS Secrets Manager ({@link SecretsProvider}). - * The user can implement a custom provider by extending the abstract class {@link BaseProvider}.</p> - * - * <p>If the parameter value requires transformation before being assigned to the annotated field - * users can specify a {@link Transformer} - * </p> + * Usage: + * <pre> + * @DynamoDbParam(key = "my-param", table = "my-table") + * String myParameter; + * </pre> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) -public @interface Param { +public @interface DynamoDbParam { + /** + * <b>Mandatory</b>. Partition key from the DynamoDB table + */ String key(); - Class<? extends BaseProvider> provider() default SSMProvider.class; + /** + * <b>Mandatory</b>. Table name for the DynamoDB table + * @return + */ + String table(); + /** + * <b>Optional</b> Provide a Transformer to transform the returned parameter values. + */ Class<? extends Transformer> transformer() default Transformer.class; } diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspect.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspect.java new file mode 100644 index 000000000..1aa022cbd --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspect.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.dynamodb; + +import java.util.function.Function; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; +import software.amazon.lambda.powertools.parameters.BaseProvider; + +/** + * Provides the Amazon DynamoDB parameter aspect. This aspect is responsible for injecting + * parameters from DynamoDB into fields annotated with @DynamoDbParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class DynamoDbParamAspect extends BaseParamAspect { + + private static Function<String, DynamoDbProvider> providerBuilder = + (String table) -> DynamoDbProvider.builder() + .withTable(table) + .build(); + + @Pointcut("get(* *) && @annotation(ddbConfigParam)") + public void getParam(DynamoDbParam ddbConfigParam) { + } + + @Around("getParam(ddbConfigParam)") + public Object injectParam(final ProceedingJoinPoint joinPoint, final DynamoDbParam ddbConfigParam) { + + BaseProvider provider = providerBuilder.apply(ddbConfigParam.table()); + return getAndTransform(ddbConfigParam.key(), ddbConfigParam.transformer(), provider, + (FieldSignature) joinPoint.getSignature()); + } + +} diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProvider.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProvider.java new file mode 100644 index 000000000..4a1476e38 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProvider.java @@ -0,0 +1,121 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.dynamodb; + +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; +import software.amazon.awssdk.services.dynamodb.model.QueryRequest; +import software.amazon.awssdk.services.dynamodb.model.QueryResponse; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.ParamProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.dynamodb.exception.DynamoDbProviderSchemaException; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Implements a {@link ParamProvider} on top of Amazon DynamoDB. The schema of the table + * is described in the Powertools for AWS Lambda (Java) documentation. + * + * @see <a href="https://docs.powertools.aws.dev/lambda-java/utilities/parameters">Parameters provider documentation</a> + */ +public class DynamoDbProvider extends BaseProvider { + + private final DynamoDbClient client; + private final String tableName; + + DynamoDbProvider(CacheManager cacheManager, TransformationManager transformationManager, DynamoDbClient client, + String tableName) { + super(cacheManager, transformationManager); + this.client = client; + this.tableName = tableName; + } + + /** + * Create a builder that can be used to configure and create a {@link DynamoDbProvider}. + * + * @return a new instance of {@link DynamoDbProviderBuilder} + */ + public static DynamoDbProviderBuilder builder() { + return new DynamoDbProviderBuilder(); + } + + /** + * Return a single value from the DynamoDB parameter provider. + * + * @param key key of the parameter + * @return The value, if it exists, null if it doesn't. Throws if the row exists but doesn't match the schema. + */ + @Override + protected String getValue(String key) { + GetItemResponse resp = client.getItem(GetItemRequest.builder() + .tableName(tableName) + .key(Collections.singletonMap("id", AttributeValue.fromS(key))) + .attributesToGet("value") + .build()); + + // If we have an item at the key, we should be able to get a 'val' out of it. If not it's + // exceptional. + // If we don't have an item at the key, we should return null. + if (resp.hasItem() && !resp.item().values().isEmpty()) { + if (!resp.item().containsKey("value")) { + throw new DynamoDbProviderSchemaException("Missing 'value': " + resp.item()); + } + return resp.item().get("value").s(); + } + + return null; + } + + /** + * Returns multiple values from the DynamoDB parameter provider. + * + * @param path Parameter store path + * @return All values matching the given path, and an empty map if none do. Throws if any records exist that don't match the schema. + */ + @Override + protected Map<String, String> getMultipleValues(String path) { + + QueryResponse resp = client.query(QueryRequest.builder() + .tableName(tableName) + .keyConditionExpression("id = :v_id") + .expressionAttributeValues(Collections.singletonMap(":v_id", AttributeValue.fromS(path))) + .build()); + + return resp + .items() + .stream() + .peek((i) -> + { + if (!i.containsKey("sk")) { + throw new DynamoDbProviderSchemaException("Missing 'sk': " + i); + } + if (!i.containsKey("value")) { + throw new DynamoDbProviderSchemaException("Missing 'value': " + i); + } + }) + .collect( + Collectors.toMap( + (i) -> i.get("sk").s(), + (i) -> i.get("value").s())); + + + } + +} diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderBuilder.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderBuilder.java new file mode 100644 index 000000000..b98ff285d --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderBuilder.java @@ -0,0 +1,116 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.dynamodb; + +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.ParamProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Implements a {@link ParamProvider} on top of the DynamoDB service. DynamoDB provides + */ +public class DynamoDbProviderBuilder { + private DynamoDbClient client; + private String table; + private CacheManager cacheManager; + private TransformationManager transformationManager; + + static DynamoDbClient createClient() { + return DynamoDbClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(BaseProvider.PARAMETERS)).build()) + .build(); + } + + /** + * Create a {@link DynamoDbProvider} instance. + * + * @return a {@link DynamoDbProvider} + */ + public DynamoDbProvider build() { + if (cacheManager == null) { + cacheManager = new CacheManager(); + } + if (table == null) { + throw new IllegalStateException("No DynamoDB table name provided; please provide one"); + } + DynamoDbProvider provider; + if (client == null) { + client = createClient(); + } + if (transformationManager == null) { + transformationManager = new TransformationManager(); + } + provider = new DynamoDbProvider(cacheManager, transformationManager, client, table); + + return provider; + } + + /** + * Set custom {@link DynamoDbClient} to pass to the {@link DynamoDbClient}. <br/> + * Use it if you want to customize the region or any other part of the client. + * + * @param client Custom client + * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) + */ + public DynamoDbProviderBuilder withClient(DynamoDbClient client) { + this.client = client; + return this; + } + + /** + * Provide a CacheManager to the {@link DynamoDbProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) + */ + public DynamoDbProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * <b>Mandatory</b>. Provide a DynamoDB table to the {@link DynamoDbProvider} + * + * @param table the table that parameters will be retrieved from. + * @return the builder to chain calls (eg. <pre>builder.withTable().build()</pre>) + */ + public DynamoDbProviderBuilder withTable(String table) { + this.table = table; + return this; + } + + /** + * Provide a transformationManager to the {@link DynamoDbProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) + */ + public DynamoDbProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/exception/DynamoDbProviderSchemaException.java similarity index 92% rename from powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java rename to powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/exception/DynamoDbProviderSchemaException.java index 77df6e3d3..4a22dbc99 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/exception/DynamoDbProviderSchemaException.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.parameters.exception; +package software.amazon.lambda.powertools.parameters.dynamodb.exception; /** * Thrown when the DynamoDbProvider comes across parameter data that diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptor.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptor.java new file mode 100644 index 000000000..0b8b01dcb --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.dynamodb.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-parameters-dynamodb module is on the classpath. + */ +public final class ParametersDynamodbUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("parameters-dynamodb"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/jni-config.json b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/jni-config.json new file mode 100644 index 000000000..2689203aa --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/jni-config.json @@ -0,0 +1,22 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.String", + "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"sun.instrument.InstrumentationImpl", + "methods":[{"name":"<init>","parameterTypes":["long","boolean","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/reflect-config.json b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/reflect-config.json new file mode 100644 index 000000000..e49454b83 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/reflect-config.json @@ -0,0 +1,330 @@ +[ +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ARCFOURCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESedeCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String"] }, {"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedParameterizedType", + "methods":[{"name":"getAnnotatedActualTypeArguments","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedTypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedWildcardType", + "methods":[{"name":"getAnnotatedUpperBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.TypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.security.AlgorithmParametersSpi" +}, +{ + "name":"java.security.KeyStoreSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"javax.security.auth.x500.X500Principal", + "fields":[{"name":"thisX500Name"}], + "methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.Unit" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"scala.util.Properties" +}, +{ + "name":"software.amazon.awssdk.awscore.AwsClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.awssdk.core.SdkClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"serviceName","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.services.dynamodb.DynamoDbClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"batchExecuteStatement","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchExecuteStatement","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchExecuteStatementRequest"] }, {"name":"batchGetItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest"] }, {"name":"batchGetItemPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetItemPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest"] }, {"name":"batchWriteItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchWriteItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest"] }, {"name":"createBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"createBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.CreateBackupRequest"] }, {"name":"createGlobalTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"createGlobalTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.CreateGlobalTableRequest"] }, {"name":"createTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"createTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.CreateTableRequest"] }, {"name":"deleteBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteBackupRequest"] }, {"name":"deleteItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest"] }, {"name":"deleteResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourcePolicy","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteResourcePolicyRequest"] }, {"name":"deleteTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest"] }, {"name":"describeBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeBackupRequest"] }, {"name":"describeContinuousBackups","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeContinuousBackups","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeContinuousBackupsRequest"] }, {"name":"describeContributorInsights","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeContributorInsights","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeContributorInsightsRequest"] }, {"name":"describeEndpoints","parameterTypes":[] }, {"name":"describeEndpoints","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEndpoints","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeEndpointsRequest"] }, {"name":"describeExport","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeExport","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeExportRequest"] }, {"name":"describeGlobalTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeGlobalTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeGlobalTableRequest"] }, {"name":"describeGlobalTableSettings","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeGlobalTableSettings","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeGlobalTableSettingsRequest"] }, {"name":"describeImport","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeImport","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeImportRequest"] }, {"name":"describeKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeKinesisStreamingDestinationRequest"] }, {"name":"describeLimits","parameterTypes":[] }, {"name":"describeLimits","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeLimits","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeLimitsRequest"] }, {"name":"describeTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest"] }, {"name":"describeTableReplicaAutoScaling","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeTableReplicaAutoScaling","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeTableReplicaAutoScalingRequest"] }, {"name":"describeTimeToLive","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeTimeToLive","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest"] }, {"name":"disableKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"disableKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DisableKinesisStreamingDestinationRequest"] }, {"name":"enableKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"enableKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.EnableKinesisStreamingDestinationRequest"] }, {"name":"executeStatement","parameterTypes":["java.util.function.Consumer"] }, {"name":"executeStatement","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ExecuteStatementRequest"] }, {"name":"executeTransaction","parameterTypes":["java.util.function.Consumer"] }, {"name":"executeTransaction","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ExecuteTransactionRequest"] }, {"name":"exportTableToPointInTime","parameterTypes":["java.util.function.Consumer"] }, {"name":"exportTableToPointInTime","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ExportTableToPointInTimeRequest"] }, {"name":"getItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"getItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.GetItemRequest"] }, {"name":"getResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePolicy","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.GetResourcePolicyRequest"] }, {"name":"importTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"importTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ImportTableRequest"] }, {"name":"listBackups","parameterTypes":[] }, {"name":"listBackups","parameterTypes":["java.util.function.Consumer"] }, {"name":"listBackups","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListBackupsRequest"] }, {"name":"listContributorInsights","parameterTypes":["java.util.function.Consumer"] }, {"name":"listContributorInsights","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListContributorInsightsRequest"] }, {"name":"listContributorInsightsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listContributorInsightsPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListContributorInsightsRequest"] }, {"name":"listExports","parameterTypes":["java.util.function.Consumer"] }, {"name":"listExports","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListExportsRequest"] }, {"name":"listExportsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listExportsPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListExportsRequest"] }, {"name":"listGlobalTables","parameterTypes":[] }, {"name":"listGlobalTables","parameterTypes":["java.util.function.Consumer"] }, {"name":"listGlobalTables","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListGlobalTablesRequest"] }, {"name":"listImports","parameterTypes":["java.util.function.Consumer"] }, {"name":"listImports","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListImportsRequest"] }, {"name":"listImportsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listImportsPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListImportsRequest"] }, {"name":"listTables","parameterTypes":[] }, {"name":"listTables","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTables","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListTablesRequest"] }, {"name":"listTablesPaginator","parameterTypes":[] }, {"name":"listTablesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTablesPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListTablesRequest"] }, {"name":"listTagsOfResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTagsOfResource","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListTagsOfResourceRequest"] }, {"name":"putItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"putItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.PutItemRequest"] }, {"name":"putResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"putResourcePolicy","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.PutResourcePolicyRequest"] }, {"name":"query","parameterTypes":["java.util.function.Consumer"] }, {"name":"query","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.QueryRequest"] }, {"name":"queryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"queryPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.QueryRequest"] }, {"name":"restoreTableFromBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"restoreTableFromBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.RestoreTableFromBackupRequest"] }, {"name":"restoreTableToPointInTime","parameterTypes":["java.util.function.Consumer"] }, {"name":"restoreTableToPointInTime","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.RestoreTableToPointInTimeRequest"] }, {"name":"scan","parameterTypes":["java.util.function.Consumer"] }, {"name":"scan","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ScanRequest"] }, {"name":"scanPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"scanPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ScanRequest"] }, {"name":"serviceClientConfiguration","parameterTypes":[] }, {"name":"tagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"tagResource","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.TagResourceRequest"] }, {"name":"transactGetItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"transactGetItems","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.TransactGetItemsRequest"] }, {"name":"transactWriteItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"transactWriteItems","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.TransactWriteItemsRequest"] }, {"name":"untagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"untagResource","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UntagResourceRequest"] }, {"name":"updateContinuousBackups","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateContinuousBackups","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateContinuousBackupsRequest"] }, {"name":"updateContributorInsights","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateContributorInsights","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateContributorInsightsRequest"] }, {"name":"updateGlobalTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateGlobalTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateGlobalTableRequest"] }, {"name":"updateGlobalTableSettings","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateGlobalTableSettings","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateGlobalTableSettingsRequest"] }, {"name":"updateItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest"] }, {"name":"updateKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateKinesisStreamingDestinationRequest"] }, {"name":"updateTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateTableRequest"] }, {"name":"updateTableReplicaAutoScaling","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateTableReplicaAutoScaling","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateTableReplicaAutoScalingRequest"] }, {"name":"updateTimeToLive","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateTimeToLive","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateTimeToLiveRequest"] }, {"name":"waiter","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.utils.SdkAutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"close","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.BaseProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"get","parameterTypes":["java.lang.String"] }, {"name":"get","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"getMultiple","parameterTypes":["java.lang.String"] }, {"name":"now","parameterTypes":[] }, {"name":"resetToDefaults","parameterTypes":[] }, {"name":"withMaxAge","parameterTypes":["int","java.time.temporal.ChronoUnit"] }, {"name":"withTransformation","parameterTypes":["java.lang.Class"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.ParamProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbParamAspect", + "fields":[{"name":"providerBuilder"}] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getMultipleValues","parameterTypes":["java.lang.String"] }, {"name":"getValue","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.transform.TransformationManager", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"performBasicTransformation","parameterTypes":["java.lang.String"] }, {"name":"performComplexTransformation","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"setTransformer","parameterTypes":["java.lang.Class"] }, {"name":"shouldTransform","parameterTypes":[] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.IssuerAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.NetscapeCertTypeExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.PrivateKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.dynamodb.internal.ParametersDynamodbUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/resource-config.json b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/resource-config.json new file mode 100644 index 000000000..abbae66cf --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/resource-config.json @@ -0,0 +1,23 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/services/dynamodb/execution.interceptors\\E" + }, { + "pattern":"\\Qversion.properties\\E" + }, { + "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/nfc.nrm\\E" + }]}, + "bundles":[] +} diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..f7de6e9be --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.parameters.dynamodb.internal.ParametersDynamodbUserAgentInterceptor diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java new file mode 100644 index 000000000..4294eca48 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.dynamodb; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Function; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class DynamoDbParamAspectTest { + + @Test + void parameterInjectedByProvider() throws Exception { + // Setup our aspect to return a mocked DynamoDbProvider + String tableName = "my-test-tablename"; + String key = "myKey"; + String value = "myValue"; + DynamoDbProvider provider = Mockito.mock(DynamoDbProvider.class); + + Function<String, DynamoDbProvider> providerBuilder = (String table) -> { + if (table.equals(tableName)) { + return provider; + } + throw new RuntimeException("Whoops! Asked for an app/env that we weren't configured for"); + }; + writeStaticField(DynamoDbParamAspect.class, "providerBuilder", providerBuilder, true); + + // Setup our mocked DynamoDbProvider to return a value for our test data + Mockito.when(provider.get(key)).thenReturn(value); + + // Create an instance of a class and let the AppConfigParametersAspect inject it + MyInjectedClass obj = new MyInjectedClass(); + assertThat(obj.myParameter).isEqualTo(value); + } + + class MyInjectedClass { + @DynamoDbParam(table = "my-test-tablename", key = "myKey") + public String myParameter; + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java similarity index 79% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java rename to powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java index 18212b45c..af2617edf 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.dynamodb; import static org.assertj.core.api.Assertions.assertThat; @@ -36,10 +36,10 @@ * will move this across. */ @Disabled -public class DynamoDbProviderE2ETest { +class DynamoDbProviderE2ETest { - final String ParamsTestTable = "ddb-params-test"; - final String MultiparamsTestTable = "ddb-multiparams-test"; + private static final String PARAMS_TEST_TABLE = "ddb-params-test"; + private static final String MULTI_PARAMS_TEST_TABLE = "ddb-multiparams-test"; private final DynamoDbClient ddbClient; public DynamoDbProviderE2ETest() { @@ -52,19 +52,19 @@ public DynamoDbProviderE2ETest() { } @Test - public void TestGetValue() { + void TestGetValue() { // Arrange - HashMap<String, AttributeValue> testItem = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> testItem = new HashMap<>(); testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(ParamsTestTable) + .tableName(PARAMS_TEST_TABLE) .item(testItem) .build()); // Act - DynamoDbProvider provider = makeProvider(ParamsTestTable); + DynamoDbProvider provider = makeProvider(PARAMS_TEST_TABLE); String value = provider.getValue("test_param"); // Assert @@ -72,29 +72,29 @@ public void TestGetValue() { } @Test - public void TestGetValues() { + void TestGetValues() { // Arrange - HashMap<String, AttributeValue> testItem = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> testItem = new HashMap<>(); testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("sk", AttributeValue.fromS("test_param_part_1")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(MultiparamsTestTable) + .tableName(MULTI_PARAMS_TEST_TABLE) .item(testItem) .build()); - HashMap<String, AttributeValue> testItem2 = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> testItem2 = new HashMap<>(); testItem2.put("id", AttributeValue.fromS("test_param")); testItem2.put("sk", AttributeValue.fromS("test_param_part_2")); testItem2.put("value", AttributeValue.fromS("the_value_is_still_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(MultiparamsTestTable) + .tableName(MULTI_PARAMS_TEST_TABLE) .item(testItem2) .build()); // Act - DynamoDbProvider provider = makeProvider(MultiparamsTestTable); + DynamoDbProvider provider = makeProvider(MULTI_PARAMS_TEST_TABLE); Map<String, String> values = provider.getMultipleValues("test_param"); // Assert @@ -104,7 +104,7 @@ public void TestGetValues() { } private DynamoDbProvider makeProvider(String tableName) { - return new DynamoDbProvider(new CacheManager(), DynamoDbClient.builder() + return new DynamoDbProvider(new CacheManager(), null, DynamoDbClient.builder() .httpClientBuilder(UrlConnectionHttpClient.builder()).build(), tableName); } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java similarity index 78% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java rename to powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 2cf84dad4..64f29db79 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -12,22 +12,28 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.dynamodb; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.MockitoAnnotations.openMocks; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.util.HashMap; import java.util.Map; import java.util.UUID; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; @@ -35,37 +41,42 @@ import software.amazon.awssdk.services.dynamodb.model.QueryRequest; import software.amazon.awssdk.services.dynamodb.model.QueryResponse; import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; +import software.amazon.lambda.powertools.parameters.dynamodb.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -public class DynamoDbProviderTest { +@ExtendWith(MockitoExtension.class) +class DynamoDbProviderTest { + + private static final String TABLE_NAME = "ddb-test-table"; - private final String tableName = "ddb-test-table"; @Mock DynamoDbClient client; + @Mock TransformationManager transformationManager; + @Captor ArgumentCaptor<GetItemRequest> getItemValueCaptor; + @Captor ArgumentCaptor<QueryRequest> queryRequestCaptor; + private DynamoDbProvider provider; @BeforeEach - public void init() { + void init() { openMocks(this); CacheManager cacheManager = new CacheManager(); - provider = new DynamoDbProvider(cacheManager, client, tableName); + provider = new DynamoDbProvider(cacheManager, transformationManager, client, TABLE_NAME); } - @Test - public void getValue() { + void getValue() { // Arrange String key = "Key1"; String expectedValue = "Value1"; - HashMap<String, AttributeValue> responseData = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> responseData = new HashMap<>(); responseData.put("id", AttributeValue.fromS(key)); responseData.put("value", AttributeValue.fromS(expectedValue)); GetItemResponse response = GetItemResponse.builder() @@ -78,13 +89,12 @@ public void getValue() { // Assert assertThat(value).isEqualTo(expectedValue); - assertThat(getItemValueCaptor.getValue().tableName()).isEqualTo(tableName); + assertThat(getItemValueCaptor.getValue().tableName()).isEqualTo(TABLE_NAME); assertThat(getItemValueCaptor.getValue().key().get("id").s()).isEqualTo(key); } - @Test - public void getValueWithNullResultsReturnsNull() { + void getValueWithNullResultsReturnsNull() { // Arrange Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(null) @@ -98,7 +108,7 @@ public void getValueWithNullResultsReturnsNull() { } @Test - public void getValueWithoutResultsReturnsNull() { + void getValueWithoutResultsReturnsNull() { // Arrange Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(new HashMap<>()) @@ -112,25 +122,23 @@ public void getValueWithoutResultsReturnsNull() { } @Test - public void getValueWithMalformedRowThrows() { + void getValueWithMalformedRowThrows() { // Arrange String key = "Key1"; - HashMap<String, AttributeValue> responseData = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> responseData = new HashMap<>(); responseData.put("id", AttributeValue.fromS(key)); responseData.put("not-value", AttributeValue.fromS("something")); Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(responseData) .build()); // Act - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { provider.getValue(key); }); } - @Test - public void getValues() { + void getValues() { // Arrange String key = "Key1"; @@ -138,11 +146,11 @@ public void getValues() { String val1 = "Val1"; String subkey2 = "Subkey2"; String val2 = "Val2"; - HashMap<String, AttributeValue> item1 = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> item1 = new HashMap<>(); item1.put("id", AttributeValue.fromS(key)); item1.put("sk", AttributeValue.fromS(subkey1)); item1.put("value", AttributeValue.fromS(val1)); - HashMap<String, AttributeValue> item2 = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> item2 = new HashMap<>(); item2.put("id", AttributeValue.fromS(key)); item2.put("sk", AttributeValue.fromS(subkey2)); item2.put("value", AttributeValue.fromS(val2)); @@ -158,13 +166,13 @@ public void getValues() { assertThat(values.size()).isEqualTo(2); assertThat(values.get(subkey1)).isEqualTo(val1); assertThat(values.get(subkey2)).isEqualTo(val2); - assertThat(queryRequestCaptor.getValue().tableName()).isEqualTo(tableName); + assertThat(queryRequestCaptor.getValue().tableName()).isEqualTo(TABLE_NAME); assertThat(queryRequestCaptor.getValue().keyConditionExpression()).isEqualTo("id = :v_id"); assertThat(queryRequestCaptor.getValue().expressionAttributeValues().get(":v_id").s()).isEqualTo(key); } @Test - public void getValuesWithoutResultsReturnsNull() { + void getValuesWithoutResultsReturnsNull() { // Arrange Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn( QueryResponse.builder().items().build()); @@ -177,10 +185,10 @@ public void getValuesWithoutResultsReturnsNull() { } @Test - public void getMultipleValuesMissingSortKey_throwsException() { + void getMultipleValuesMissingSortKey_throwsException() { // Arrange String key = "Key1"; - HashMap<String, AttributeValue> item = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> item = new HashMap<>(); item.put("id", AttributeValue.fromS(key)); item.put("value", AttributeValue.fromS("somevalue")); QueryResponse response = QueryResponse.builder() @@ -189,18 +197,17 @@ public void getMultipleValuesMissingSortKey_throwsException() { Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); // Assert - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { // Act provider.getMultipleValues(key); }); } @Test - public void getValuesWithMalformedRowThrows() { + void getValuesWithMalformedRowThrows() { // Arrange String key = "Key1"; - HashMap<String, AttributeValue> item1 = new HashMap<String, AttributeValue>(); + Map<String, AttributeValue> item1 = new HashMap<>(); item1.put("id", AttributeValue.fromS(key)); item1.put("sk", AttributeValue.fromS("some-subkey")); item1.put("not-value", AttributeValue.fromS("somevalue")); @@ -210,29 +217,29 @@ public void getValuesWithMalformedRowThrows() { Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); // Assert - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { // Act provider.getMultipleValues(key); }); } @Test - public void testDynamoDBBuilderMissingCacheManager_throwsException() { + void testDynamoDBBuilderMissingTable_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() - .withTable("table") + .withCacheManager(new CacheManager()) .build()); } @Test - public void testDynamoDBBuilderMissingTable_throwsException() { + void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() - .withCacheManager(new CacheManager()) - .build()); + // Act + DynamoDbProvider dynamoDbProvider = DynamoDbProvider.builder().withTable("test-table") + .build(); + // Assert + assertDoesNotThrow(() -> dynamoDbProvider.withTransformation(json)); } } diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptorTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptorTest.java new file mode 100644 index 000000000..f9c8ebea5 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.dynamodb.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class ParametersDynamodbUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/PARAMETERS-DYNAMODB/"); + } +} \ No newline at end of file diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml new file mode 100644 index 000000000..b9535269e --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parent</artifactId> + <version>2.9.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>powertools-parameters-secrets</artifactId> + <name>Powertools for AWS Lambda (Java) library Parameters - Secrets Manager</name> + <description>Secrets Manager implementation for the Parameters module</description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>secretsmanager</artifactId> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>apache-client</artifactId> + </exclusion> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>netty-nio-client</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + + <!-- Required explicitly for @Captor ArgumentCaptor --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-parameters-secrets</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_REGION>eu-central-1</AWS_REGION> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParam.java b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParam.java new file mode 100644 index 000000000..f9c110c49 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParam.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.secrets; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import software.amazon.lambda.powertools.parameters.transform.Transformer; + +/** + * Inject a parameter from the Secrets Manager into a field. You can also use + * {@code SecretsProviderBuilder} to obtain Secrets Manager values directly, rather than + * injecting them implicitly. + * + * <pre> + * @SecretsParam(key = "my-secret") + * String mySecret; + * </pre> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SecretsParam { + /** + * <b>Mandatory</b>. key from the secrets manager store. + * @return + */ + String key(); + + /** + * <b>Optional</b>. a transfer to apply to the value + */ + Class<? extends Transformer> transformer() default Transformer.class; +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspect.java b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspect.java new file mode 100644 index 000000000..748c88cc9 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspect.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.secrets; + +import java.util.function.Supplier; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; + +/** + * Provides the Secrets parameter aspect. This aspect is responsible for injecting + * parameters from AWS Secrets Manager into fields annotated with @SecretsParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class SecretsParamAspect extends BaseParamAspect { + + private static Supplier<SecretsProvider> providerBuilder = () -> SecretsProvider.builder() + .build(); + + @Pointcut("get(* *) && @annotation(secretsParam)") + public void getParam(SecretsParam secretsParam) { + } + + @Around("getParam(secretsParam)") + public Object injectParam(final ProceedingJoinPoint joinPoint, final SecretsParam secretsParam) { + + SecretsProvider provider = providerBuilder.get(); + return getAndTransform(secretsParam.key(), secretsParam.transformer(), provider, + (FieldSignature) joinPoint.getSignature()); + } + +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProvider.java b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProvider.java new file mode 100644 index 000000000..9087c1ad6 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProvider.java @@ -0,0 +1,115 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.secrets; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.util.Base64; +import java.util.Map; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * AWS Secrets Manager Parameter Provider<br/><br/> + * + * <u>Samples:</u> + * <pre> + * SecretsProvider provider = SecretsProvider.builder().build(); + * + * String value = provider.get("key"); + * System.out.println(value); + * >>> "value" + * + * // Get a value and cache it for 30 seconds (all others values will now be cached for 30 seconds) + * String value = provider.defaultMaxAge(30, ChronoUnit.SECONDS).get("key"); + * + * // Get a value and cache it for 1 minute (all others values are cached for 5 seconds by default) + * String value = provider.withMaxAge(1, ChronoUnit.MINUTES).get("key"); + * + * // Get a base64 encoded value, decoded into a String, and store it in the cache + * String value = provider.withTransformation(Transformer.base64).get("key"); + * + * // Get a json value, transform it into an Object, and store it in the cache + * TargetObject = provider.withTransformation(Transformer.json).get("key", TargetObject.class); + * </pre> + */ +public class SecretsProvider extends BaseProvider { + + private final SecretsManagerClient client; + + /** + * Use the {@link SecretsProviderBuilder} to create an instance! + * + * @param client custom client you would like to use. + */ + SecretsProvider(CacheManager cacheManager, TransformationManager transformationManager, + SecretsManagerClient client) { + super(cacheManager, transformationManager); + this.client = client; + } + + /** + * Create a builder that can be used to configure and create a {@link SecretsProvider}. + * + * @return a new instance of {@link SecretsProviderBuilder} + */ + public static SecretsProviderBuilder builder() { + return new SecretsProviderBuilder(); + } + + /** + * Create a SecretsProvider with all default settings. + */ + public static SecretsProvider create() { + return new SecretsProviderBuilder().build(); + } + + /** + * Retrieve the parameter value from the AWS Secrets Manager. + * + * @param key key of the parameter + * @return the value of the parameter identified by the key + */ + @Override + protected String getValue(String key) { + GetSecretValueRequest request = GetSecretValueRequest.builder().secretId(key).build(); + + String secretValue = client.getSecretValue(request).secretString(); + if (secretValue == null) { + secretValue = + new String(Base64.getDecoder().decode(client.getSecretValue(request).secretBinary().asByteArray()), + UTF_8); + } + return secretValue; + } + + /** + * @throws UnsupportedOperationException as it is not possible to get multiple values simultaneously from Secrets Manager + */ + @Override + protected Map<String, String> getMultipleValues(String path) { + throw new UnsupportedOperationException("Impossible to get multiple values from AWS Secrets Manager"); + } + + + // For test purpose only + SecretsManagerClient getClient() { + return client; + } + +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderBuilder.java b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderBuilder.java new file mode 100644 index 000000000..517274e19 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderBuilder.java @@ -0,0 +1,102 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.secrets; + +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.ParamProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Implements a {@link ParamProvider} on top of the SecretsManager service. SecretsManager provides + */ +public class SecretsProviderBuilder { + + private SecretsManagerClient client; + private CacheManager cacheManager; + private TransformationManager transformationManager; + + private static SecretsManagerClient createClient() { + return SecretsManagerClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(BaseProvider.PARAMETERS)).build()) + .build(); + } + + /** + * Create a {@link SecretsProvider} instance. + * + * @return a {@link SecretsProvider} + */ + public SecretsProvider build() { + if (cacheManager == null) { + cacheManager = new CacheManager(); + } + SecretsProvider provider; + if (client == null) { + client = createClient(); + } + if(transformationManager == null){ + transformationManager = new TransformationManager(); + } + provider = new SecretsProvider(cacheManager, transformationManager, client); + + return provider; + } + + /** + * Set custom {@link SecretsManagerClient} to pass to the {@link SecretsProvider}. <br/> + * Use it if you want to customize the region or any other part of the client. + * + * @param client Custom client + * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) + */ + public SecretsProviderBuilder withClient(SecretsManagerClient client) { + this.client = client; + return this; + } + + /** + * Provide a CacheManager to the {@link SecretsProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) + */ + public SecretsProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * Provide a transformationManager to the {@link SecretsProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) + */ + public SecretsProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptor.java b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptor.java new file mode 100644 index 000000000..e3eda5889 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.secrets.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-parameters-secrets module is on the classpath. + */ +public final class ParametersSecretsUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("parameters-secrets"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/jni-config.json b/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/jni-config.json new file mode 100644 index 000000000..2689203aa --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/jni-config.json @@ -0,0 +1,22 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.String", + "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"sun.instrument.InstrumentationImpl", + "methods":[{"name":"<init>","parameterTypes":["long","boolean","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/reflect-config.json b/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/reflect-config.json new file mode 100644 index 000000000..097a30784 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/reflect-config.json @@ -0,0 +1,327 @@ +[ +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ARCFOURCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESedeCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String"] }, {"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedParameterizedType", + "methods":[{"name":"getAnnotatedActualTypeArguments","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedTypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedWildcardType", + "methods":[{"name":"getAnnotatedUpperBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.TypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.security.AlgorithmParametersSpi" +}, +{ + "name":"java.security.KeyStoreSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"javax.security.auth.x500.X500Principal", + "fields":[{"name":"thisX500Name"}], + "methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"scala.util.Properties" +}, +{ + "name":"software.amazon.awssdk.awscore.AwsClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.awssdk.core.SdkClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"serviceName","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.services.secretsmanager.SecretsManagerClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"batchGetSecretValue","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetSecretValue","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.BatchGetSecretValueRequest"] }, {"name":"batchGetSecretValuePaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetSecretValuePaginator","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.BatchGetSecretValueRequest"] }, {"name":"cancelRotateSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"cancelRotateSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.CancelRotateSecretRequest"] }, {"name":"createSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"createSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.CreateSecretRequest"] }, {"name":"deleteResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.DeleteResourcePolicyRequest"] }, {"name":"deleteSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.DeleteSecretRequest"] }, {"name":"describeSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.DescribeSecretRequest"] }, {"name":"getRandomPassword","parameterTypes":[] }, {"name":"getRandomPassword","parameterTypes":["java.util.function.Consumer"] }, {"name":"getRandomPassword","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.GetRandomPasswordRequest"] }, {"name":"getResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.GetResourcePolicyRequest"] }, {"name":"getSecretValue","parameterTypes":["java.util.function.Consumer"] }, {"name":"getSecretValue","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest"] }, {"name":"listSecretVersionIds","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecretVersionIds","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretVersionIdsRequest"] }, {"name":"listSecretVersionIdsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecretVersionIdsPaginator","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretVersionIdsRequest"] }, {"name":"listSecrets","parameterTypes":[] }, {"name":"listSecrets","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecrets","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretsRequest"] }, {"name":"listSecretsPaginator","parameterTypes":[] }, {"name":"listSecretsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecretsPaginator","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretsRequest"] }, {"name":"putResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"putResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.PutResourcePolicyRequest"] }, {"name":"putSecretValue","parameterTypes":["java.util.function.Consumer"] }, {"name":"putSecretValue","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.PutSecretValueRequest"] }, {"name":"removeRegionsFromReplication","parameterTypes":["java.util.function.Consumer"] }, {"name":"removeRegionsFromReplication","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.RemoveRegionsFromReplicationRequest"] }, {"name":"replicateSecretToRegions","parameterTypes":["java.util.function.Consumer"] }, {"name":"replicateSecretToRegions","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ReplicateSecretToRegionsRequest"] }, {"name":"restoreSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"restoreSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.RestoreSecretRequest"] }, {"name":"rotateSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"rotateSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.RotateSecretRequest"] }, {"name":"serviceClientConfiguration","parameterTypes":[] }, {"name":"stopReplicationToReplica","parameterTypes":["java.util.function.Consumer"] }, {"name":"stopReplicationToReplica","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.StopReplicationToReplicaRequest"] }, {"name":"tagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"tagResource","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.TagResourceRequest"] }, {"name":"untagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"untagResource","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.UntagResourceRequest"] }, {"name":"updateSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.UpdateSecretRequest"] }, {"name":"updateSecretVersionStage","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateSecretVersionStage","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.UpdateSecretVersionStageRequest"] }, {"name":"validateResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"validateResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ValidateResourcePolicyRequest"] }] +}, +{ + "name":"software.amazon.awssdk.utils.SdkAutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"close","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.BaseProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"get","parameterTypes":["java.lang.String"] }, {"name":"get","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"getMultiple","parameterTypes":["java.lang.String"] }, {"name":"now","parameterTypes":[] }, {"name":"resetToDefaults","parameterTypes":[] }, {"name":"withMaxAge","parameterTypes":["int","java.time.temporal.ChronoUnit"] }, {"name":"withTransformation","parameterTypes":["java.lang.Class"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.ParamProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.lambda.powertools.parameters.secrets.SecretsParamAspect", + "fields":[{"name":"providerBuilder"}] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.secrets.SecretsProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getMultipleValues","parameterTypes":["java.lang.String"] }, {"name":"getValue","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.transform.TransformationManager", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"performBasicTransformation","parameterTypes":["java.lang.String"] }, {"name":"performComplexTransformation","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"setTransformer","parameterTypes":["java.lang.Class"] }, {"name":"shouldTransform","parameterTypes":[] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.IssuerAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.NetscapeCertTypeExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.PrivateKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.secrets.internal.ParametersSecretsUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/resource-config.json b/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/resource-config.json new file mode 100644 index 000000000..f914dbf44 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/resource-config.json @@ -0,0 +1,23 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/services/secretsmanager/execution.interceptors\\E" + }, { + "pattern":"\\Qversion.properties\\E" + }, { + "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/nfc.nrm\\E" + }]}, + "bundles":[] +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-parameters/powertools-parameters-secrets/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..ca08369c5 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.parameters.secrets.internal.ParametersSecretsUserAgentInterceptor diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java new file mode 100644 index 000000000..5523cbb0e --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.secrets; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class SecretsParamAspectTest { + + @Test + void parameterInjectedByProvider() throws Exception { + // Setup our aspect to return a mocked SecretsProvider + String key = "myKey"; + String value = "mySecretValue"; + SecretsProvider provider = Mockito.mock(SecretsProvider.class); + + Supplier<SecretsProvider> providerBuilder = () -> provider; + writeStaticField(SecretsParamAspect.class, "providerBuilder", providerBuilder, true); + + // Setup our mocked SecretsProvider to return a value for our test data + Mockito.when(provider.get(key)).thenReturn(value); + + // Create an instance of a class and let the SecretsParamAspect inject it + MyInjectedClass obj = new MyInjectedClass(); + assertThat(obj.mySecret).isEqualTo(value); + } + + class MyInjectedClass { + @SecretsParam(key = "myKey") + public String mySecret; + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java similarity index 67% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java rename to powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java index f4f2d9bee..a6fbe1c8e 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java @@ -12,21 +12,26 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.secrets; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; -import static org.mockito.MockitoAnnotations.openMocks; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.time.temporal.ChronoUnit; import java.util.Base64; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; @@ -34,7 +39,8 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -public class SecretsProviderTest { +@ExtendWith(MockitoExtension.class) +class SecretsProviderTest { @Mock SecretsManagerClient client; @@ -50,19 +56,17 @@ public class SecretsProviderTest { SecretsProvider provider; @BeforeEach - public void init() { - openMocks(this); + void init() { cacheManager = new CacheManager(); - provider = new SecretsProvider(cacheManager, client); + provider = new SecretsProvider(cacheManager, transformationManager, client); } @Test - public void getValue() { + void getValue() { String key = "Key1"; String expectedValue = "Value1"; GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(expectedValue).build(); Mockito.when(client.getSecretValue(paramCaptor.capture())).thenReturn(response); - provider.defaultMaxAge(1, ChronoUnit.DAYS); provider.withMaxAge(2, ChronoUnit.DAYS); String value = provider.getValue(key); @@ -72,12 +76,12 @@ public void getValue() { } @Test - public void getValueBase64() { + void getValueBase64() { String key = "Key2"; String expectedValue = "Value2"; byte[] valueb64 = Base64.getEncoder().encode(expectedValue.getBytes()); - GetSecretValueResponse response = - GetSecretValueResponse.builder().secretBinary(SdkBytes.fromByteArray(valueb64)).build(); + GetSecretValueResponse response = GetSecretValueResponse.builder() + .secretBinary(SdkBytes.fromByteArray(valueb64)).build(); Mockito.when(client.getSecretValue(paramCaptor.capture())).thenReturn(response); String value = provider.getValue(key); @@ -87,22 +91,29 @@ public void getValueBase64() { } @Test - public void getMultipleValuesThrowsException() { - + void getMultipleValuesThrowsException() { // Act & Assert assertThatRuntimeException().isThrownBy(() -> provider.getMultipleValues("path")) .withMessage("Impossible to get multiple values from AWS Secrets Manager"); - } @Test - public void testSecretsProviderBuilderMissingCacheManager_throwsException() { + void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { + // Act + SecretsProvider secretsProvider = SecretsProvider.builder() + .build(); + + // Assert + assertNotNull(secretsProvider); + assertNotNull(secretsProvider.getClient()); + } - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> SecretsProvider.builder() - .withClient(client) - .withTransformationManager(transformationManager) - .build()) - .withMessage("No CacheManager provided, please provide one"); + @Test + void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + // Act + SecretsProvider secretsProvider = SecretsProvider.builder() + .build(); + // Assert + assertDoesNotThrow(() -> secretsProvider.withTransformation(json)); } } diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptorTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptorTest.java new file mode 100644 index 000000000..6d59ff717 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.secrets.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class ParametersSecretsUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/PARAMETERS-SECRETS/"); + } +} \ No newline at end of file diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml new file mode 100644 index 000000000..e0253e10b --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parent</artifactId> + <version>2.9.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>powertools-parameters-ssm</artifactId> + <name>Powertools for AWS Lambda (Java) library Parameters - SSM</name> + <description>SSM Parameter Store implementation for the Parameters module</description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>ssm</artifactId> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>apache-client</artifactId> + </exclusion> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>netty-nio-client</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_REGION>eu-central-1</AWS_REGION> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + + <!-- Required explicitly for @Captor ArgumentCaptor --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-parameters-ssm</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParam.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParam.java new file mode 100644 index 000000000..9b1587bb4 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParam.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.ssm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import software.amazon.lambda.powertools.parameters.transform.Transformer; + +/** + * Inject a parameter from the SSM Parameter Store into a field. You can also use + * {@code SSMProviderBuilder} to obtain SSM values directly, rather than injecting them implicitly. + * + * Usage: + * <pre> + * @SSMParam(key = "/my/parameter") + * String myParameter; + * </pre> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SSMParam { + /** + * <b>Mandatory</b>. Key from the SSM parameter store + * @return + */ + String key(); + + + /** + * <b>Optional</b>. a transfer to apply to the value + */ + Class<? extends Transformer> transformer() default Transformer.class; +} diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspect.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspect.java new file mode 100644 index 000000000..b4d370506 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspect.java @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.ssm; + +import java.util.function.Supplier; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; + +/** + * Provides the SSM parameter store parameter aspect. This aspect is responsible for injecting + * parameters from SSM Parameter Store into fields annotated with @SSMParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class SSMParamAspect extends BaseParamAspect { + + // This supplier produces a new SSMProvider each time it is called + private static Supplier<SSMProvider> providerBuilder = () -> SSMProvider.builder() + .build(); + + @Pointcut("get(* *) && @annotation(secretsParam)") + public void getParam(SSMParam secretsParam) { + } + + @Around("getParam(ssmPaam)") + public Object injectParam(final ProceedingJoinPoint joinPoint, final SSMParam ssmPaam) { + + SSMProvider provider = providerBuilder.get(); + return getAndTransform(ssmPaam.key(), ssmPaam.transformer(), provider, + (FieldSignature) joinPoint.getSignature()); + } + +} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java similarity index 55% rename from powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java rename to powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java index 549cdfbab..3cf728219 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java @@ -12,32 +12,25 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.ssm; -import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.Map; -import software.amazon.awssdk.core.SdkSystemSetting; -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.ssm.SsmClient; import software.amazon.awssdk.services.ssm.model.GetParameterRequest; import software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest; import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse; import software.amazon.awssdk.utils.StringUtils; -import software.amazon.lambda.powertools.core.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.parameters.BaseProvider; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import software.amazon.lambda.powertools.parameters.transform.Transformer; /** * AWS System Manager Parameter Store Provider <br/><br/> * * <u>Samples:</u> * <pre> - * SSMProvider provider = ParamManager.getSsmProvider(); + * SSMProvider provider = SSMProvider.builder().build(); * * String value = provider.get("key"); * System.out.println(value); @@ -73,40 +66,37 @@ public class SSMProvider extends BaseProvider { private final SsmClient client; - private boolean decrypt = false; - private boolean recursive = false; + private final ThreadLocal<Boolean> decrypt = ThreadLocal.withInitial(() -> false); + private final ThreadLocal<Boolean> recursive = ThreadLocal.withInitial(() -> false); /** * Constructor with custom {@link SsmClient}. <br/> * Use when you need to customize region or any other attribute of the client.<br/><br/> * <p> - * Use the {@link SSMProvider.Builder} to create an instance of it. + * Use the {@link SSMProviderBuilder} to create an instance of it. * - * @param client custom client you would like to use. + * @param client custom client you would like to use. + * @param transformationManager Null, or a transformation manager */ - SSMProvider(CacheManager cacheManager, SsmClient client) { - super(cacheManager); + SSMProvider(CacheManager cacheManager, TransformationManager transformationManager, SsmClient client) { + super(cacheManager, transformationManager); this.client = client; } /** - * Constructor with only a CacheManager<br/> - * <p> - * Used in {@link ParamManager#createProvider(Class)} + * Create a builder that can be used to configure and create a {@link SSMProvider}. * - * @param cacheManager handles the parameter caching + * @return a new instance of {@link SSMProviderBuilder} */ - SSMProvider(CacheManager cacheManager) { - this(cacheManager, Builder.createClient()); + public static SSMProviderBuilder builder() { + return new SSMProviderBuilder(); } /** - * Create a builder that can be used to configure and create a {@link SSMProvider}. - * - * @return a new instance of {@link SSMProvider.Builder} + * Create a SSMProvider with all default settings. */ - public static SSMProvider.Builder builder() { - return new SSMProvider.Builder(); + public static SSMProvider create() { + return new SSMProviderBuilder().build(); } /** @@ -119,38 +109,11 @@ public static SSMProvider.Builder builder() { public String getValue(String key) { GetParameterRequest request = GetParameterRequest.builder() .name(key) - .withDecryption(decrypt) + .withDecryption(decrypt.get()) .build(); return client.getParameter(request).parameter().value(); } - /** - * {@inheritDoc} - */ - @Override - public SSMProvider defaultMaxAge(int maxAge, ChronoUnit unit) { - super.defaultMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SSMProvider withMaxAge(int maxAge, ChronoUnit unit) { - super.withMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SSMProvider withTransformation(Class<? extends Transformer> transformerClass) { - super.withTransformation(transformerClass); - return this; - } - /** * Tells System Manager Parameter Store to decrypt the parameter value.<br/> * By default, parameter values are not decrypted.<br/> @@ -159,7 +122,7 @@ public SSMProvider withTransformation(Class<? extends Transformer> transformerCl * @return the provider itself in order to chain calls (eg. <pre>provider.withDecryption().get("key")</pre>). */ public SSMProvider withDecryption() { - this.decrypt = true; + this.decrypt.set(true); return this; } @@ -170,7 +133,7 @@ public SSMProvider withDecryption() { * @return the provider itself in order to chain calls (eg. <pre>provider.recursive().getMultiple("key")</pre>). */ public SSMProvider recursive() { - this.recursive = true; + this.recursive.set(true); return this; } @@ -197,8 +160,8 @@ protected Map<String, String> getMultipleValues(String path) { private Map<String, String> getMultipleBis(String path, String nextToken) { GetParametersByPathRequest request = GetParametersByPathRequest.builder() .path(path) - .withDecryption(decrypt) - .recursive(recursive) + .withDecryption(decrypt.get()) + .recursive(recursive.get()) .nextToken(nextToken) .build(); @@ -207,12 +170,12 @@ private Map<String, String> getMultipleBis(String path, String nextToken) { // not using the client.getParametersByPathPaginator() as hardly testable GetParametersByPathResponse res = client.getParametersByPath(request); if (res.hasParameters()) { - res.parameters().forEach(parameter -> - { - /* Standardize the parameter name - The parameter name returned by SSM will contained the full path. - However, for readability, we should return only the part after - the path. + res.parameters().forEach(parameter -> { + /* + * Standardize the parameter name + * The parameter name returned by SSM will contain the full path. + * However, for readability, we should return only the part after + * the path. */ String name = parameter.name(); if (name.startsWith(path)) { @@ -233,8 +196,8 @@ private Map<String, String> getMultipleBis(String path, String nextToken) { @Override protected void resetToDefaults() { super.resetToDefaults(); - recursive = false; - decrypt = false; + decrypt.remove(); + recursive.remove(); } // For tests purpose only @@ -242,75 +205,4 @@ SsmClient getClient() { return client; } - static class Builder { - private SsmClient client; - private CacheManager cacheManager; - private TransformationManager transformationManager; - - private static SsmClient createClient() { - return SsmClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) - .build(); - } - - /** - * Create a {@link SSMProvider} instance. - * - * @return a {@link SSMProvider} - */ - public SSMProvider build() { - if (cacheManager == null) { - throw new IllegalStateException("No CacheManager provided, please provide one"); - } - SSMProvider provider; - if (client == null) { - client = createClient(); - } - - provider = new SSMProvider(cacheManager, client); - - if (transformationManager != null) { - provider.setTransformationManager(transformationManager); - } - return provider; - } - - /** - * Set custom {@link SsmClient} to pass to the {@link SSMProvider}. <br/> - * Use it if you want to customize the region or any other part of the client. - * - * @param client Custom client - * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) - */ - public SSMProvider.Builder withClient(SsmClient client) { - this.client = client; - return this; - } - - /** - * <b>Mandatory</b>. Provide a CacheManager to the {@link SSMProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) - */ - public SSMProvider.Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * Provide a transformationManager to the {@link SSMProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) - */ - public SSMProvider.Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } } diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java new file mode 100644 index 000000000..4c26463fb --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java @@ -0,0 +1,101 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.ssm; + +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.ssm.SsmClient; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Builder for the {@link SSMProvider} + */ +public class SSMProviderBuilder { + private SsmClient client; + private CacheManager cacheManager; + private TransformationManager transformationManager; + + private static SsmClient createClient() { + return SsmClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(BaseProvider.PARAMETERS)).build()) + .build(); + } + + /** + * Create a {@link SSMProvider} instance. + * + * @return a {@link SSMProvider} + */ + public SSMProvider build() { + if (cacheManager == null) { + cacheManager = new CacheManager(); + } + SSMProvider provider; + if (client == null) { + client = createClient(); + } + + if(transformationManager == null){ + transformationManager = new TransformationManager(); + } + provider = new SSMProvider(cacheManager, transformationManager, client); + + return provider; + } + + /** + * Set custom {@link SsmClient} to pass to the {@link SSMProvider}. <br/> + * Use it if you want to customize the region or any other part of the client. + * + * @param client Custom client + * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) + */ + public SSMProviderBuilder withClient(SsmClient client) { + this.client = client; + return this; + } + + /** + * Provide a CacheManager to the {@link SSMProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) + */ + public SSMProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * Provide a transformationManager to the {@link SSMProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) + */ + public SSMProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptor.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptor.java new file mode 100644 index 000000000..ad1ff65dc --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.ssm.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-parameters-ssm module is on the classpath. + */ +public final class ParametersSsmUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("parameters-ssm"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/jni-config.json b/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/jni-config.json new file mode 100644 index 000000000..2c4de0562 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/jni-config.json @@ -0,0 +1,26 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.String", + "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"org.apache.maven.surefire.booter.ForkedBooter", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"sun.instrument.InstrumentationImpl", + "methods":[{"name":"<init>","parameterTypes":["long","boolean","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/reflect-config.json b/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/reflect-config.json new file mode 100644 index 000000000..a655e62ce --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/reflect-config.json @@ -0,0 +1,345 @@ +[ +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ARCFOURCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESedeCipher", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String"] }, {"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedParameterizedType", + "methods":[{"name":"getAnnotatedActualTypeArguments","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedTypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedWildcardType", + "methods":[{"name":"getAnnotatedUpperBounds","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.TypeVariable", + "methods":[{"name":"getAnnotatedBounds","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.security.AlgorithmParametersSpi" +}, +{ + "name":"java.security.KeyStoreSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"javax.security.auth.x500.X500Principal", + "fields":[{"name":"thisX500Name"}], + "methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"scala.util.Properties" +}, +{ + "name":"software.amazon.awssdk.awscore.AwsClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.awssdk.core.SdkClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"serviceName","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.services.ssm.SsmClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"addTagsToResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"addTagsToResource","parameterTypes":["software.amazon.awssdk.services.ssm.model.AddTagsToResourceRequest"] }, {"name":"associateOpsItemRelatedItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"associateOpsItemRelatedItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.AssociateOpsItemRelatedItemRequest"] }, {"name":"cancelCommand","parameterTypes":["java.util.function.Consumer"] }, {"name":"cancelCommand","parameterTypes":["software.amazon.awssdk.services.ssm.model.CancelCommandRequest"] }, {"name":"cancelMaintenanceWindowExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"cancelMaintenanceWindowExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.CancelMaintenanceWindowExecutionRequest"] }, {"name":"createActivation","parameterTypes":["java.util.function.Consumer"] }, {"name":"createActivation","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateActivationRequest"] }, {"name":"createAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"createAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateAssociationRequest"] }, {"name":"createAssociationBatch","parameterTypes":["java.util.function.Consumer"] }, {"name":"createAssociationBatch","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateAssociationBatchRequest"] }, {"name":"createDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"createDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateDocumentRequest"] }, {"name":"createMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"createMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateMaintenanceWindowRequest"] }, {"name":"createOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"createOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateOpsItemRequest"] }, {"name":"createOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"createOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateOpsMetadataRequest"] }, {"name":"createPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"createPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreatePatchBaselineRequest"] }, {"name":"createResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"createResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateResourceDataSyncRequest"] }, {"name":"deleteActivation","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteActivation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteActivationRequest"] }, {"name":"deleteAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteAssociationRequest"] }, {"name":"deleteDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteDocumentRequest"] }, {"name":"deleteInventory","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteInventory","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteInventoryRequest"] }, {"name":"deleteMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteMaintenanceWindowRequest"] }, {"name":"deleteOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteOpsItemRequest"] }, {"name":"deleteOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteOpsMetadataRequest"] }, {"name":"deleteParameter","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteParameter","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteParameterRequest"] }, {"name":"deleteParameters","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteParameters","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteParametersRequest"] }, {"name":"deletePatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"deletePatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeletePatchBaselineRequest"] }, {"name":"deleteResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteResourceDataSyncRequest"] }, {"name":"deleteResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourcePolicy","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteResourcePolicyRequest"] }, {"name":"deregisterManagedInstance","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterManagedInstance","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterManagedInstanceRequest"] }, {"name":"deregisterPatchBaselineForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterPatchBaselineForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterPatchBaselineForPatchGroupRequest"] }, {"name":"deregisterTargetFromMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterTargetFromMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterTargetFromMaintenanceWindowRequest"] }, {"name":"deregisterTaskFromMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterTaskFromMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterTaskFromMaintenanceWindowRequest"] }, {"name":"describeActivations","parameterTypes":[] }, {"name":"describeActivations","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeActivations","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeActivationsRequest"] }, {"name":"describeActivationsPaginator","parameterTypes":[] }, {"name":"describeActivationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeActivationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeActivationsRequest"] }, {"name":"describeAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationRequest"] }, {"name":"describeAssociationExecutionTargets","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutionTargets","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionTargetsRequest"] }, {"name":"describeAssociationExecutionTargetsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutionTargetsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionTargetsRequest"] }, {"name":"describeAssociationExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionsRequest"] }, {"name":"describeAssociationExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionsRequest"] }, {"name":"describeAutomationExecutions","parameterTypes":[] }, {"name":"describeAutomationExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationExecutionsRequest"] }, {"name":"describeAutomationExecutionsPaginator","parameterTypes":[] }, {"name":"describeAutomationExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationExecutionsRequest"] }, {"name":"describeAutomationStepExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationStepExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationStepExecutionsRequest"] }, {"name":"describeAutomationStepExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationStepExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationStepExecutionsRequest"] }, {"name":"describeAvailablePatches","parameterTypes":[] }, {"name":"describeAvailablePatches","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAvailablePatches","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAvailablePatchesRequest"] }, {"name":"describeAvailablePatchesPaginator","parameterTypes":[] }, {"name":"describeAvailablePatchesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAvailablePatchesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAvailablePatchesRequest"] }, {"name":"describeDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeDocumentRequest"] }, {"name":"describeDocumentPermission","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeDocumentPermission","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeDocumentPermissionRequest"] }, {"name":"describeEffectiveInstanceAssociations","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectiveInstanceAssociations","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectiveInstanceAssociationsRequest"] }, {"name":"describeEffectiveInstanceAssociationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectiveInstanceAssociationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectiveInstanceAssociationsRequest"] }, {"name":"describeEffectivePatchesForPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectivePatchesForPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectivePatchesForPatchBaselineRequest"] }, {"name":"describeEffectivePatchesForPatchBaselinePaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectivePatchesForPatchBaselinePaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectivePatchesForPatchBaselineRequest"] }, {"name":"describeInstanceAssociationsStatus","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceAssociationsStatus","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceAssociationsStatusRequest"] }, {"name":"describeInstanceAssociationsStatusPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceAssociationsStatusPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceAssociationsStatusRequest"] }, {"name":"describeInstanceInformation","parameterTypes":[] }, {"name":"describeInstanceInformation","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceInformation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceInformationRequest"] }, {"name":"describeInstanceInformationPaginator","parameterTypes":[] }, {"name":"describeInstanceInformationPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceInformationPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceInformationRequest"] }, {"name":"describeInstancePatchStates","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStates","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesRequest"] }, {"name":"describeInstancePatchStatesForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStatesForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesForPatchGroupRequest"] }, {"name":"describeInstancePatchStatesForPatchGroupPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStatesForPatchGroupPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesForPatchGroupRequest"] }, {"name":"describeInstancePatchStatesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStatesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesRequest"] }, {"name":"describeInstancePatches","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatches","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchesRequest"] }, {"name":"describeInstancePatchesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchesRequest"] }, {"name":"describeInstanceProperties","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceProperties","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePropertiesRequest"] }, {"name":"describeInstancePropertiesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePropertiesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePropertiesRequest"] }, {"name":"describeInventoryDeletions","parameterTypes":[] }, {"name":"describeInventoryDeletions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInventoryDeletions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInventoryDeletionsRequest"] }, {"name":"describeInventoryDeletionsPaginator","parameterTypes":[] }, {"name":"describeInventoryDeletionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInventoryDeletionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInventoryDeletionsRequest"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocations","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocations","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTaskInvocationsRequest"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTaskInvocationsRequest"] }, {"name":"describeMaintenanceWindowExecutionTasks","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTasks","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTasksRequest"] }, {"name":"describeMaintenanceWindowExecutionTasksPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTasksPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTasksRequest"] }, {"name":"describeMaintenanceWindowExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionsRequest"] }, {"name":"describeMaintenanceWindowExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionsRequest"] }, {"name":"describeMaintenanceWindowSchedule","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowSchedule","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowScheduleRequest"] }, {"name":"describeMaintenanceWindowSchedulePaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowSchedulePaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowScheduleRequest"] }, {"name":"describeMaintenanceWindowTargets","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTargets","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTargetsRequest"] }, {"name":"describeMaintenanceWindowTargetsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTargetsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTargetsRequest"] }, {"name":"describeMaintenanceWindowTasks","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTasks","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTasksRequest"] }, {"name":"describeMaintenanceWindowTasksPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTasksPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTasksRequest"] }, {"name":"describeMaintenanceWindows","parameterTypes":[] }, {"name":"describeMaintenanceWindows","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindows","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsRequest"] }, {"name":"describeMaintenanceWindowsForTarget","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowsForTarget","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsForTargetRequest"] }, {"name":"describeMaintenanceWindowsForTargetPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowsForTargetPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsForTargetRequest"] }, {"name":"describeMaintenanceWindowsPaginator","parameterTypes":[] }, {"name":"describeMaintenanceWindowsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsRequest"] }, {"name":"describeOpsItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeOpsItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeOpsItemsRequest"] }, {"name":"describeOpsItemsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeOpsItemsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeOpsItemsRequest"] }, {"name":"describeParameters","parameterTypes":[] }, {"name":"describeParameters","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeParameters","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeParametersRequest"] }, {"name":"describeParametersPaginator","parameterTypes":[] }, {"name":"describeParametersPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeParametersPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeParametersRequest"] }, {"name":"describePatchBaselines","parameterTypes":[] }, {"name":"describePatchBaselines","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchBaselines","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchBaselinesRequest"] }, {"name":"describePatchBaselinesPaginator","parameterTypes":[] }, {"name":"describePatchBaselinesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchBaselinesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchBaselinesRequest"] }, {"name":"describePatchGroupState","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchGroupState","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchGroupStateRequest"] }, {"name":"describePatchGroups","parameterTypes":[] }, {"name":"describePatchGroups","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchGroups","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchGroupsRequest"] }, {"name":"describePatchGroupsPaginator","parameterTypes":[] }, {"name":"describePatchGroupsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchGroupsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchGroupsRequest"] }, {"name":"describePatchProperties","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchProperties","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchPropertiesRequest"] }, {"name":"describePatchPropertiesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchPropertiesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchPropertiesRequest"] }, {"name":"describeSessions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeSessions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeSessionsRequest"] }, {"name":"describeSessionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeSessionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeSessionsRequest"] }, {"name":"disassociateOpsItemRelatedItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"disassociateOpsItemRelatedItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.DisassociateOpsItemRelatedItemRequest"] }, {"name":"getAutomationExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"getAutomationExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetAutomationExecutionRequest"] }, {"name":"getCalendarState","parameterTypes":["java.util.function.Consumer"] }, {"name":"getCalendarState","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetCalendarStateRequest"] }, {"name":"getCommandInvocation","parameterTypes":["java.util.function.Consumer"] }, {"name":"getCommandInvocation","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetCommandInvocationRequest"] }, {"name":"getConnectionStatus","parameterTypes":["java.util.function.Consumer"] }, {"name":"getConnectionStatus","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetConnectionStatusRequest"] }, {"name":"getDefaultPatchBaseline","parameterTypes":[] }, {"name":"getDefaultPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"getDefaultPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetDefaultPatchBaselineRequest"] }, {"name":"getDeployablePatchSnapshotForInstance","parameterTypes":["java.util.function.Consumer"] }, {"name":"getDeployablePatchSnapshotForInstance","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetDeployablePatchSnapshotForInstanceRequest"] }, {"name":"getDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"getDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetDocumentRequest"] }, {"name":"getInventory","parameterTypes":[] }, {"name":"getInventory","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventory","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventoryRequest"] }, {"name":"getInventoryPaginator","parameterTypes":[] }, {"name":"getInventoryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventoryPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventoryRequest"] }, {"name":"getInventorySchema","parameterTypes":[] }, {"name":"getInventorySchema","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventorySchema","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventorySchemaRequest"] }, {"name":"getInventorySchemaPaginator","parameterTypes":[] }, {"name":"getInventorySchemaPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventorySchemaPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventorySchemaRequest"] }, {"name":"getMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowRequest"] }, {"name":"getMaintenanceWindowExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowExecutionRequest"] }, {"name":"getMaintenanceWindowExecutionTask","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowExecutionTask","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowExecutionTaskRequest"] }, {"name":"getMaintenanceWindowExecutionTaskInvocation","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowExecutionTaskInvocation","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowExecutionTaskInvocationRequest"] }, {"name":"getMaintenanceWindowTask","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowTask","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowTaskRequest"] }, {"name":"getOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsItemRequest"] }, {"name":"getOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsMetadataRequest"] }, {"name":"getOpsSummary","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsSummary","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsSummaryRequest"] }, {"name":"getOpsSummaryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsSummaryPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsSummaryRequest"] }, {"name":"getParameter","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameter","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParameterRequest"] }, {"name":"getParameterHistory","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameterHistory","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParameterHistoryRequest"] }, {"name":"getParameterHistoryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameterHistoryPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParameterHistoryRequest"] }, {"name":"getParameters","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameters","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParametersRequest"] }, {"name":"getParametersByPath","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParametersByPath","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest"] }, {"name":"getParametersByPathPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParametersByPathPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest"] }, {"name":"getPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"getPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetPatchBaselineRequest"] }, {"name":"getPatchBaselineForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"getPatchBaselineForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetPatchBaselineForPatchGroupRequest"] }, {"name":"getResourcePolicies","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePolicies","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetResourcePoliciesRequest"] }, {"name":"getResourcePoliciesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePoliciesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetResourcePoliciesRequest"] }, {"name":"getServiceSetting","parameterTypes":["java.util.function.Consumer"] }, {"name":"getServiceSetting","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetServiceSettingRequest"] }, {"name":"labelParameterVersion","parameterTypes":["java.util.function.Consumer"] }, {"name":"labelParameterVersion","parameterTypes":["software.amazon.awssdk.services.ssm.model.LabelParameterVersionRequest"] }, {"name":"listAssociationVersions","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociationVersions","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationVersionsRequest"] }, {"name":"listAssociationVersionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociationVersionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationVersionsRequest"] }, {"name":"listAssociations","parameterTypes":[] }, {"name":"listAssociations","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociations","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationsRequest"] }, {"name":"listAssociationsPaginator","parameterTypes":[] }, {"name":"listAssociationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationsRequest"] }, {"name":"listCommandInvocations","parameterTypes":[] }, {"name":"listCommandInvocations","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommandInvocations","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandInvocationsRequest"] }, {"name":"listCommandInvocationsPaginator","parameterTypes":[] }, {"name":"listCommandInvocationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommandInvocationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandInvocationsRequest"] }, {"name":"listCommands","parameterTypes":[] }, {"name":"listCommands","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommands","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandsRequest"] }, {"name":"listCommandsPaginator","parameterTypes":[] }, {"name":"listCommandsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommandsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandsRequest"] }, {"name":"listComplianceItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceItemsRequest"] }, {"name":"listComplianceItemsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceItemsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceItemsRequest"] }, {"name":"listComplianceSummaries","parameterTypes":[] }, {"name":"listComplianceSummaries","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceSummaries","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceSummariesRequest"] }, {"name":"listComplianceSummariesPaginator","parameterTypes":[] }, {"name":"listComplianceSummariesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceSummariesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceSummariesRequest"] }, {"name":"listDocumentMetadataHistory","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentMetadataHistory","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentMetadataHistoryRequest"] }, {"name":"listDocumentVersions","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentVersions","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentVersionsRequest"] }, {"name":"listDocumentVersionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentVersionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentVersionsRequest"] }, {"name":"listDocuments","parameterTypes":[] }, {"name":"listDocuments","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocuments","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentsRequest"] }, {"name":"listDocumentsPaginator","parameterTypes":[] }, {"name":"listDocumentsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentsRequest"] }, {"name":"listInventoryEntries","parameterTypes":["java.util.function.Consumer"] }, {"name":"listInventoryEntries","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListInventoryEntriesRequest"] }, {"name":"listOpsItemEvents","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemEvents","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemEventsRequest"] }, {"name":"listOpsItemEventsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemEventsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemEventsRequest"] }, {"name":"listOpsItemRelatedItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemRelatedItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemRelatedItemsRequest"] }, {"name":"listOpsItemRelatedItemsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemRelatedItemsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemRelatedItemsRequest"] }, {"name":"listOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsMetadataRequest"] }, {"name":"listOpsMetadataPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsMetadataPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsMetadataRequest"] }, {"name":"listResourceComplianceSummaries","parameterTypes":[] }, {"name":"listResourceComplianceSummaries","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceComplianceSummaries","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceComplianceSummariesRequest"] }, {"name":"listResourceComplianceSummariesPaginator","parameterTypes":[] }, {"name":"listResourceComplianceSummariesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceComplianceSummariesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceComplianceSummariesRequest"] }, {"name":"listResourceDataSync","parameterTypes":[] }, {"name":"listResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceDataSyncRequest"] }, {"name":"listResourceDataSyncPaginator","parameterTypes":[] }, {"name":"listResourceDataSyncPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceDataSyncPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceDataSyncRequest"] }, {"name":"listTagsForResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTagsForResource","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListTagsForResourceRequest"] }, {"name":"modifyDocumentPermission","parameterTypes":["java.util.function.Consumer"] }, {"name":"modifyDocumentPermission","parameterTypes":["software.amazon.awssdk.services.ssm.model.ModifyDocumentPermissionRequest"] }, {"name":"putComplianceItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"putComplianceItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutComplianceItemsRequest"] }, {"name":"putInventory","parameterTypes":["java.util.function.Consumer"] }, {"name":"putInventory","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutInventoryRequest"] }, {"name":"putParameter","parameterTypes":["java.util.function.Consumer"] }, {"name":"putParameter","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutParameterRequest"] }, {"name":"putResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"putResourcePolicy","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutResourcePolicyRequest"] }, {"name":"registerDefaultPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerDefaultPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterDefaultPatchBaselineRequest"] }, {"name":"registerPatchBaselineForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerPatchBaselineForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterPatchBaselineForPatchGroupRequest"] }, {"name":"registerTargetWithMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerTargetWithMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterTargetWithMaintenanceWindowRequest"] }, {"name":"registerTaskWithMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerTaskWithMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterTaskWithMaintenanceWindowRequest"] }, {"name":"removeTagsFromResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"removeTagsFromResource","parameterTypes":["software.amazon.awssdk.services.ssm.model.RemoveTagsFromResourceRequest"] }, {"name":"resetServiceSetting","parameterTypes":["java.util.function.Consumer"] }, {"name":"resetServiceSetting","parameterTypes":["software.amazon.awssdk.services.ssm.model.ResetServiceSettingRequest"] }, {"name":"resumeSession","parameterTypes":["java.util.function.Consumer"] }, {"name":"resumeSession","parameterTypes":["software.amazon.awssdk.services.ssm.model.ResumeSessionRequest"] }, {"name":"sendAutomationSignal","parameterTypes":["java.util.function.Consumer"] }, {"name":"sendAutomationSignal","parameterTypes":["software.amazon.awssdk.services.ssm.model.SendAutomationSignalRequest"] }, {"name":"sendCommand","parameterTypes":["java.util.function.Consumer"] }, {"name":"sendCommand","parameterTypes":["software.amazon.awssdk.services.ssm.model.SendCommandRequest"] }, {"name":"serviceClientConfiguration","parameterTypes":[] }, {"name":"startAssociationsOnce","parameterTypes":["java.util.function.Consumer"] }, {"name":"startAssociationsOnce","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartAssociationsOnceRequest"] }, {"name":"startAutomationExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"startAutomationExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartAutomationExecutionRequest"] }, {"name":"startChangeRequestExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"startChangeRequestExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartChangeRequestExecutionRequest"] }, {"name":"startSession","parameterTypes":["java.util.function.Consumer"] }, {"name":"startSession","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartSessionRequest"] }, {"name":"stopAutomationExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"stopAutomationExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.StopAutomationExecutionRequest"] }, {"name":"terminateSession","parameterTypes":["java.util.function.Consumer"] }, {"name":"terminateSession","parameterTypes":["software.amazon.awssdk.services.ssm.model.TerminateSessionRequest"] }, {"name":"unlabelParameterVersion","parameterTypes":["java.util.function.Consumer"] }, {"name":"unlabelParameterVersion","parameterTypes":["software.amazon.awssdk.services.ssm.model.UnlabelParameterVersionRequest"] }, {"name":"updateAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateAssociationRequest"] }, {"name":"updateAssociationStatus","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateAssociationStatus","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateAssociationStatusRequest"] }, {"name":"updateDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateDocumentRequest"] }, {"name":"updateDocumentDefaultVersion","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateDocumentDefaultVersion","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateDocumentDefaultVersionRequest"] }, {"name":"updateDocumentMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateDocumentMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateDocumentMetadataRequest"] }, {"name":"updateMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateMaintenanceWindowRequest"] }, {"name":"updateMaintenanceWindowTarget","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateMaintenanceWindowTarget","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateMaintenanceWindowTargetRequest"] }, {"name":"updateMaintenanceWindowTask","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateMaintenanceWindowTask","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateMaintenanceWindowTaskRequest"] }, {"name":"updateManagedInstanceRole","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateManagedInstanceRole","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateManagedInstanceRoleRequest"] }, {"name":"updateOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateOpsItemRequest"] }, {"name":"updateOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateOpsMetadataRequest"] }, {"name":"updatePatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"updatePatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdatePatchBaselineRequest"] }, {"name":"updateResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateResourceDataSyncRequest"] }, {"name":"updateServiceSetting","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateServiceSetting","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateServiceSettingRequest"] }, {"name":"waiter","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.utils.SdkAutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"close","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.BaseProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"get","parameterTypes":["java.lang.String"] }, {"name":"get","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"getMultiple","parameterTypes":["java.lang.String"] }, {"name":"now","parameterTypes":[] }, {"name":"withMaxAge","parameterTypes":["int","java.time.temporal.ChronoUnit"] }, {"name":"withTransformation","parameterTypes":["java.lang.Class"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.ParamProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.lambda.powertools.parameters.ssm.SSMParamAspect", + "fields":[{"name":"providerBuilder"}] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.ssm.SSMProvider", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getMultipleValues","parameterTypes":["java.lang.String"] }, {"name":"getValue","parameterTypes":["java.lang.String"] }, {"name":"recursive","parameterTypes":[] }, {"name":"resetToDefaults","parameterTypes":[] }, {"name":"withDecryption","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.transform.TransformationManager", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"performBasicTransformation","parameterTypes":["java.lang.String"] }, {"name":"performComplexTransformation","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"setTransformer","parameterTypes":["java.lang.Class"] }, {"name":"shouldTransform","parameterTypes":[] }] +}, +{ + "name": "software.amazon.lambda.powertools.parameters.transform.JsonTransformer", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods": [{"name": "applyTransformation","parameterTypes":["java.lang.String","java.lang.Class"]}] +}, +{ + "name": "software.amazon.lambda.powertools.parameters.transform.Base64Transformer", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods": [{"name": "applyTransformation","parameterTypes":["java.lang.String"]}] +}, +{ + "name": "software.amazon.lambda.powertools.parameters.transform.BasicTransformer", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods": [{"name": "applyTransformation","parameterTypes":["java.lang.String","java.lang.Class"]}] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.IssuerAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.NetscapeCertTypeExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.PrivateKeyUsageExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectAlternativeNameExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.ssm.internal.ParametersSsmUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/resource-config.json b/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/resource-config.json new file mode 100644 index 000000000..6d8f3660f --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/resource-config.json @@ -0,0 +1,23 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/services/ssm/execution.interceptors\\E" + }, { + "pattern":"\\Qversion.properties\\E" + }, { + "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/nfc.nrm\\E" + }]}, + "bundles":[] +} diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-parameters/powertools-parameters-ssm/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..4cce863f6 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.parameters.ssm.internal.ParametersSsmUserAgentInterceptor diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java new file mode 100644 index 000000000..abfc1d7fa --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.ssm; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class SSMParamAspectTest { + + // This class tests the SSM Param aspect in the same fashion + // as the tests for the aspects for the other providers. + + @Test + void parameterInjectedByProvider() throws Exception { + + String key = "myKey"; + String value = "mySecretValue"; + SSMProvider provider = Mockito.mock(SSMProvider.class); + + Supplier<SSMProvider> providerBuilder = () -> provider; + writeStaticField(SSMParamAspect.class, "providerBuilder", providerBuilder, true); + + // Setup our mocked SSMProvider to return a value for our test data + Mockito.when(provider.get(key)).thenReturn(value); + + // Create an instance of a class and let the SSMParamAspect inject it + MyInjectedClass obj = new MyInjectedClass(); + assertThat(obj.mySecret).isEqualTo(value); + } + + class MyInjectedClass { + @SSMParam(key = "myKey") + public String mySecret; + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java similarity index 57% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java rename to powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index 2a4f8f927..fb475a737 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -12,27 +12,29 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.ssm; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; + import org.assertj.core.data.MapEntry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + import software.amazon.awssdk.services.ssm.SsmClient; import software.amazon.awssdk.services.ssm.model.GetParameterRequest; import software.amazon.awssdk.services.ssm.model.GetParameterResponse; @@ -42,7 +44,7 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -public class SSMProviderTest { +class SSMProviderTest { @Mock SsmClient client; @@ -61,14 +63,14 @@ public class SSMProviderTest { SSMProvider provider; @BeforeEach - public void init() { - openMocks(this); + void init() { + MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); - provider = new SSMProvider(cacheManager, client); + provider = new SSMProvider(cacheManager, null, client); } @Test - public void getValue() { + void getValue() { String key = "Key1"; String expectedValue = "Value1"; initMock(expectedValue); @@ -81,7 +83,7 @@ public void getValue() { } @Test - public void getValueDecrypted() { + void getValueDecrypted() { String key = "Key2"; String expectedValue = "Value2"; initMock(expectedValue); @@ -94,13 +96,13 @@ public void getValueDecrypted() { } @Test - public void getMultiple() { + void getMultiple() { List<Parameter> parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); parameters.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response = GetParametersByPathResponse.builder().parameters(parameters).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); Map<String, String> params = provider.getMultiple("/prod/app1"); assertThat(params).contains( @@ -117,13 +119,13 @@ public void getMultiple() { } @Test - public void getMultipleWithTrailingSlash() { + void getMultipleWithTrailingSlash() { List<Parameter> parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); parameters.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response = GetParametersByPathResponse.builder().parameters(parameters).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); Map<String, String> params = provider.getMultiple("/prod/app1/"); assertThat(params).contains( @@ -140,13 +142,13 @@ public void getMultipleWithTrailingSlash() { } @Test - public void getMultiple_cached_shouldNotCallSSM() { + void getMultiple_cached_shouldNotCallSSM() { List<Parameter> parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); parameters.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response = GetParametersByPathResponse.builder().parameters(parameters).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); provider.getMultiple("/prod/app1"); @@ -156,23 +158,24 @@ public void getMultiple_cached_shouldNotCallSSM() { provider.get("/prod/app1/key2"); provider.get("/prod/app1/key3"); - verify(client, times(1)).getParametersByPath(any(GetParametersByPathRequest.class)); + Mockito.verify(client, Mockito.times(1)) + .getParametersByPath(ArgumentMatchers.any(GetParametersByPathRequest.class)); } @Test - public void getMultipleWithNextToken() { + void getMultipleWithNextToken() { List<Parameter> parameters1 = new ArrayList<>(); parameters1.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters1.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); - GetParametersByPathResponse response1 = - GetParametersByPathResponse.builder().parameters(parameters1).nextToken("123abc").build(); + GetParametersByPathResponse response1 = GetParametersByPathResponse.builder().parameters(parameters1) + .nextToken("123abc").build(); List<Parameter> parameters2 = new ArrayList<>(); parameters2.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response2 = GetParametersByPathResponse.builder().parameters(parameters2).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response1, response2); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response1, response2); Map<String, String> params = provider.getMultiple("/prod/app1"); @@ -185,33 +188,124 @@ public void getMultipleWithNextToken() { GetParametersByPathRequest request1 = requestParams.get(0); GetParametersByPathRequest request2 = requestParams.get(1); - assertThat(asList(request1, request2)).allSatisfy(req -> - { - assertThat(req.path()).isEqualTo("/prod/app1"); - assertThat(req.withDecryption()).isFalse(); - assertThat(req.recursive()).isFalse(); - }); + assertThat(asList(request1, request2)) + .isNotEmpty() + .allSatisfy(req -> { + assertThat(req.path()).isEqualTo("/prod/app1"); + assertThat(req.withDecryption()).isFalse(); + assertThat(req.recursive()).isFalse(); + }); assertThat(request1.nextToken()).isNull(); assertThat(request2.nextToken()).isEqualTo("123abc"); } @Test - public void testSecretsProviderBuilderMissingCacheManager_throwsException() { - - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> SSMProvider.builder() - .withClient(client) - .withTransformationManager(transformationManager) - .build()) - .withMessage("No CacheManager provided, please provide one"); + void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + + // Act + SSMProvider ssmProvider = SSMProvider.builder() + .build(); + // Assert + assertDoesNotThrow(() -> ssmProvider.withTransformation(json)); + } + + @Test + void withDecryption_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + Parameter param1 = Parameter.builder().value("value1").build(); + Parameter param2 = Parameter.builder().value("value2").build(); + GetParameterResponse response1 = GetParameterResponse.builder().parameter(param1).build(); + GetParameterResponse response2 = GetParameterResponse.builder().parameter(param2).build(); + CountDownLatch latch = new CountDownLatch(2); + Mockito.when(client.getParameter(paramCaptor.capture())) + .thenReturn(response1, response2); + + // WHEN + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.withDecryption().getValue("key1"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.getValue("key2"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + // THEN + List<GetParameterRequest> requests = paramCaptor.getAllValues(); + assertThat(requests) + .hasSize(2) + .anyMatch(GetParameterRequest::withDecryption) + .anyMatch(r -> !r.withDecryption()); + } + + @Test + void recursive_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + List<Parameter> params1 = new ArrayList<>(); + params1.add(Parameter.builder().name("/path1/key1").value("value1").build()); + List<Parameter> params2 = new ArrayList<>(); + params2.add(Parameter.builder().name("/path2/key2").value("value2").build()); + GetParametersByPathResponse response1 = GetParametersByPathResponse.builder().parameters(params1).build(); + GetParametersByPathResponse response2 = GetParametersByPathResponse.builder().parameters(params2).build(); + CountDownLatch latch = new CountDownLatch(2); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())) + .thenReturn(response1, response2); + + // WHEN + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.recursive().getMultiple("/path1"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.getMultiple("/path2"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + // THEN + List<GetParametersByPathRequest> requests = paramByPathCaptor.getAllValues(); + assertThat(requests) + .hasSize(2) + .anyMatch(GetParametersByPathRequest::recursive) + .anyMatch(r -> !r.recursive()); } private void initMock(String expectedValue) { Parameter parameter = Parameter.builder().value(expectedValue).build(); GetParameterResponse result = GetParameterResponse.builder().parameter(parameter).build(); - when(client.getParameter(paramCaptor.capture())).thenReturn(result); - provider.defaultMaxAge(1, ChronoUnit.DAYS); + Mockito.when(client.getParameter(paramCaptor.capture())).thenReturn(result); provider.withMaxAge(2, ChronoUnit.DAYS); provider.recursive(); } diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptorTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptorTest.java new file mode 100644 index 000000000..8f6db7e21 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.parameters.ssm.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class ParametersSsmUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/PARAMETERS-SSM/"); + } +} \ No newline at end of file diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml new file mode 100644 index 000000000..fa2542730 --- /dev/null +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parent</artifactId> + <version>2.9.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <name>Powertools for AWS Lambda (Java) library Parameters - Tests</name> + <artifactId>powertools-parameters-tests</artifactId> + <description>Powertools parameters tests that cut across all the parameters providers</description> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters-ssm</artifactId> + <scope>test</scope> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters-secrets</artifactId> + <scope>test</scope> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters-dynamodb</artifactId> + <scope>test</scope> + <version>${project.version}</version> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>default</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + + <!-- Required explicitly for @Captor ArgumentCaptor --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-parameters</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.4</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java similarity index 75% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java index edc671e2c..dd31ce016 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java @@ -20,44 +20,42 @@ import static java.time.temporal.ChronoUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.MockitoAnnotations.openMocks; import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.time.Clock; +import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Base64; import java.util.HashMap; import java.util.Map; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -public class BaseProviderTest { +class BaseProviderTest { Clock clock; CacheManager cacheManager; TransformationManager transformationManager; BasicProvider provider; - boolean getFromStore = false; @BeforeEach - public void setup() { - openMocks(this); - + void setup() { clock = Clock.systemDefaultZone(); cacheManager = new CacheManager(); - provider = new BasicProvider(cacheManager); transformationManager = new TransformationManager(); - provider.setTransformationManager(transformationManager); + provider = new BasicProvider(cacheManager, transformationManager); } @Test - public void get_notCached_shouldGetValue() { + void get_notCached_shouldGetValue() { String foo = provider.get("toto"); assertThat(foo).isEqualTo("valueFromStore"); @@ -65,7 +63,7 @@ public void get_notCached_shouldGetValue() { } @Test - public void get_cached_shouldGetFromCache() { + void get_cached_shouldGetFromCache() { provider.get("foo"); getFromStore = false; @@ -75,7 +73,7 @@ public void get_cached_shouldGetFromCache() { } @Test - public void get_expired_shouldGetValue() { + void get_expired_shouldGetValue() { provider.get("bar"); getFromStore = false; @@ -86,7 +84,7 @@ public void get_expired_shouldGetValue() { } @Test - public void getMultiple_notCached_shouldGetValue() { + void getMultiple_notCached_shouldGetValue() { Map<String, String> foo = provider.getMultiple("toto"); assertThat(foo.get("toto")).isEqualTo("valueFromStore"); @@ -94,7 +92,7 @@ public void getMultiple_notCached_shouldGetValue() { } @Test - public void getMultiple_cached_shouldGetFromCache() { + void getMultiple_cached_shouldGetFromCache() { provider.getMultiple("foo"); getFromStore = false; @@ -104,7 +102,7 @@ public void getMultiple_cached_shouldGetFromCache() { } @Test - public void getMultiple_expired_shouldGetValue() { + void getMultiple_expired_shouldGetValue() { provider.getMultiple("bar"); getFromStore = false; @@ -115,7 +113,7 @@ public void getMultiple_expired_shouldGetValue() { } @Test - public void get_customTTL_cached_shouldGetFromCache() { + void get_customTTL_cached_shouldGetFromCache() { provider.withMaxAge(12, ChronoUnit.MINUTES).get("key"); getFromStore = false; @@ -126,7 +124,7 @@ public void get_customTTL_cached_shouldGetFromCache() { } @Test - public void get_customTTL_expired_shouldGetValue() { + void get_customTTL_expired_shouldGetValue() { provider.withMaxAge(2, ChronoUnit.MINUTES).get("mykey"); getFromStore = false; @@ -137,8 +135,9 @@ public void get_customTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTL_cached_shouldGetFromCache() { - provider.defaultMaxAge(12, ChronoUnit.MINUTES).get("foobar"); + void get_customDefaultTTL_cached_shouldGetFromCache() { + provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); + provider.get("foobar"); getFromStore = false; provider.setClock(offset(clock, of(10, MINUTES))); @@ -148,8 +147,8 @@ public void get_customDefaultTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTL_expired_shouldGetValue() { - provider.defaultMaxAge(2, ChronoUnit.MINUTES).get("barbaz"); + void get_customDefaultTTL_expired_shouldGetValue() { + provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); getFromStore = false; provider.setClock(offset(clock, of(3, MINUTES))); @@ -159,10 +158,8 @@ public void get_customDefaultTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { - provider.defaultMaxAge(12, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) - .get("foobaz"); + void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { + provider.get("foobaz"); getFromStore = false; provider.setClock(offset(clock, of(4, SECONDS))); @@ -172,10 +169,11 @@ public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { - provider.defaultMaxAge(2, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) - .get("bariton"); + void get_customDefaultTTLAndTTL_expired_shouldGetValue() { + + provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); + + provider.withMaxAge(5, SECONDS).get("bariton"); getFromStore = false; provider.setClock(offset(clock, of(6, SECONDS))); @@ -185,7 +183,7 @@ public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { } @Test - public void get_basicTransformation_shouldTransformInString() { + void get_basicTransformation_shouldTransformInString() { provider.setValue(Base64.getEncoder().encodeToString("bar".getBytes())); String value = provider.withTransformation(Transformer.base64).get("base64"); @@ -194,20 +192,20 @@ public void get_basicTransformation_shouldTransformInString() { } @Test - public void get_complexTransformation_shouldTransformInObject() { + void get_complexTransformation_shouldTransformInObject() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - ObjectToDeserialize objectToDeserialize = - provider.withTransformation(json).get("foo", ObjectToDeserialize.class); + ObjectToDeserialize objectToDeserialize = provider.withTransformation(json).get("foo", + ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( - o -> o.getFoo().equals("Foo") + o -> "Foo".equals(o.getFoo()) && o.getBar() == 42 && o.getBaz() == 123456789); } @Test - public void getObject_notCached_shouldGetValue() { + void getObject_notCached_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); ObjectToDeserialize foo = provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -217,7 +215,7 @@ public void getObject_notCached_shouldGetValue() { } @Test - public void getObject_cached_shouldGetFromCache() { + void getObject_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -229,7 +227,7 @@ public void getObject_cached_shouldGetFromCache() { } @Test - public void getObject_expired_shouldGetValue() { + void getObject_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -242,7 +240,7 @@ public void getObject_expired_shouldGetValue() { } @Test - public void getObject_customTTL_cached_shouldGetFromCache() { + void getObject_customTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withMaxAge(12, ChronoUnit.MINUTES) @@ -257,7 +255,7 @@ public void getObject_customTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customTTL_expired_shouldGetValue() { + void getObject_customTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withMaxAge(2, ChronoUnit.MINUTES) @@ -272,11 +270,12 @@ public void getObject_customTTL_expired_shouldGetValue() { } @Test - public void getObject_customDefaultTTL_cached_shouldGetFromCache() { + void getObject_customDefaultTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(12, ChronoUnit.MINUTES) - .withTransformation(json) + provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); + + provider.withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -287,11 +286,12 @@ public void getObject_customDefaultTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customDefaultTTL_expired_shouldGetValue() { + void getObject_customDefaultTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(2, ChronoUnit.MINUTES) - .withTransformation(json) + provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); + + provider.withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -302,12 +302,13 @@ public void getObject_customDefaultTTL_expired_shouldGetValue() { } @Test - public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { + void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(12, ChronoUnit.MINUTES) + provider.cacheManager.setDefaultExpirationTime(Duration.ofSeconds(5)); + + provider.withTransformation(json) .withMaxAge(5, SECONDS) - .withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -318,11 +319,12 @@ public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { + void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(2, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) + provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); + + provider.withMaxAge(5, SECONDS) .withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -334,23 +336,22 @@ public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { } @Test - public void get_noTransformationManager_shouldThrowException() { - provider.setTransformationManager(null); + void get_noTransformationManager_shouldThrowException() { + provider = new BasicProvider(new CacheManager(), null); assertThatIllegalStateException() .isThrownBy(() -> provider.withTransformation(base64).get("foo")); } @Test - public void getObject_noTransformationManager_shouldThrowException() { - provider.setTransformationManager(null); + void getObject_noTransformationManager_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> provider.get("foo", ObjectToDeserialize.class)); } @Test - public void getTwoParams_shouldResetTTLOptionsInBetween() { + void getTwoParams_shouldResetTTLOptionsInBetween() { provider.withMaxAge(50, SECONDS).get("foo50"); provider.get("foo5"); @@ -364,7 +365,7 @@ public void getTwoParams_shouldResetTTLOptionsInBetween() { } @Test - public void getTwoParams_shouldResetTransformationOptionsInBetween() { + void getTwoParams_shouldResetTransformationOptionsInBetween() { provider.setValue(Base64.getEncoder().encodeToString("base64encoded".getBytes())); String foob64 = provider.withTransformation(base64).get("foob64"); @@ -379,11 +380,11 @@ class BasicProvider extends BaseProvider { private String value = "valueFromStore"; - public BasicProvider(CacheManager cacheManager) { - super(cacheManager); + public BasicProvider(CacheManager cacheManager, TransformationManager transformationManager) { + super(cacheManager, transformationManager); } - public void setValue(String value) { + void setValue(String value) { this.value = value; } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java similarity index 76% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java index d6fbe66f0..5bc609777 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java @@ -14,25 +14,24 @@ package software.amazon.lambda.powertools.parameters; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; + import org.assertj.core.data.MapEntry; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; +import org.mockito.junit.jupiter.MockitoExtension; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; @@ -43,35 +42,34 @@ import software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest; import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse; import software.amazon.awssdk.services.ssm.model.Parameter; +import software.amazon.lambda.powertools.parameters.secrets.SecretsProvider; +import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; -public class ParamManagerIntegrationTest { - +@ExtendWith(MockitoExtension.class) +class ParamProvidersIntegrationTest { @Mock SsmClient ssmClient; @Mock DynamoDbClient ddbClient; + @Captor ArgumentCaptor<GetParameterRequest> ssmParamCaptor; + @Captor ArgumentCaptor<GetParametersByPathRequest> ssmParamByPathCaptor; + @Mock SecretsManagerClient secretsManagerClient; + @Captor ArgumentCaptor<GetSecretValueRequest> secretsCaptor; - @Mock - private AppConfigDataClient appConfigDataClient; - - @BeforeEach - public void setup() throws IllegalAccessException { - openMocks(this); - - writeStaticField(ParamManager.class, "providers", new ConcurrentHashMap<>(), true); - } @Test - public void ssmProvider_get() { - SSMProvider ssmProvider = ParamManager.getSsmProvider(ssmClient); + void ssmProvider_get() { + SSMProvider ssmProvider = SSMProvider.builder() + .withClient(ssmClient) + .build(); String expectedValue = "value"; Parameter parameter = Parameter.builder().value(expectedValue).build(); @@ -86,8 +84,10 @@ public void ssmProvider_get() { } @Test - public void ssmProvider_getMultiple() { - SSMProvider ssmProvider = ParamManager.getSsmProvider(ssmClient); + void ssmProvider_getMultiple() { + SSMProvider ssmProvider = SSMProvider.builder() + .withClient(ssmClient) + .build(); List<Parameter> parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); @@ -111,8 +111,10 @@ public void ssmProvider_getMultiple() { } @Test - public void secretsProvider_get() { - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(secretsManagerClient); + void secretsProvider_get() { + SecretsProvider secretsProvider = SecretsProvider.builder() + .withClient(secretsManagerClient) + .build(); String expectedValue = "Value1"; GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(expectedValue).build(); @@ -125,24 +127,4 @@ public void secretsProvider_get() { verify(secretsManagerClient, times(1)).getSecretValue(any(GetSecretValueRequest.class)); } - @Test - public void getDynamoDbProvider() { - - // Act - DynamoDbProvider provider = ParamManager.getDynamoDbProvider(ddbClient, "test-table"); - - // Assert - assertThat(provider).isNotNull(); - } - - @Test - public void getAppConfigProvider() { - - // Act - AppConfigProvider provider = ParamManager.getAppConfigProvider(appConfigDataClient, "test-env", "test-app"); - - // Assert - assertThat(provider).isNotNull(); - - } } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java new file mode 100644 index 000000000..d22572fad --- /dev/null +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java @@ -0,0 +1,196 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.cache; + +import static java.time.Clock.offset; +import static java.time.Duration.of; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Clock; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class CacheManagerTest { + + CacheManager manager; + + Clock clock; + + @BeforeEach + void setup() { + clock = Clock.systemDefaultZone(); + manager = new CacheManager(); + } + + @Test + void getIfNotExpired_notExpired_shouldReturnValue() { + manager.putInCache("key", "value"); + + Optional<String> value = manager.getIfNotExpired("key", clock.instant()); + + assertThat(value).isPresent().contains("value"); + } + + @Test + void getIfNotExpired_expired_shouldReturnNothing() { + manager.putInCache("key", "value"); + + Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(6, SECONDS)).instant()); + + assertThat(value).isNotPresent(); + } + + @Test + void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { + manager.setExpirationTime(of(42, SECONDS)); + manager.putInCache("key", "value"); + + Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); + + assertThat(value).isPresent().contains("value"); + } + + @Test + void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldReturnValue() { + manager.setDefaultExpirationTime(of(42, SECONDS)); + manager.putInCache("key", "value"); + + Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); + + assertThat(value).isPresent().contains("value"); + } + + @Test + void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_shouldUseExpirationTime() { + manager.setDefaultExpirationTime(of(42, SECONDS)); + manager.setExpirationTime(of(2, SECONDS)); + manager.putInCache("key", "value"); + + Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); + + assertThat(value).isNotPresent(); + } + + @Test + void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { + manager.setDefaultExpirationTime(of(42, SECONDS)); + manager.setExpirationTime(of(2, SECONDS)); + manager.putInCache("key", "value"); + manager.resetExpirationTime(); + manager.putInCache("key2", "value2"); + + Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); + Optional<String> value2 = manager.getIfNotExpired("key2", offset(clock, of(40, SECONDS)).instant()); + + assertThat(value).isNotPresent(); + assertThat(value2).isPresent().contains("value2"); + } + + @Test + void putInCache_sharedCache_shouldBeAccessibleAcrossThreads() throws InterruptedException { + // GIVEN + Thread thread1 = new Thread(() -> { + manager.setExpirationTime(of(60, SECONDS)); + manager.putInCache("sharedKey", "valueFromThread1"); + manager.resetExpirationTime(); + }); + + Thread thread2 = new Thread(() -> { + manager.setExpirationTime(of(10, SECONDS)); + // Thread 2 should be able to read the value cached by Thread 1 + Optional<String> value = manager.getIfNotExpired("sharedKey", clock.instant()); + assertThat(value).isPresent().contains("valueFromThread1"); + manager.resetExpirationTime(); + }); + + // WHEN + thread1.start(); + thread1.join(); + thread2.start(); + thread2.join(); + + // THEN - Both threads should be able to access the same cached value + Optional<String> value = manager.getIfNotExpired("sharedKey", clock.instant()); + assertThat(value).isPresent().contains("valueFromThread1"); + } + + @Test + void putInCache_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + boolean[] success = new boolean[threadCount]; + Clock testClock = Clock.systemDefaultZone(); + + // WHEN - Multiple threads set different expiration times and cache values concurrently + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final int expirationSeconds = (i % 2 == 0) ? 60 : 10; // Alternate between 60s and 10s + + threads[i] = new Thread(() -> { + try { + manager.setExpirationTime(of(expirationSeconds, SECONDS)); + manager.putInCache("key" + threadIndex, "value" + threadIndex); + manager.resetExpirationTime(); + success[threadIndex] = true; + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each cached value should have the correct expiration time + // Values with 60s TTL should still be present after 9s, values with 10s should expire after 11s + for (int i = 0; i < threadCount; i++) { + final int expirationSeconds = (i % 2 == 0) ? 60 : 10; + + // Check that value is still present just before expiration + Optional<String> valueBeforeExpiry = manager.getIfNotExpired("key" + i, + offset(testClock, of(expirationSeconds - 1, SECONDS)).instant()); + assertThat(valueBeforeExpiry) + .as("Thread %d with %ds expiration should still have value after %ds", i, expirationSeconds, + expirationSeconds - 1) + .isPresent() + .contains("value" + i); + + // Check that value expires after the TTL + Optional<String> valueAfterExpiry = manager.getIfNotExpired("key" + i, + offset(testClock, of(expirationSeconds + 1, SECONDS)).instant()); + assertThat(valueAfterExpiry) + .as("Thread %d with %ds expiration should not have value after %ds", i, expirationSeconds, + expirationSeconds + 1) + .isNotPresent(); + } + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java similarity index 84% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java index e86ded9be..0de31e63d 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java @@ -24,35 +24,35 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class DataStoreTest { +class DataStoreTest { Clock clock; DataStore store; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); store = new DataStore(); } @Test - public void put_shouldInsertInStore() { + void put_shouldInsertInStore() { store.put("key", "value", Instant.now()); assertThat(store.get("key")).isEqualTo("value"); } @Test - public void get_invalidKey_shouldReturnNull() { + void get_invalidKey_shouldReturnNull() { assertThat(store.get("key")).isNull(); } @Test - public void hasExpired_invalidKey_shouldReturnTrue() { + void hasExpired_invalidKey_shouldReturnTrue() { assertThat(store.hasExpired("key", clock.instant())).isTrue(); } @Test - public void hasExpired_notExpired_shouldReturnFalse() { + void hasExpired_notExpired_shouldReturnFalse() { Instant now = Instant.now(); store.put("key", "value", now.plus(10, SECONDS)); @@ -61,7 +61,7 @@ public void hasExpired_notExpired_shouldReturnFalse() { } @Test - public void hasExpired_expired_shouldReturnTrueAndRemoveElement() { + void hasExpired_expired_shouldReturnTrueAndRemoveElement() { Instant now = Instant.now(); store.put("key", "value", now.plus(10, SECONDS)); diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java similarity index 97% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java index 2c9db3712..a98d68c22 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java @@ -25,7 +25,7 @@ public class CustomProvider extends BaseProvider { private final Map<String, String> values = new HashMap<>(); public CustomProvider(CacheManager cacheManager) { - super(cacheManager); + super(cacheManager, null); values.put("/simple", "value"); values.put("/base64", Base64.getEncoder().encodeToString("value".getBytes())); values.put("/json", "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java similarity index 89% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java index ea713b552..8dbddacf6 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class Base64TransformerTest { +class Base64TransformerTest { @Test - public void transform_base64_shouldTransformInString() { + void transform_base64_shouldTransformInString() { Base64Transformer transformer = new Base64Transformer(); String s = transformer.applyTransformation(Base64.getEncoder().encodeToString("foobar".getBytes())); @@ -33,7 +33,7 @@ public void transform_base64_shouldTransformInString() { } @Test - public void transform_base64WrongFormat_shouldThrowException() { + void transform_base64WrongFormat_shouldThrowException() { Base64Transformer transformer = new Base64Transformer(); assertThatExceptionOfType(TransformationException.class) diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java similarity index 87% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java index 5cb980cc7..48cebb6b0 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java @@ -22,23 +22,23 @@ import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class JsonTransformerTest { +class JsonTransformerTest { @Test - public void transform_json_shouldTransformInObject() throws TransformationException { + void transform_json_shouldTransformInObject() throws TransformationException { JsonTransformer<ObjectToDeserialize> transformation = new JsonTransformer<>(); ObjectToDeserialize objectToDeserialize = transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( - o -> o.getFoo().equals("Foo") + o -> "Foo".equals(o.getFoo()) && o.getBar() == 42 && o.getBaz() == 123456789); } @Test - public void transform_json_shouldTransformInHashMap() throws TransformationException { + void transform_json_shouldTransformInHashMap() throws TransformationException { JsonTransformer<Map> transformation = new JsonTransformer<>(); Map<String, Object> map = @@ -50,7 +50,7 @@ public void transform_json_shouldTransformInHashMap() throws TransformationExcep } @Test - public void transform_badJson_shouldThrowException() { + void transform_badJson_shouldThrowException() { JsonTransformer<ObjectToDeserialize> transformation = new JsonTransformer<>(); assertThatExceptionOfType(TransformationException.class) diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java new file mode 100644 index 000000000..fad6f5391 --- /dev/null +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java @@ -0,0 +1,219 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters.transform; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; + +import java.util.Base64; +import java.util.concurrent.CountDownLatch; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import software.amazon.lambda.powertools.parameters.exception.TransformationException; + +class TransformationManagerTest { + + TransformationManager manager; + + Class<BasicTransformer> basicTransformer = BasicTransformer.class; + + @BeforeEach + void setup() { + manager = new TransformationManager(); + } + + @Test + void setTransformer_shouldTransform() { + manager.setTransformer(json); + + assertThat(manager.shouldTransform()).isTrue(); + } + + @Test + void notSetTransformer_shouldNotTransform() { + assertThat(manager.shouldTransform()).isFalse(); + } + + @Test + void performBasicTransformation_noTransformer_shouldThrowException() { + assertThatIllegalStateException() + .isThrownBy(() -> manager.performBasicTransformation("value")); + } + + @Test + void performBasicTransformation_notBasicTransformer_shouldThrowException() { + manager.setTransformer(json); + + assertThatIllegalStateException() + .isThrownBy(() -> manager.performBasicTransformation("value")); + } + + @Test + void performBasicTransformation_abstractTransformer_throwsTransformationException() { + manager.setTransformer(basicTransformer); + + assertThatExceptionOfType(TransformationException.class) + .isThrownBy(() -> manager.performBasicTransformation("value")); + } + + @Test + void performBasicTransformation_shouldPerformTransformation() { + manager.setTransformer(base64); + + String expectedValue = "bar"; + String value = manager.performBasicTransformation(Base64.getEncoder().encodeToString(expectedValue.getBytes())); + + assertThat(value).isEqualTo(expectedValue); + } + + @Test + void performComplexTransformation_noTransformer_shouldThrowException() { + assertThatIllegalStateException() + .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); + } + + @Test + void performComplexTransformation_shouldPerformTransformation() { + manager.setTransformer(json); + + ObjectToDeserialize object = manager.performComplexTransformation( + "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); + + assertThat(object).isNotNull(); + } + + @Test + void performComplexTransformation_throwsTransformationException() { + manager.setTransformer(basicTransformer); + + assertThatExceptionOfType(TransformationException.class) + .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); + } + + @Test + void unsetTransformer_shouldCleanUpThreadLocal() { + // GIVEN + manager.setTransformer(json); + assertThat(manager.shouldTransform()).isTrue(); + + // WHEN + manager.unsetTransformer(); + + // THEN + assertThat(manager.shouldTransform()).isFalse(); + } + + @Test + void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + boolean[] success = new boolean[2]; + CountDownLatch latch = new CountDownLatch(2); + + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(json); + // Thread 1 expects json transformer + String result = manager.performComplexTransformation( + "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class).getFoo(); + success[0] = "Foo".equals(result); + } catch (Exception e) { + e.printStackTrace(); + success[0] = false; + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(base64); + // Thread 2 expects base64 transformer + String result = manager.performBasicTransformation( + Base64.getEncoder().encodeToString("bar".getBytes())); + success[1] = "bar".equals(result); + } catch (Exception e) { + e.printStackTrace(); + success[1] = false; + } + }); + + // WHEN - Start both threads concurrently + thread1.start(); + thread2.start(); + + // THEN - Both threads should complete without errors + thread1.join(); + thread2.join(); + + assertThat(success[0]).as("Thread 1 with JSON transformer should succeed").isTrue(); + assertThat(success[1]).as("Thread 2 with Base64 transformer should succeed").isTrue(); + } + + @Test + void unsetTransformer_concurrentCalls_shouldNotAffectOtherThreads() throws InterruptedException { + // GIVEN + boolean[] success = new boolean[2]; + CountDownLatch latch = new CountDownLatch(2); + + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(json); + // Thread 1 should still have json transformer even if thread 2 unsets + assertThat(manager.shouldTransform()).isTrue(); + success[0] = true; + } catch (Exception e) { + e.printStackTrace(); + success[0] = false; + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(base64); + manager.unsetTransformer(); + // Thread 2 should have no transformer after unset + assertThat(manager.shouldTransform()).isFalse(); + success[1] = true; + } catch (Exception e) { + e.printStackTrace(); + success[1] = false; + } + }); + + // WHEN + thread1.start(); + thread2.start(); + + // THEN + thread1.join(); + thread2.join(); + + assertThat(success[0]).as("Thread 1 should still have transformer").isTrue(); + assertThat(success[1]).as("Thread 2 should have unset transformer").isTrue(); + } +} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java deleted file mode 100644 index f2e4faebb..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters; - -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.core.SdkSystemSetting; -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; -import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest; -import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationResponse; -import software.amazon.awssdk.services.appconfigdata.model.StartConfigurationSessionRequest; -import software.amazon.lambda.powertools.core.internal.UserAgentConfigurator; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; - -/** - * Implements a {@link ParamProvider} on top of the AppConfig service. AppConfig provides - * a mechanism to retrieve and update configuration of applications over time. - * AppConfig requires the user to create an application, environment, and configuration profile. - * The configuration profile's value can then be retrieved, by key name, through this provider. - * <p> - * Because AppConfig is designed to handle rollouts of configuration over time, we must first - * establish a session for each key we wish to retrieve, and then poll the session for the latest - * value when the user re-requests it. This means we must hold a keyed set of session tokens - * and values. - * - * @see <a href="https://docs.powertools.aws.dev/lambda/java/utilities/parameters/">Parameters provider documentation</a> - * @see <a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-working.html">AppConfig documentation</a> - */ -public class AppConfigProvider extends BaseProvider { - - private final AppConfigDataClient client; - private final String application; - private final String environment; - private final HashMap<String, EstablishedSession> establishedSessions = new HashMap<>(); - - AppConfigProvider(CacheManager cacheManager, AppConfigDataClient client, String environment, String application) { - super(cacheManager); - this.client = client; - this.application = application; - this.environment = environment; - } - - /** - * Create a builder that can be used to configure and create a {@link AppConfigProvider}. - * - * @return a new instance of {@link AppConfigProvider.Builder} - */ - public static AppConfigProvider.Builder builder() { - return new AppConfigProvider.Builder(); - } - - /** - * Retrieve the parameter value from the AppConfig parameter store.<br /> - * - * @param key key of the parameter. This ties back to AppConfig's 'profile' concept - * @return the value of the parameter identified by the key - */ - @Override - protected String getValue(String key) { - // Start a configuration session if we don't already have one for the key requested - // so that we can the initial token. If we already have a session, we can take - // the next request token from there. - EstablishedSession establishedSession = establishedSessions.getOrDefault(key, null); - String sessionToken = establishedSession != null ? - establishedSession.nextSessionToken : - client.startConfigurationSession(StartConfigurationSessionRequest.builder() - .applicationIdentifier(this.application) - .environmentIdentifier(this.environment) - .configurationProfileIdentifier(key) - .build()) - .initialConfigurationToken(); - - // Get the configuration using the token - GetLatestConfigurationResponse response = client.getLatestConfiguration(GetLatestConfigurationRequest.builder() - .configurationToken(sessionToken) - .build()); - - // Get the next session token we'll use next time we are asked for this key - String nextSessionToken = response.nextPollConfigurationToken(); - - // Get the value of the key. Note that AppConfig will return null if the value - // has not changed since we last asked for it in this session - in this case - // we return the value we stashed at last request. - String value = response.configuration() != null ? - response.configuration().asUtf8String() : // if we have a new value, use it - establishedSession != null ? - establishedSession.lastConfigurationValue : - // if we don't but we have a previous value, use that - null; // otherwise we've got no value - - // Update the cache so we can get the next value later - establishedSessions.put(key, new EstablishedSession(nextSessionToken, value)); - - return value; - } - - @Override - protected Map<String, String> getMultipleValues(String path) { - // Retrieving multiple values is not supported with the AppConfig provider. - throw new RuntimeException( - "Retrieving multiple parameter values is not supported with the AWS App Config Provider"); - } - - private static class EstablishedSession { - private final String nextSessionToken; - private final String lastConfigurationValue; - - private EstablishedSession(String nextSessionToken, String value) { - this.nextSessionToken = nextSessionToken; - this.lastConfigurationValue = value; - } - } - - static class Builder { - private AppConfigDataClient client; - private CacheManager cacheManager; - private TransformationManager transformationManager; - private String environment; - private String application; - - /** - * Create a {@link AppConfigProvider} instance. - * - * @return a {@link AppConfigProvider} - */ - public AppConfigProvider build() { - if (cacheManager == null) { - throw new IllegalStateException("No CacheManager provided; please provide one"); - } - if (environment == null) { - throw new IllegalStateException("No environment provided; please provide one"); - } - if (application == null) { - throw new IllegalStateException("No application provided; please provide one"); - } - - // Create a AppConfigDataClient if we haven't been given one - if (client == null) { - client = AppConfigDataClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) - .build(); - } - - AppConfigProvider provider = new AppConfigProvider(cacheManager, client, environment, application); - - if (transformationManager != null) { - provider.setTransformationManager(transformationManager); - } - return provider; - } - - /** - * Set custom {@link AppConfigProvider} to pass to the {@link AppConfigDataClient}. <br/> - * Use it if you want to customize the region or any other part of the client. - * - * @param client Custom client - * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) - */ - public AppConfigProvider.Builder withClient(AppConfigDataClient client) { - this.client = client; - return this; - } - - /** - * <b>Mandatory</b>. Provide an environment to the {@link AppConfigProvider} - * - * @param environment the AppConfig environment - * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) - */ - public AppConfigProvider.Builder withEnvironment(String environment) { - this.environment = environment; - return this; - } - - /** - * <b>Mandatory</b>. Provide an application to the {@link AppConfigProvider} - * - * @param application the application to pull configuration from - * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) - */ - public AppConfigProvider.Builder withApplication(String application) { - this.application = application; - return this; - } - - /** - * <b>Mandatory</b>. Provide a CacheManager to the {@link AppConfigProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) - */ - public AppConfigProvider.Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * Provide a transformationManager to the {@link AppConfigProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) - */ - public AppConfigProvider.Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } -} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseParamAspect.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseParamAspect.java new file mode 100644 index 000000000..f7ebdf97a --- /dev/null +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseParamAspect.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.parameters; + +import org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.transform.Transformer; + +/** + * Provides a common base for all parameter aspects. This lets us group functionality that + * we need to reimplement in each aspect. This class should be extended for each + * additional parameter aspect. + */ +public abstract class BaseParamAspect { + + /** + * Gets the parameter value from the provider and transforms it if necessary. This transformation + * is generic across all parameter providers. + * + * @param key The key of the parameter to get + * @param transformer The transformer to use to transform the parameter value + * @param provider A concrete instance of the parameter provider to retrieve the value from + * @param fieldSignature The signature of the field that the parameter is being injected into + * @return The value of the parameter, transformed if necessary + */ + protected Object getAndTransform(String key, Class<? extends Transformer> transformer, BaseProvider provider, + FieldSignature fieldSignature) { + if (transformer.isInterface()) { + // No transformation + return provider.get(key); + } else { + Class fieldType = fieldSignature.getFieldType(); + if (String.class.isAssignableFrom(fieldType)) { + // Basic transformation + return provider + .withTransformation(transformer) + .get(key); + } else { + // Complex transformation + return provider + .withTransformation(transformer) + .get(key, fieldType); + } + } + } +} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java index edb82f5ec..d83ff0298 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java @@ -19,7 +19,8 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; -import software.amazon.awssdk.annotations.NotThreadSafe; + +import software.amazon.awssdk.annotations.ThreadSafe; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.TransformationException; import software.amazon.lambda.powertools.parameters.transform.BasicTransformer; @@ -28,17 +29,30 @@ /** * Base class for all parameter providers. + * <p> + * This class is thread-safe when used as a singleton in multi-threaded environments. + * Configuration methods ({@link #withMaxAge(int, ChronoUnit)}, {@link #withTransformation(Class)}) + * use thread-local storage to support concurrent requests with different requirements. + * <p> + * The cache and transformation managers are thread-safe with zero synchronization overhead, + * using lock-free data structures (ThreadLocal, AtomicReference, ConcurrentHashMap) for optimal performance. + * The cache storage is shared across all threads, allowing cached values to be reused across requests. + * <p> + * <b>Implementation Requirements:</b> Subclasses must ensure that implementations of + * {@link #getValue(String)} and {@link #getMultipleValues(String)} are thread-safe to + * guarantee overall thread-safety of the provider. */ -@NotThreadSafe +@ThreadSafe public abstract class BaseProvider implements ParamProvider { public static final String PARAMETERS = "parameters"; protected final CacheManager cacheManager; - private TransformationManager transformationManager; + private final TransformationManager transformationManager; private Clock clock; - public BaseProvider(CacheManager cacheManager) { + public BaseProvider(CacheManager cacheManager, TransformationManager transformationManager) { this.cacheManager = cacheManager; + this.transformationManager = transformationManager; } /** @@ -59,25 +73,10 @@ public BaseProvider(CacheManager cacheManager) { */ protected abstract Map<String, String> getMultipleValues(String path); - /** - * (Optional) Set the default max age for the cache of all parameters. Override the default 5 seconds.<br/> - * If for some parameters, you need to set a different maxAge, use {@link #withMaxAge(int, ChronoUnit)}.<br /> - * Use {@link #withMaxAge(int, ChronoUnit)} after {#defaultMaxAge(int, ChronoUnit)} in the chain. - * - * @param maxAge Maximum time to cache the parameter, before calling the underlying parameter store. - * @param unit Unit of time - * @return the provider itself in order to chain calls (eg. <pre>provider.defaultMaxAge(10, SECONDS).get("key")</pre>). - */ - protected BaseProvider defaultMaxAge(int maxAge, ChronoUnit unit) { - Duration duration = Duration.of(maxAge, unit); - cacheManager.setDefaultExpirationTime(duration); - return this; - } - /** * (Optional) Builder method to call before {@link #get(String)} or {@link #get(String, Class)} * to set cache max age for the parameter to get.<br/><br/> - * The max age is reset to default (either 5 or a custom value set with {@link #defaultMaxAge}) after each get, + * The max age is reset to default (either 5 or a custom value that may be set on the CacheManager) after each get, * so you need to use this method for each parameter to cache with non-default max age.<br/><br/> * * <b>Not Thread Safe</b>: calling this method simultaneously by several threads @@ -105,6 +104,7 @@ public BaseProvider withMaxAge(int maxAge, ChronoUnit unit) { * @param transformerClass Class of the transformer to apply. For convenience, you can use {@link Transformer#json} or {@link Transformer#base64} shortcuts. * @return the provider itself in order to chain calls (eg. <pre>provider.withTransformation(json).get("key", MyObject.class)</pre>). */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public BaseProvider withTransformation(Class<? extends Transformer> transformerClass) { if (transformationManager == null) { throw new IllegalStateException( @@ -124,12 +124,12 @@ public BaseProvider withTransformation(Class<? extends Transformer> transformerC * eg. getMultiple("/foo/bar") will retrieve [key="baz", value="valuebaz"] for parameter "/foo/bar/baz" */ @Override + @SuppressWarnings("unchecked") // Cache stores Object, safe cast as we control what's stored public Map<String, String> getMultiple(String path) { // remove trailing whitespace String pathWithoutTrailingSlash = path.replaceAll("\\/+$", ""); try { - return (Map<String, String>) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> - { + return (Map<String, String>) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> { Map<String, String> params = getMultipleValues(pathWithoutTrailingSlash); cacheManager.putInCache(pathWithoutTrailingSlash, params); @@ -157,8 +157,7 @@ public Map<String, String> getMultiple(String path) { @Override public String get(final String key) { try { - return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { + return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { String value = getValue(key); String transformedValue = value; @@ -189,10 +188,10 @@ public String get(final String key) { * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. */ @Override + @SuppressWarnings("unchecked") // Cache stores Object, safe cast as we control what's stored public <T> T get(final String key, final Class<T> targetClass) { try { - return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { + return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { String value = getValue(key); if (transformationManager == null) { @@ -221,14 +220,10 @@ protected Instant now() { protected void resetToDefaults() { cacheManager.resetExpirationTime(); if (transformationManager != null) { - transformationManager.setTransformer(null); + transformationManager.unsetTransformer(); } } - protected void setTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - } - /** * For test purpose * diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java deleted file mode 100644 index 3a8732e18..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters; - -import java.util.Collections; -import java.util.Map; -import java.util.stream.Collectors; -import software.amazon.awssdk.core.SdkSystemSetting; -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; -import software.amazon.lambda.powertools.core.internal.UserAgentConfigurator; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; - -/** - * Implements a {@link ParamProvider} on top of DynamoDB. The schema of the table - * is described in the Powertools for AWS Lambda (Java) documentation. - * - * @see <a href="https://docs.powertools.aws.dev/lambda-java/utilities/parameters">Parameters provider documentation</a> - */ -public class DynamoDbProvider extends BaseProvider { - - private final DynamoDbClient client; - private final String tableName; - - DynamoDbProvider(CacheManager cacheManager, DynamoDbClient client, String tableName) { - super(cacheManager); - this.client = client; - this.tableName = tableName; - } - - DynamoDbProvider(CacheManager cacheManager, String tableName) { - this(cacheManager, Builder.createClient(), tableName); - } - - /** - * Create a builder that can be used to configure and create a {@link DynamoDbProvider}. - * - * @return a new instance of {@link DynamoDbProvider.Builder} - */ - public static DynamoDbProvider.Builder builder() { - return new DynamoDbProvider.Builder(); - } - - /** - * Return a single value from the DynamoDB parameter provider. - * - * @param key key of the parameter - * @return The value, if it exists, null if it doesn't. Throws if the row exists but doesn't match the schema. - */ - @Override - protected String getValue(String key) { - GetItemResponse resp = client.getItem(GetItemRequest.builder() - .tableName(tableName) - .key(Collections.singletonMap("id", AttributeValue.fromS(key))) - .attributesToGet("value") - .build()); - - // If we have an item at the key, we should be able to get a 'val' out of it. If not it's - // exceptional. - // If we don't have an item at the key, we should return null. - if (resp.hasItem() && !resp.item().values().isEmpty()) { - if (!resp.item().containsKey("value")) { - throw new DynamoDbProviderSchemaException("Missing 'value': " + resp.item().toString()); - } - return resp.item().get("value").s(); - } - - return null; - } - - /** - * Returns multiple values from the DynamoDB parameter provider. - * - * @param path Parameter store path - * @return All values matching the given path, and an empty map if none do. Throws if any records exist that don't match the schema. - */ - @Override - protected Map<String, String> getMultipleValues(String path) { - - QueryResponse resp = client.query(QueryRequest.builder() - .tableName(tableName) - .keyConditionExpression("id = :v_id") - .expressionAttributeValues(Collections.singletonMap(":v_id", AttributeValue.fromS(path))) - .build()); - - return resp - .items() - .stream() - .peek((i) -> - { - if (!i.containsKey("sk")) { - throw new DynamoDbProviderSchemaException("Missing 'sk': " + i.toString()); - } - if (!i.containsKey("value")) { - throw new DynamoDbProviderSchemaException("Missing 'value': " + i.toString()); - } - }) - .collect( - Collectors.toMap( - (i) -> i.get("sk").s(), - (i) -> i.get("value").s())); - - - } - - static class Builder { - private DynamoDbClient client; - private String table; - private CacheManager cacheManager; - private TransformationManager transformationManager; - - private static DynamoDbClient createClient() { - return DynamoDbClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) - .build(); - } - - /** - * Create a {@link DynamoDbProvider} instance. - * - * @return a {@link DynamoDbProvider} - */ - public DynamoDbProvider build() { - if (cacheManager == null) { - throw new IllegalStateException("No CacheManager provided; please provide one"); - } - if (table == null) { - throw new IllegalStateException("No DynamoDB table name provided; please provide one"); - } - DynamoDbProvider provider; - if (client == null) { - client = createClient(); - } - provider = new DynamoDbProvider(cacheManager, client, table); - - if (transformationManager != null) { - provider.setTransformationManager(transformationManager); - } - return provider; - } - - /** - * Set custom {@link DynamoDbClient} to pass to the {@link DynamoDbClient}. <br/> - * Use it if you want to customize the region or any other part of the client. - * - * @param client Custom client - * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) - */ - public DynamoDbProvider.Builder withClient(DynamoDbClient client) { - this.client = client; - return this; - } - - /** - * <b>Mandatory</b>. Provide a CacheManager to the {@link DynamoDbProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) - */ - public DynamoDbProvider.Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * <b>Mandatory</b>. Provide a DynamoDB table to the {@link DynamoDbProvider} - * - * @param table the table that parameters will be retrieved from. - * @return the builder to chain calls (eg. <pre>builder.withTable().build()</pre>) - */ - public DynamoDbProvider.Builder withTable(String table) { - this.table = table; - return this; - } - - /** - * Provide a transformationManager to the {@link DynamoDbProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) - */ - public DynamoDbProvider.Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } -} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java deleted file mode 100644 index 6fee0f114..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters; - -import java.lang.reflect.Constructor; -import java.util.concurrent.ConcurrentHashMap; -import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; -import software.amazon.awssdk.services.ssm.SsmClient; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; - -/** - * Utility class to retrieve instances of parameter providers. - * Each instance is unique (singleton). - */ -public final class ParamManager { - - private static final CacheManager cacheManager = new CacheManager(); - private static final TransformationManager transformationManager = new TransformationManager(); - - // NOTE: For testing purposes `providers` cannot be final - private static ConcurrentHashMap<Class<? extends BaseProvider>, BaseProvider> providers = new ConcurrentHashMap<>(); - - /** - * Get a concrete implementation of {@link BaseProvider}.<br/> - * You can specify {@link SecretsProvider}, {@link SSMProvider} or create your - * custom provider by extending {@link BaseProvider} if you need to integrate with a different parameter store. - * - * @return a {@link SecretsProvider} - * @deprecated You should not use this method directly but a typed one (getSecretsProvider, getSsmProvider, getDynamoDbProvider, getAppConfigProvider), will be removed in v2 - */ - // TODO in v2: remove public access to this and review how we get providers (it was not designed for DDB and AppConfig in mind initially) - public static <T extends BaseProvider> T getProvider(Class<T> providerClass) { - if (providerClass == null) { - throw new IllegalStateException("providerClass cannot be null."); - } - if (providerClass == DynamoDbProvider.class || providerClass == AppConfigProvider.class) { - throw new IllegalArgumentException( - providerClass + " cannot be instantiated like this, additional parameters are required"); - } - return (T) providers.computeIfAbsent(providerClass, ParamManager::createProvider); - } - - /** - * Get a {@link SecretsProvider} with default {@link SecretsManagerClient}.<br/> - * If you need to customize the region, or other part of the client, use {@link ParamManager#getSecretsProvider(SecretsManagerClient)} instead. - * - * @return a {@link SecretsProvider} - */ - public static SecretsProvider getSecretsProvider() { - return getProvider(SecretsProvider.class); - } - - /** - * Get a {@link SSMProvider} with default {@link SsmClient}.<br/> - * If you need to customize the region, or other part of the client, use {@link ParamManager#getSsmProvider(SsmClient)} instead. - * - * @return a {@link SSMProvider} - */ - public static SSMProvider getSsmProvider() { - return getProvider(SSMProvider.class); - } - - /** - * Get a {@link DynamoDbProvider} with default {@link DynamoDbClient} <br/> - * If you need to customize the region, or other part of the client, use {@link ParamManager#getDynamoDbProvider(DynamoDbClient, String)} - */ - public static DynamoDbProvider getDynamoDbProvider(String tableName) { - // Because we need a DDB table name to configure our client, we can't use - // ParamManager#getProvider. This means that we need to make sure we do the same stuff - - // set transformation manager and cache manager. - return DynamoDbProvider.builder() - .withCacheManager(cacheManager) - .withTable(tableName) - .withTransformationManager(transformationManager) - .build(); - } - - /** - * Get a {@link AppConfigProvider} with default {@link AppConfigDataClient}.<br/> - * If you need to customize the region, or other part of the client, use {@link ParamManager#getAppConfigProvider(AppConfigDataClient, String, String)} instead. - * - * @return a {@link AppConfigProvider} - */ - public static AppConfigProvider getAppConfigProvider(String environment, String application) { - // Because we need a DDB table name to configure our client, we can't use - // ParamManager#getProvider. This means that we need to make sure we do the same stuff - - // set transformation manager and cache manager. - return AppConfigProvider.builder() - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .withEnvironment(environment) - .withApplication(application) - .build(); - } - - - /** - * Get a {@link SecretsProvider} with your custom {@link SecretsManagerClient}.<br/> - * Use this to configure region or other part of the client. Use {@link ParamManager#getSsmProvider()} if you don't need this customization. - * - * @return a {@link SecretsProvider} - */ - public static SecretsProvider getSecretsProvider(SecretsManagerClient client) { - return (SecretsProvider) providers.computeIfAbsent(SecretsProvider.class, (k) -> SecretsProvider.builder() - .withClient(client) - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .build()); - } - - /** - * Get a {@link SSMProvider} with your custom {@link SsmClient}.<br/> - * Use this to configure region or other part of the client. Use {@link ParamManager#getSsmProvider()} if you don't need this customization. - * - * @return a {@link SSMProvider} - */ - public static SSMProvider getSsmProvider(SsmClient client) { - return (SSMProvider) providers.computeIfAbsent(SSMProvider.class, (k) -> SSMProvider.builder() - .withClient(client) - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .build()); - } - - /** - * Get a {@link DynamoDbProvider} with your custom {@link DynamoDbClient}.<br/> - * Use this to configure region or other part of the client. Use {@link ParamManager#getDynamoDbProvider(String)} )} if you don't need this customization. - * - * @return a {@link DynamoDbProvider} - */ - public static DynamoDbProvider getDynamoDbProvider(DynamoDbClient client, String table) { - return (DynamoDbProvider) providers.computeIfAbsent(DynamoDbProvider.class, (k) -> DynamoDbProvider.builder() - .withClient(client) - .withTable(table) - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .build()); - } - - /** - * Get a {@link AppConfigProvider} with your custom {@link AppConfigDataClient}.<br/> - * Use this to configure region or other part of the client. Use {@link ParamManager#getAppConfigProvider(String, String)} if you don't need this customization. - * - * @return a {@link AppConfigProvider} - */ - public static AppConfigProvider getAppConfigProvider(AppConfigDataClient client, String environment, - String application) { - return (AppConfigProvider) providers.computeIfAbsent(AppConfigProvider.class, (k) -> AppConfigProvider.builder() - .withClient(client) - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .withEnvironment(environment) - .withApplication(application) - .build()); - } - - - public static CacheManager getCacheManager() { - return cacheManager; - } - - public static TransformationManager getTransformationManager() { - return transformationManager; - } - - static <T extends BaseProvider> T createProvider(Class<T> providerClass) { - try { - Constructor<T> constructor = providerClass.getDeclaredConstructor(CacheManager.class); - T provider = - constructor.newInstance(cacheManager); // FIXME: avoid reflection here as we may have issues (#1280) - provider.setTransformationManager(transformationManager); - return provider; - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Unexpected error occurred. Please raise issue at " + - "https://github.com/aws-powertools/powertools-lambda-java/issues", e); - } - } - -} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java deleted file mode 100644 index 2612f6c7f..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import java.time.temporal.ChronoUnit; -import java.util.Base64; -import java.util.Map; -import software.amazon.awssdk.core.SdkSystemSetting; -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; -import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; -import software.amazon.lambda.powertools.core.internal.UserAgentConfigurator; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import software.amazon.lambda.powertools.parameters.transform.Transformer; - -/** - * AWS Secrets Manager Parameter Provider<br/><br/> - * - * <u>Samples:</u> - * <pre> - * SecretsProvider provider = ParamManager.getSecretsProvider(); - * - * String value = provider.get("key"); - * System.out.println(value); - * >>> "value" - * - * // Get a value and cache it for 30 seconds (all others values will now be cached for 30 seconds) - * String value = provider.defaultMaxAge(30, ChronoUnit.SECONDS).get("key"); - * - * // Get a value and cache it for 1 minute (all others values are cached for 5 seconds by default) - * String value = provider.withMaxAge(1, ChronoUnit.MINUTES).get("key"); - * - * // Get a base64 encoded value, decoded into a String, and store it in the cache - * String value = provider.withTransformation(Transformer.base64).get("key"); - * - * // Get a json value, transform it into an Object, and store it in the cache - * TargetObject = provider.withTransformation(Transformer.json).get("key", TargetObject.class); - * </pre> - */ -public class SecretsProvider extends BaseProvider { - - private final SecretsManagerClient client; - - /** - * Constructor with custom {@link SecretsManagerClient}. <br/> - * Use when you need to customize region or any other attribute of the client.<br/><br/> - * <p> - * Use the {@link Builder} to create an instance of it. - * - * @param client custom client you would like to use. - */ - SecretsProvider(CacheManager cacheManager, SecretsManagerClient client) { - super(cacheManager); - this.client = client; - } - - /** - * Constructor with only a CacheManager<br/> - * <p> - * Used in {@link ParamManager#createProvider(Class)} - * - * @param cacheManager handles the parameter caching - */ - SecretsProvider(CacheManager cacheManager) { - this(cacheManager, Builder.createClient()); - } - - /** - * Create a builder that can be used to configure and create a {@link SecretsProvider}. - * - * @return a new instance of {@link SecretsProvider.Builder} - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Retrieve the parameter value from the AWS Secrets Manager. - * - * @param key key of the parameter - * @return the value of the parameter identified by the key - */ - @Override - protected String getValue(String key) { - GetSecretValueRequest request = GetSecretValueRequest.builder().secretId(key).build(); - - String secretValue = client.getSecretValue(request).secretString(); - if (secretValue == null) { - secretValue = - new String(Base64.getDecoder().decode(client.getSecretValue(request).secretBinary().asByteArray()), - UTF_8); - } - return secretValue; - } - - /** - * @throws UnsupportedOperationException as it is not possible to get multiple values simultaneously from Secrets Manager - */ - @Override - protected Map<String, String> getMultipleValues(String path) { - throw new UnsupportedOperationException("Impossible to get multiple values from AWS Secrets Manager"); - } - - /** - * {@inheritDoc} - */ - @Override - public SecretsProvider defaultMaxAge(int maxAge, ChronoUnit unit) { - super.defaultMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SecretsProvider withMaxAge(int maxAge, ChronoUnit unit) { - super.withMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SecretsProvider withTransformation(Class<? extends Transformer> transformerClass) { - super.withTransformation(transformerClass); - return this; - } - - // For test purpose only - SecretsManagerClient getClient() { - return client; - } - - static class Builder { - - private SecretsManagerClient client; - private CacheManager cacheManager; - private TransformationManager transformationManager; - - private static SecretsManagerClient createClient() { - return SecretsManagerClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) - .build(); - } - - /** - * Create a {@link SecretsProvider} instance. - * - * @return a {@link SecretsProvider} - */ - public SecretsProvider build() { - if (cacheManager == null) { - throw new IllegalStateException("No CacheManager provided, please provide one"); - } - SecretsProvider provider; - if (client == null) { - client = createClient(); - } - - provider = new SecretsProvider(cacheManager, client); - - if (transformationManager != null) { - provider.setTransformationManager(transformationManager); - } - return provider; - } - - /** - * Set custom {@link SecretsManagerClient} to pass to the {@link SecretsProvider}. <br/> - * Use it if you want to customize the region or any other part of the client. - * - * @param client Custom client - * @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>) - */ - public Builder withClient(SecretsManagerClient client) { - this.client = client; - return this; - } - - /** - * <b>Mandatory</b>. Provide a CacheManager to the {@link SecretsProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>) - */ - public Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * Provide a transformationManager to the {@link SecretsProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>) - */ - public Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } -} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java index b868cb642..99c281b3d 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java @@ -20,18 +20,27 @@ import java.time.Duration; import java.time.Instant; import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +/** + * Manages caching of parameter values with configurable expiration times. + * <p> + * This class is thread-safe. The cache storage is shared across all threads, + * while expiration time configuration is thread-local to support concurrent + * requests with different cache TTL requirements. + */ public class CacheManager { static final Duration DEFAULT_MAX_AGE_SECS = Duration.of(5, SECONDS); private final DataStore store; - private Duration defaultMaxAge = DEFAULT_MAX_AGE_SECS; - private Duration maxAge = defaultMaxAge; + private final AtomicReference<Duration> defaultMaxAge = new AtomicReference<>(DEFAULT_MAX_AGE_SECS); + private final ThreadLocal<Duration> maxAge = ThreadLocal.withInitial(() -> null); public CacheManager() { store = new DataStore(); } + @SuppressWarnings("unchecked") // DataStore stores Object, safe cast as we control what's stored public <T> Optional<T> getIfNotExpired(String key, Instant now) { if (store.hasExpired(key, now)) { return Optional.empty(); @@ -40,19 +49,19 @@ public <T> Optional<T> getIfNotExpired(String key, Instant now) { } public void setExpirationTime(Duration duration) { - this.maxAge = duration; + this.maxAge.set(duration); } public void setDefaultExpirationTime(Duration duration) { - this.defaultMaxAge = duration; - this.maxAge = duration; + this.defaultMaxAge.set(duration); } public <T> void putInCache(String key, T value) { - store.put(key, value, Clock.systemDefaultZone().instant().plus(maxAge)); + Duration effectiveMaxAge = maxAge.get() != null ? maxAge.get() : defaultMaxAge.get(); + store.put(key, value, Clock.systemDefaultZone().instant().plus(effectiveMaxAge)); } public void resetExpirationTime() { - maxAge = defaultMaxAge; + maxAge.remove(); } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java index 737faa353..4b6350cb5 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java @@ -15,6 +15,7 @@ package software.amazon.lambda.powertools.parameters.cache; import java.time.Instant; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** @@ -22,7 +23,7 @@ */ public class DataStore { - private final ConcurrentHashMap<String, ValueNode> store; + private final Map<String, ValueNode> store; public DataStore() { this.store = new ConcurrentHashMap<>(); @@ -32,8 +33,8 @@ public void put(String key, Object value, Instant time) { store.put(key, new ValueNode(value, time)); } - public void remove(String Key) { - store.remove(Key); + public void remove(String key) { + store.remove(key); } public Object get(String key) { @@ -42,7 +43,11 @@ public Object get(String key) { } public boolean hasExpired(String key, Instant now) { - boolean hasExpired = !store.containsKey(key) || now.isAfter(store.get(key).time); + ValueNode node = store.get(key); + if (node == null) { + return true; + } + boolean hasExpired = now.isAfter(node.time); // Auto-clean if the parameter has expired if (hasExpired) { remove(key); diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java deleted file mode 100644 index 081af108d..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters.internal; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.FieldSignature; -import software.amazon.lambda.powertools.parameters.BaseProvider; -import software.amazon.lambda.powertools.parameters.Param; -import software.amazon.lambda.powertools.parameters.ParamManager; - -@Aspect -public class LambdaParametersAspect { - - @Pointcut("get(* *) && @annotation(paramAnnotation)") - public void getParam(Param paramAnnotation) { - } - - @Around("getParam(paramAnnotation)") - public Object injectParam(final ProceedingJoinPoint joinPoint, final Param paramAnnotation) { - BaseProvider provider = ParamManager.getProvider(paramAnnotation.provider()); - - if (paramAnnotation.transformer().isInterface()) { - // No transformation - return provider.get(paramAnnotation.key()); - } else { - FieldSignature s = (FieldSignature) joinPoint.getSignature(); - if (String.class.isAssignableFrom(s.getFieldType())) { - // Basic transformation - return provider - .withTransformation(paramAnnotation.transformer()) - .get(paramAnnotation.key()); - } else { - // Complex transformation - return provider - .withTransformation(paramAnnotation.transformer()) - .get(paramAnnotation.key(), s.getFieldType()); - } - } - } - -} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java index d3fbce14f..bddbf81d9 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java @@ -15,31 +15,44 @@ package software.amazon.lambda.powertools.parameters.transform; import java.lang.reflect.InvocationTargetException; + import software.amazon.lambda.powertools.parameters.exception.TransformationException; /** * Manager in charge of transforming parameter values in another format. <br/> * Leverages a {@link Transformer} in order to perform the transformation. <br/> * The transformer must be passed with {@link #setTransformer(Class)} before performing any transform operation. + * <p> + * This class is thread-safe. Transformer configuration is thread-local to support concurrent + * requests with different transformation requirements. */ public class TransformationManager { - private Class<? extends Transformer> transformer = null; + private final ThreadLocal<Class<? extends Transformer>> transformer = ThreadLocal.withInitial(() -> null); /** * Set the {@link Transformer} to use for transformation. Must be called before any transformation. * * @param transformerClass class of the {@link Transformer} */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public void setTransformer(Class<? extends Transformer> transformerClass) { - this.transformer = transformerClass; + this.transformer.set(transformerClass); + } + + /** + * Unset the {@link Transformer} and clean up thread-local storage. + * Should be called after transformation is complete to prevent memory leaks. + */ + public void unsetTransformer() { + this.transformer.remove(); } /** * @return true if a {@link Transformer} has been passed to the Manager */ public boolean shouldTransform() { - return transformer != null; + return transformer.get() != null; } /** @@ -48,20 +61,22 @@ public boolean shouldTransform() { * @param value the value to transform * @return the value transformed */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public String performBasicTransformation(String value) { - if (transformer == null) { + Class<? extends Transformer> transformerClass = transformer.get(); + if (transformerClass == null) { throw new IllegalStateException( "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } - if (!BasicTransformer.class.isAssignableFrom(transformer)) { + if (!BasicTransformer.class.isAssignableFrom(transformerClass)) { throw new IllegalStateException("Wrong Transformer for a String, choose a BasicTransformer."); } try { - BasicTransformer basicTransformer = - (BasicTransformer) transformer.getDeclaredConstructor().newInstance(null); + BasicTransformer basicTransformer = (BasicTransformer) transformerClass.getDeclaredConstructor() + .newInstance(null); return basicTransformer.applyTransformation(value); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | - InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException e) { throw new TransformationException(e); } } @@ -73,17 +88,19 @@ public String performBasicTransformation(String value) { * @param targetClass the type of the target object. * @return the value transformed in an object ot type T. */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public <T> T performComplexTransformation(String value, Class<T> targetClass) { - if (transformer == null) { + Class<? extends Transformer> transformerClass = transformer.get(); + if (transformerClass == null) { throw new IllegalStateException( "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } try { - Transformer<T> complexTransformer = transformer.getDeclaredConstructor().newInstance(null); + Transformer<T> complexTransformer = transformerClass.getDeclaredConstructor().newInstance(null); return complexTransformer.applyTransformation(value, targetClass); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | - InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException e) { throw new TransformationException(e); } } diff --git a/powertools-parameters/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters/reflect-config.json b/powertools-parameters/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters/reflect-config.json new file mode 100644 index 000000000..380489707 --- /dev/null +++ b/powertools-parameters/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters/reflect-config.json @@ -0,0 +1,197 @@ +[ +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String"] }, {"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedParameterizedType", + "methods":[{"name":"getAnnotatedActualTypeArguments","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.awssdk.awscore.AwsClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"software.amazon.awssdk.core.SdkClient", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"serviceName","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.services.dynamodb.DynamoDbClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"batchExecuteStatement","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchExecuteStatement","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchExecuteStatementRequest"] }, {"name":"batchGetItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest"] }, {"name":"batchGetItemPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetItemPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest"] }, {"name":"batchWriteItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchWriteItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest"] }, {"name":"createBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"createBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.CreateBackupRequest"] }, {"name":"createGlobalTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"createGlobalTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.CreateGlobalTableRequest"] }, {"name":"createTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"createTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.CreateTableRequest"] }, {"name":"deleteBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteBackupRequest"] }, {"name":"deleteItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest"] }, {"name":"deleteResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourcePolicy","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteResourcePolicyRequest"] }, {"name":"deleteTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest"] }, {"name":"describeBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeBackupRequest"] }, {"name":"describeContinuousBackups","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeContinuousBackups","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeContinuousBackupsRequest"] }, {"name":"describeContributorInsights","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeContributorInsights","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeContributorInsightsRequest"] }, {"name":"describeEndpoints","parameterTypes":[] }, {"name":"describeEndpoints","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEndpoints","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeEndpointsRequest"] }, {"name":"describeExport","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeExport","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeExportRequest"] }, {"name":"describeGlobalTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeGlobalTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeGlobalTableRequest"] }, {"name":"describeGlobalTableSettings","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeGlobalTableSettings","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeGlobalTableSettingsRequest"] }, {"name":"describeImport","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeImport","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeImportRequest"] }, {"name":"describeKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeKinesisStreamingDestinationRequest"] }, {"name":"describeLimits","parameterTypes":[] }, {"name":"describeLimits","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeLimits","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeLimitsRequest"] }, {"name":"describeTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest"] }, {"name":"describeTableReplicaAutoScaling","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeTableReplicaAutoScaling","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeTableReplicaAutoScalingRequest"] }, {"name":"describeTimeToLive","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeTimeToLive","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest"] }, {"name":"disableKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"disableKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.DisableKinesisStreamingDestinationRequest"] }, {"name":"enableKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"enableKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.EnableKinesisStreamingDestinationRequest"] }, {"name":"executeStatement","parameterTypes":["java.util.function.Consumer"] }, {"name":"executeStatement","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ExecuteStatementRequest"] }, {"name":"executeTransaction","parameterTypes":["java.util.function.Consumer"] }, {"name":"executeTransaction","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ExecuteTransactionRequest"] }, {"name":"exportTableToPointInTime","parameterTypes":["java.util.function.Consumer"] }, {"name":"exportTableToPointInTime","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ExportTableToPointInTimeRequest"] }, {"name":"getItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"getItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.GetItemRequest"] }, {"name":"getResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePolicy","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.GetResourcePolicyRequest"] }, {"name":"importTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"importTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ImportTableRequest"] }, {"name":"listBackups","parameterTypes":[] }, {"name":"listBackups","parameterTypes":["java.util.function.Consumer"] }, {"name":"listBackups","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListBackupsRequest"] }, {"name":"listContributorInsights","parameterTypes":["java.util.function.Consumer"] }, {"name":"listContributorInsights","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListContributorInsightsRequest"] }, {"name":"listContributorInsightsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listContributorInsightsPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListContributorInsightsRequest"] }, {"name":"listExports","parameterTypes":["java.util.function.Consumer"] }, {"name":"listExports","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListExportsRequest"] }, {"name":"listExportsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listExportsPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListExportsRequest"] }, {"name":"listGlobalTables","parameterTypes":[] }, {"name":"listGlobalTables","parameterTypes":["java.util.function.Consumer"] }, {"name":"listGlobalTables","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListGlobalTablesRequest"] }, {"name":"listImports","parameterTypes":["java.util.function.Consumer"] }, {"name":"listImports","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListImportsRequest"] }, {"name":"listImportsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listImportsPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListImportsRequest"] }, {"name":"listTables","parameterTypes":[] }, {"name":"listTables","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTables","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListTablesRequest"] }, {"name":"listTablesPaginator","parameterTypes":[] }, {"name":"listTablesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTablesPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListTablesRequest"] }, {"name":"listTagsOfResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTagsOfResource","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ListTagsOfResourceRequest"] }, {"name":"putItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"putItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.PutItemRequest"] }, {"name":"putResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"putResourcePolicy","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.PutResourcePolicyRequest"] }, {"name":"query","parameterTypes":["java.util.function.Consumer"] }, {"name":"query","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.QueryRequest"] }, {"name":"queryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"queryPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.QueryRequest"] }, {"name":"restoreTableFromBackup","parameterTypes":["java.util.function.Consumer"] }, {"name":"restoreTableFromBackup","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.RestoreTableFromBackupRequest"] }, {"name":"restoreTableToPointInTime","parameterTypes":["java.util.function.Consumer"] }, {"name":"restoreTableToPointInTime","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.RestoreTableToPointInTimeRequest"] }, {"name":"scan","parameterTypes":["java.util.function.Consumer"] }, {"name":"scan","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ScanRequest"] }, {"name":"scanPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"scanPaginator","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.ScanRequest"] }, {"name":"serviceClientConfiguration","parameterTypes":[] }, {"name":"tagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"tagResource","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.TagResourceRequest"] }, {"name":"transactGetItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"transactGetItems","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.TransactGetItemsRequest"] }, {"name":"transactWriteItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"transactWriteItems","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.TransactWriteItemsRequest"] }, {"name":"untagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"untagResource","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UntagResourceRequest"] }, {"name":"updateContinuousBackups","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateContinuousBackups","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateContinuousBackupsRequest"] }, {"name":"updateContributorInsights","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateContributorInsights","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateContributorInsightsRequest"] }, {"name":"updateGlobalTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateGlobalTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateGlobalTableRequest"] }, {"name":"updateGlobalTableSettings","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateGlobalTableSettings","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateGlobalTableSettingsRequest"] }, {"name":"updateItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateItem","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest"] }, {"name":"updateKinesisStreamingDestination","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateKinesisStreamingDestination","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateKinesisStreamingDestinationRequest"] }, {"name":"updateTable","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateTable","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateTableRequest"] }, {"name":"updateTableReplicaAutoScaling","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateTableReplicaAutoScaling","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateTableReplicaAutoScalingRequest"] }, {"name":"updateTimeToLive","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateTimeToLive","parameterTypes":["software.amazon.awssdk.services.dynamodb.model.UpdateTimeToLiveRequest"] }, {"name":"waiter","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.services.secretsmanager.SecretsManagerClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"batchGetSecretValue","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetSecretValue","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.BatchGetSecretValueRequest"] }, {"name":"batchGetSecretValuePaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"batchGetSecretValuePaginator","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.BatchGetSecretValueRequest"] }, {"name":"cancelRotateSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"cancelRotateSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.CancelRotateSecretRequest"] }, {"name":"createSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"createSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.CreateSecretRequest"] }, {"name":"deleteResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.DeleteResourcePolicyRequest"] }, {"name":"deleteSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.DeleteSecretRequest"] }, {"name":"describeSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.DescribeSecretRequest"] }, {"name":"getRandomPassword","parameterTypes":[] }, {"name":"getRandomPassword","parameterTypes":["java.util.function.Consumer"] }, {"name":"getRandomPassword","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.GetRandomPasswordRequest"] }, {"name":"getResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.GetResourcePolicyRequest"] }, {"name":"getSecretValue","parameterTypes":["java.util.function.Consumer"] }, {"name":"getSecretValue","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest"] }, {"name":"listSecretVersionIds","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecretVersionIds","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretVersionIdsRequest"] }, {"name":"listSecretVersionIdsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecretVersionIdsPaginator","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretVersionIdsRequest"] }, {"name":"listSecrets","parameterTypes":[] }, {"name":"listSecrets","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecrets","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretsRequest"] }, {"name":"listSecretsPaginator","parameterTypes":[] }, {"name":"listSecretsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listSecretsPaginator","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ListSecretsRequest"] }, {"name":"putResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"putResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.PutResourcePolicyRequest"] }, {"name":"putSecretValue","parameterTypes":["java.util.function.Consumer"] }, {"name":"putSecretValue","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.PutSecretValueRequest"] }, {"name":"removeRegionsFromReplication","parameterTypes":["java.util.function.Consumer"] }, {"name":"removeRegionsFromReplication","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.RemoveRegionsFromReplicationRequest"] }, {"name":"replicateSecretToRegions","parameterTypes":["java.util.function.Consumer"] }, {"name":"replicateSecretToRegions","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ReplicateSecretToRegionsRequest"] }, {"name":"restoreSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"restoreSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.RestoreSecretRequest"] }, {"name":"rotateSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"rotateSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.RotateSecretRequest"] }, {"name":"serviceClientConfiguration","parameterTypes":[] }, {"name":"stopReplicationToReplica","parameterTypes":["java.util.function.Consumer"] }, {"name":"stopReplicationToReplica","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.StopReplicationToReplicaRequest"] }, {"name":"tagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"tagResource","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.TagResourceRequest"] }, {"name":"untagResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"untagResource","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.UntagResourceRequest"] }, {"name":"updateSecret","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateSecret","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.UpdateSecretRequest"] }, {"name":"updateSecretVersionStage","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateSecretVersionStage","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.UpdateSecretVersionStageRequest"] }, {"name":"validateResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"validateResourcePolicy","parameterTypes":["software.amazon.awssdk.services.secretsmanager.model.ValidateResourcePolicyRequest"] }] +}, +{ + "name":"software.amazon.awssdk.services.ssm.SsmClient", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"addTagsToResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"addTagsToResource","parameterTypes":["software.amazon.awssdk.services.ssm.model.AddTagsToResourceRequest"] }, {"name":"associateOpsItemRelatedItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"associateOpsItemRelatedItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.AssociateOpsItemRelatedItemRequest"] }, {"name":"cancelCommand","parameterTypes":["java.util.function.Consumer"] }, {"name":"cancelCommand","parameterTypes":["software.amazon.awssdk.services.ssm.model.CancelCommandRequest"] }, {"name":"cancelMaintenanceWindowExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"cancelMaintenanceWindowExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.CancelMaintenanceWindowExecutionRequest"] }, {"name":"createActivation","parameterTypes":["java.util.function.Consumer"] }, {"name":"createActivation","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateActivationRequest"] }, {"name":"createAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"createAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateAssociationRequest"] }, {"name":"createAssociationBatch","parameterTypes":["java.util.function.Consumer"] }, {"name":"createAssociationBatch","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateAssociationBatchRequest"] }, {"name":"createDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"createDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateDocumentRequest"] }, {"name":"createMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"createMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateMaintenanceWindowRequest"] }, {"name":"createOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"createOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateOpsItemRequest"] }, {"name":"createOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"createOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateOpsMetadataRequest"] }, {"name":"createPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"createPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreatePatchBaselineRequest"] }, {"name":"createResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"createResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.CreateResourceDataSyncRequest"] }, {"name":"deleteActivation","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteActivation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteActivationRequest"] }, {"name":"deleteAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteAssociationRequest"] }, {"name":"deleteDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteDocumentRequest"] }, {"name":"deleteInventory","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteInventory","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteInventoryRequest"] }, {"name":"deleteMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteMaintenanceWindowRequest"] }, {"name":"deleteOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteOpsItemRequest"] }, {"name":"deleteOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteOpsMetadataRequest"] }, {"name":"deleteParameter","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteParameter","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteParameterRequest"] }, {"name":"deleteParameters","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteParameters","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteParametersRequest"] }, {"name":"deletePatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"deletePatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeletePatchBaselineRequest"] }, {"name":"deleteResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteResourceDataSyncRequest"] }, {"name":"deleteResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"deleteResourcePolicy","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeleteResourcePolicyRequest"] }, {"name":"deregisterManagedInstance","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterManagedInstance","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterManagedInstanceRequest"] }, {"name":"deregisterPatchBaselineForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterPatchBaselineForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterPatchBaselineForPatchGroupRequest"] }, {"name":"deregisterTargetFromMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterTargetFromMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterTargetFromMaintenanceWindowRequest"] }, {"name":"deregisterTaskFromMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"deregisterTaskFromMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.DeregisterTaskFromMaintenanceWindowRequest"] }, {"name":"describeActivations","parameterTypes":[] }, {"name":"describeActivations","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeActivations","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeActivationsRequest"] }, {"name":"describeActivationsPaginator","parameterTypes":[] }, {"name":"describeActivationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeActivationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeActivationsRequest"] }, {"name":"describeAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationRequest"] }, {"name":"describeAssociationExecutionTargets","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutionTargets","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionTargetsRequest"] }, {"name":"describeAssociationExecutionTargetsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutionTargetsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionTargetsRequest"] }, {"name":"describeAssociationExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionsRequest"] }, {"name":"describeAssociationExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAssociationExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAssociationExecutionsRequest"] }, {"name":"describeAutomationExecutions","parameterTypes":[] }, {"name":"describeAutomationExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationExecutionsRequest"] }, {"name":"describeAutomationExecutionsPaginator","parameterTypes":[] }, {"name":"describeAutomationExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationExecutionsRequest"] }, {"name":"describeAutomationStepExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationStepExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationStepExecutionsRequest"] }, {"name":"describeAutomationStepExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAutomationStepExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAutomationStepExecutionsRequest"] }, {"name":"describeAvailablePatches","parameterTypes":[] }, {"name":"describeAvailablePatches","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAvailablePatches","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAvailablePatchesRequest"] }, {"name":"describeAvailablePatchesPaginator","parameterTypes":[] }, {"name":"describeAvailablePatchesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeAvailablePatchesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeAvailablePatchesRequest"] }, {"name":"describeDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeDocumentRequest"] }, {"name":"describeDocumentPermission","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeDocumentPermission","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeDocumentPermissionRequest"] }, {"name":"describeEffectiveInstanceAssociations","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectiveInstanceAssociations","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectiveInstanceAssociationsRequest"] }, {"name":"describeEffectiveInstanceAssociationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectiveInstanceAssociationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectiveInstanceAssociationsRequest"] }, {"name":"describeEffectivePatchesForPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectivePatchesForPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectivePatchesForPatchBaselineRequest"] }, {"name":"describeEffectivePatchesForPatchBaselinePaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeEffectivePatchesForPatchBaselinePaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeEffectivePatchesForPatchBaselineRequest"] }, {"name":"describeInstanceAssociationsStatus","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceAssociationsStatus","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceAssociationsStatusRequest"] }, {"name":"describeInstanceAssociationsStatusPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceAssociationsStatusPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceAssociationsStatusRequest"] }, {"name":"describeInstanceInformation","parameterTypes":[] }, {"name":"describeInstanceInformation","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceInformation","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceInformationRequest"] }, {"name":"describeInstanceInformationPaginator","parameterTypes":[] }, {"name":"describeInstanceInformationPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceInformationPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstanceInformationRequest"] }, {"name":"describeInstancePatchStates","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStates","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesRequest"] }, {"name":"describeInstancePatchStatesForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStatesForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesForPatchGroupRequest"] }, {"name":"describeInstancePatchStatesForPatchGroupPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStatesForPatchGroupPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesForPatchGroupRequest"] }, {"name":"describeInstancePatchStatesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchStatesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchStatesRequest"] }, {"name":"describeInstancePatches","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatches","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchesRequest"] }, {"name":"describeInstancePatchesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePatchesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePatchesRequest"] }, {"name":"describeInstanceProperties","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstanceProperties","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePropertiesRequest"] }, {"name":"describeInstancePropertiesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInstancePropertiesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInstancePropertiesRequest"] }, {"name":"describeInventoryDeletions","parameterTypes":[] }, {"name":"describeInventoryDeletions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInventoryDeletions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInventoryDeletionsRequest"] }, {"name":"describeInventoryDeletionsPaginator","parameterTypes":[] }, {"name":"describeInventoryDeletionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeInventoryDeletionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeInventoryDeletionsRequest"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocations","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocations","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTaskInvocationsRequest"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTaskInvocationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTaskInvocationsRequest"] }, {"name":"describeMaintenanceWindowExecutionTasks","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTasks","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTasksRequest"] }, {"name":"describeMaintenanceWindowExecutionTasksPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionTasksPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionTasksRequest"] }, {"name":"describeMaintenanceWindowExecutions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionsRequest"] }, {"name":"describeMaintenanceWindowExecutionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowExecutionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowExecutionsRequest"] }, {"name":"describeMaintenanceWindowSchedule","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowSchedule","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowScheduleRequest"] }, {"name":"describeMaintenanceWindowSchedulePaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowSchedulePaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowScheduleRequest"] }, {"name":"describeMaintenanceWindowTargets","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTargets","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTargetsRequest"] }, {"name":"describeMaintenanceWindowTargetsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTargetsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTargetsRequest"] }, {"name":"describeMaintenanceWindowTasks","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTasks","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTasksRequest"] }, {"name":"describeMaintenanceWindowTasksPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowTasksPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowTasksRequest"] }, {"name":"describeMaintenanceWindows","parameterTypes":[] }, {"name":"describeMaintenanceWindows","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindows","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsRequest"] }, {"name":"describeMaintenanceWindowsForTarget","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowsForTarget","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsForTargetRequest"] }, {"name":"describeMaintenanceWindowsForTargetPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowsForTargetPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsForTargetRequest"] }, {"name":"describeMaintenanceWindowsPaginator","parameterTypes":[] }, {"name":"describeMaintenanceWindowsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeMaintenanceWindowsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeMaintenanceWindowsRequest"] }, {"name":"describeOpsItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeOpsItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeOpsItemsRequest"] }, {"name":"describeOpsItemsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeOpsItemsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeOpsItemsRequest"] }, {"name":"describeParameters","parameterTypes":[] }, {"name":"describeParameters","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeParameters","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeParametersRequest"] }, {"name":"describeParametersPaginator","parameterTypes":[] }, {"name":"describeParametersPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeParametersPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeParametersRequest"] }, {"name":"describePatchBaselines","parameterTypes":[] }, {"name":"describePatchBaselines","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchBaselines","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchBaselinesRequest"] }, {"name":"describePatchBaselinesPaginator","parameterTypes":[] }, {"name":"describePatchBaselinesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchBaselinesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchBaselinesRequest"] }, {"name":"describePatchGroupState","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchGroupState","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchGroupStateRequest"] }, {"name":"describePatchGroups","parameterTypes":[] }, {"name":"describePatchGroups","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchGroups","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchGroupsRequest"] }, {"name":"describePatchGroupsPaginator","parameterTypes":[] }, {"name":"describePatchGroupsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchGroupsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchGroupsRequest"] }, {"name":"describePatchProperties","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchProperties","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchPropertiesRequest"] }, {"name":"describePatchPropertiesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describePatchPropertiesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribePatchPropertiesRequest"] }, {"name":"describeSessions","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeSessions","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeSessionsRequest"] }, {"name":"describeSessionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"describeSessionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.DescribeSessionsRequest"] }, {"name":"disassociateOpsItemRelatedItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"disassociateOpsItemRelatedItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.DisassociateOpsItemRelatedItemRequest"] }, {"name":"getAutomationExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"getAutomationExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetAutomationExecutionRequest"] }, {"name":"getCalendarState","parameterTypes":["java.util.function.Consumer"] }, {"name":"getCalendarState","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetCalendarStateRequest"] }, {"name":"getCommandInvocation","parameterTypes":["java.util.function.Consumer"] }, {"name":"getCommandInvocation","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetCommandInvocationRequest"] }, {"name":"getConnectionStatus","parameterTypes":["java.util.function.Consumer"] }, {"name":"getConnectionStatus","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetConnectionStatusRequest"] }, {"name":"getDefaultPatchBaseline","parameterTypes":[] }, {"name":"getDefaultPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"getDefaultPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetDefaultPatchBaselineRequest"] }, {"name":"getDeployablePatchSnapshotForInstance","parameterTypes":["java.util.function.Consumer"] }, {"name":"getDeployablePatchSnapshotForInstance","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetDeployablePatchSnapshotForInstanceRequest"] }, {"name":"getDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"getDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetDocumentRequest"] }, {"name":"getInventory","parameterTypes":[] }, {"name":"getInventory","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventory","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventoryRequest"] }, {"name":"getInventoryPaginator","parameterTypes":[] }, {"name":"getInventoryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventoryPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventoryRequest"] }, {"name":"getInventorySchema","parameterTypes":[] }, {"name":"getInventorySchema","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventorySchema","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventorySchemaRequest"] }, {"name":"getInventorySchemaPaginator","parameterTypes":[] }, {"name":"getInventorySchemaPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getInventorySchemaPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetInventorySchemaRequest"] }, {"name":"getMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowRequest"] }, {"name":"getMaintenanceWindowExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowExecutionRequest"] }, {"name":"getMaintenanceWindowExecutionTask","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowExecutionTask","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowExecutionTaskRequest"] }, {"name":"getMaintenanceWindowExecutionTaskInvocation","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowExecutionTaskInvocation","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowExecutionTaskInvocationRequest"] }, {"name":"getMaintenanceWindowTask","parameterTypes":["java.util.function.Consumer"] }, {"name":"getMaintenanceWindowTask","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetMaintenanceWindowTaskRequest"] }, {"name":"getOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsItemRequest"] }, {"name":"getOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsMetadataRequest"] }, {"name":"getOpsSummary","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsSummary","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsSummaryRequest"] }, {"name":"getOpsSummaryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getOpsSummaryPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetOpsSummaryRequest"] }, {"name":"getParameter","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameter","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParameterRequest"] }, {"name":"getParameterHistory","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameterHistory","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParameterHistoryRequest"] }, {"name":"getParameterHistoryPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameterHistoryPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParameterHistoryRequest"] }, {"name":"getParameters","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParameters","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParametersRequest"] }, {"name":"getParametersByPath","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParametersByPath","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest"] }, {"name":"getParametersByPathPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getParametersByPathPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest"] }, {"name":"getPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"getPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetPatchBaselineRequest"] }, {"name":"getPatchBaselineForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"getPatchBaselineForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetPatchBaselineForPatchGroupRequest"] }, {"name":"getResourcePolicies","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePolicies","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetResourcePoliciesRequest"] }, {"name":"getResourcePoliciesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"getResourcePoliciesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetResourcePoliciesRequest"] }, {"name":"getServiceSetting","parameterTypes":["java.util.function.Consumer"] }, {"name":"getServiceSetting","parameterTypes":["software.amazon.awssdk.services.ssm.model.GetServiceSettingRequest"] }, {"name":"labelParameterVersion","parameterTypes":["java.util.function.Consumer"] }, {"name":"labelParameterVersion","parameterTypes":["software.amazon.awssdk.services.ssm.model.LabelParameterVersionRequest"] }, {"name":"listAssociationVersions","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociationVersions","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationVersionsRequest"] }, {"name":"listAssociationVersionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociationVersionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationVersionsRequest"] }, {"name":"listAssociations","parameterTypes":[] }, {"name":"listAssociations","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociations","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationsRequest"] }, {"name":"listAssociationsPaginator","parameterTypes":[] }, {"name":"listAssociationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listAssociationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListAssociationsRequest"] }, {"name":"listCommandInvocations","parameterTypes":[] }, {"name":"listCommandInvocations","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommandInvocations","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandInvocationsRequest"] }, {"name":"listCommandInvocationsPaginator","parameterTypes":[] }, {"name":"listCommandInvocationsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommandInvocationsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandInvocationsRequest"] }, {"name":"listCommands","parameterTypes":[] }, {"name":"listCommands","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommands","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandsRequest"] }, {"name":"listCommandsPaginator","parameterTypes":[] }, {"name":"listCommandsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listCommandsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListCommandsRequest"] }, {"name":"listComplianceItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceItemsRequest"] }, {"name":"listComplianceItemsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceItemsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceItemsRequest"] }, {"name":"listComplianceSummaries","parameterTypes":[] }, {"name":"listComplianceSummaries","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceSummaries","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceSummariesRequest"] }, {"name":"listComplianceSummariesPaginator","parameterTypes":[] }, {"name":"listComplianceSummariesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listComplianceSummariesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListComplianceSummariesRequest"] }, {"name":"listDocumentMetadataHistory","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentMetadataHistory","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentMetadataHistoryRequest"] }, {"name":"listDocumentVersions","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentVersions","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentVersionsRequest"] }, {"name":"listDocumentVersionsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentVersionsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentVersionsRequest"] }, {"name":"listDocuments","parameterTypes":[] }, {"name":"listDocuments","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocuments","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentsRequest"] }, {"name":"listDocumentsPaginator","parameterTypes":[] }, {"name":"listDocumentsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listDocumentsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListDocumentsRequest"] }, {"name":"listInventoryEntries","parameterTypes":["java.util.function.Consumer"] }, {"name":"listInventoryEntries","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListInventoryEntriesRequest"] }, {"name":"listOpsItemEvents","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemEvents","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemEventsRequest"] }, {"name":"listOpsItemEventsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemEventsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemEventsRequest"] }, {"name":"listOpsItemRelatedItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemRelatedItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemRelatedItemsRequest"] }, {"name":"listOpsItemRelatedItemsPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsItemRelatedItemsPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsItemRelatedItemsRequest"] }, {"name":"listOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsMetadataRequest"] }, {"name":"listOpsMetadataPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listOpsMetadataPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListOpsMetadataRequest"] }, {"name":"listResourceComplianceSummaries","parameterTypes":[] }, {"name":"listResourceComplianceSummaries","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceComplianceSummaries","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceComplianceSummariesRequest"] }, {"name":"listResourceComplianceSummariesPaginator","parameterTypes":[] }, {"name":"listResourceComplianceSummariesPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceComplianceSummariesPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceComplianceSummariesRequest"] }, {"name":"listResourceDataSync","parameterTypes":[] }, {"name":"listResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceDataSyncRequest"] }, {"name":"listResourceDataSyncPaginator","parameterTypes":[] }, {"name":"listResourceDataSyncPaginator","parameterTypes":["java.util.function.Consumer"] }, {"name":"listResourceDataSyncPaginator","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListResourceDataSyncRequest"] }, {"name":"listTagsForResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"listTagsForResource","parameterTypes":["software.amazon.awssdk.services.ssm.model.ListTagsForResourceRequest"] }, {"name":"modifyDocumentPermission","parameterTypes":["java.util.function.Consumer"] }, {"name":"modifyDocumentPermission","parameterTypes":["software.amazon.awssdk.services.ssm.model.ModifyDocumentPermissionRequest"] }, {"name":"putComplianceItems","parameterTypes":["java.util.function.Consumer"] }, {"name":"putComplianceItems","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutComplianceItemsRequest"] }, {"name":"putInventory","parameterTypes":["java.util.function.Consumer"] }, {"name":"putInventory","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutInventoryRequest"] }, {"name":"putParameter","parameterTypes":["java.util.function.Consumer"] }, {"name":"putParameter","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutParameterRequest"] }, {"name":"putResourcePolicy","parameterTypes":["java.util.function.Consumer"] }, {"name":"putResourcePolicy","parameterTypes":["software.amazon.awssdk.services.ssm.model.PutResourcePolicyRequest"] }, {"name":"registerDefaultPatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerDefaultPatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterDefaultPatchBaselineRequest"] }, {"name":"registerPatchBaselineForPatchGroup","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerPatchBaselineForPatchGroup","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterPatchBaselineForPatchGroupRequest"] }, {"name":"registerTargetWithMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerTargetWithMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterTargetWithMaintenanceWindowRequest"] }, {"name":"registerTaskWithMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"registerTaskWithMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.RegisterTaskWithMaintenanceWindowRequest"] }, {"name":"removeTagsFromResource","parameterTypes":["java.util.function.Consumer"] }, {"name":"removeTagsFromResource","parameterTypes":["software.amazon.awssdk.services.ssm.model.RemoveTagsFromResourceRequest"] }, {"name":"resetServiceSetting","parameterTypes":["java.util.function.Consumer"] }, {"name":"resetServiceSetting","parameterTypes":["software.amazon.awssdk.services.ssm.model.ResetServiceSettingRequest"] }, {"name":"resumeSession","parameterTypes":["java.util.function.Consumer"] }, {"name":"resumeSession","parameterTypes":["software.amazon.awssdk.services.ssm.model.ResumeSessionRequest"] }, {"name":"sendAutomationSignal","parameterTypes":["java.util.function.Consumer"] }, {"name":"sendAutomationSignal","parameterTypes":["software.amazon.awssdk.services.ssm.model.SendAutomationSignalRequest"] }, {"name":"sendCommand","parameterTypes":["java.util.function.Consumer"] }, {"name":"sendCommand","parameterTypes":["software.amazon.awssdk.services.ssm.model.SendCommandRequest"] }, {"name":"serviceClientConfiguration","parameterTypes":[] }, {"name":"startAssociationsOnce","parameterTypes":["java.util.function.Consumer"] }, {"name":"startAssociationsOnce","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartAssociationsOnceRequest"] }, {"name":"startAutomationExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"startAutomationExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartAutomationExecutionRequest"] }, {"name":"startChangeRequestExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"startChangeRequestExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartChangeRequestExecutionRequest"] }, {"name":"startSession","parameterTypes":["java.util.function.Consumer"] }, {"name":"startSession","parameterTypes":["software.amazon.awssdk.services.ssm.model.StartSessionRequest"] }, {"name":"stopAutomationExecution","parameterTypes":["java.util.function.Consumer"] }, {"name":"stopAutomationExecution","parameterTypes":["software.amazon.awssdk.services.ssm.model.StopAutomationExecutionRequest"] }, {"name":"terminateSession","parameterTypes":["java.util.function.Consumer"] }, {"name":"terminateSession","parameterTypes":["software.amazon.awssdk.services.ssm.model.TerminateSessionRequest"] }, {"name":"unlabelParameterVersion","parameterTypes":["java.util.function.Consumer"] }, {"name":"unlabelParameterVersion","parameterTypes":["software.amazon.awssdk.services.ssm.model.UnlabelParameterVersionRequest"] }, {"name":"updateAssociation","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateAssociation","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateAssociationRequest"] }, {"name":"updateAssociationStatus","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateAssociationStatus","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateAssociationStatusRequest"] }, {"name":"updateDocument","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateDocument","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateDocumentRequest"] }, {"name":"updateDocumentDefaultVersion","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateDocumentDefaultVersion","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateDocumentDefaultVersionRequest"] }, {"name":"updateDocumentMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateDocumentMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateDocumentMetadataRequest"] }, {"name":"updateMaintenanceWindow","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateMaintenanceWindow","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateMaintenanceWindowRequest"] }, {"name":"updateMaintenanceWindowTarget","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateMaintenanceWindowTarget","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateMaintenanceWindowTargetRequest"] }, {"name":"updateMaintenanceWindowTask","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateMaintenanceWindowTask","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateMaintenanceWindowTaskRequest"] }, {"name":"updateManagedInstanceRole","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateManagedInstanceRole","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateManagedInstanceRoleRequest"] }, {"name":"updateOpsItem","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateOpsItem","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateOpsItemRequest"] }, {"name":"updateOpsMetadata","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateOpsMetadata","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateOpsMetadataRequest"] }, {"name":"updatePatchBaseline","parameterTypes":["java.util.function.Consumer"] }, {"name":"updatePatchBaseline","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdatePatchBaselineRequest"] }, {"name":"updateResourceDataSync","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateResourceDataSync","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateResourceDataSyncRequest"] }, {"name":"updateServiceSetting","parameterTypes":["java.util.function.Consumer"] }, {"name":"updateServiceSetting","parameterTypes":["software.amazon.awssdk.services.ssm.model.UpdateServiceSettingRequest"] }, {"name":"waiter","parameterTypes":[] }] +}, +{ + "name":"software.amazon.awssdk.utils.SdkAutoCloseable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"close","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.transform.Base64Transformer", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.transform.BasicTransformer", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.transform.JsonTransformer", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBar","parameterTypes":["int"] }, {"name":"setBaz","parameterTypes":["long"] }, {"name":"setFoo","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +} +] diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java deleted file mode 100644 index b84fcf743..000000000 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters; - -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.assertThatRuntimeException; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.internal.CustomProvider; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; - -public class ParamManagerTest { - - @Test - public void testGetCacheManager() { - - // Act - CacheManager cacheManager = ParamManager.getCacheManager(); - - // Assert - assertNotNull(cacheManager); - } - - @Test - public void testGetTransformationManager() { - - // Act - TransformationManager transformationManager = ParamManager.getTransformationManager(); - - // Assert - assertNotNull(transformationManager); - } - - @Test - public void testCreateProvider() { - - // Act - CustomProvider customProvider = ParamManager.createProvider(CustomProvider.class); - - // Assert - assertNotNull(customProvider); - } - - @Test - public void testCreateUninstanciableProvider_throwsException() { - - // Act & Assert - assertThatRuntimeException().isThrownBy(() -> ParamManager.createProvider(BaseProvider.class)); - } - - @Test - public void testGetProviderWithProviderClass() { - - // Act - SecretsProvider secretsProvider = ParamManager.getProvider(SecretsProvider.class); - - // Assert - assertNotNull(secretsProvider); - } - - @Test - public void testGetProviderWithProviderClass_throwsException() { - - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> ParamManager.getProvider(null)); - } - - @Test - public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { - - // Act - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); - - // Assert - assertNotNull(secretsProvider); - assertNotNull(secretsProvider.getClient()); - } - - @Test - public void testGetSSMProvider_withoutParameter_shouldCreateDefaultClient() { - - // Act - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - - // Assert - assertNotNull(ssmProvider); - assertNotNull(ssmProvider.getClient()); - } - - @Test - public void testGetDynamoDBProvider_requireOtherParameters_throwException() { - - // Act & Assert - assertThatIllegalArgumentException().isThrownBy(() -> ParamManager.getProvider(DynamoDbProvider.class)); - } - - @Test - public void testGetAppConfigProvider_requireOtherParameters_throwException() { - - // Act & Assert - assertThatIllegalArgumentException().isThrownBy(() -> ParamManager.getProvider(AppConfigProvider.class)); - } -} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java deleted file mode 100644 index 2bcfcc566..000000000 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters.cache; - -import static java.time.Clock.offset; -import static java.time.Duration.of; -import static java.time.temporal.ChronoUnit.SECONDS; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Clock; -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class CacheManagerTest { - - CacheManager manager; - - Clock clock; - - @BeforeEach - public void setup() { - clock = Clock.systemDefaultZone(); - manager = new CacheManager(); - } - - @Test - public void getIfNotExpired_notExpired_shouldReturnValue() { - manager.putInCache("key", "value"); - - Optional<String> value = manager.getIfNotExpired("key", clock.instant()); - - assertThat(value).isPresent().contains("value"); - } - - @Test - public void getIfNotExpired_expired_shouldReturnNothing() { - manager.putInCache("key", "value"); - - Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(6, SECONDS)).instant()); - - assertThat(value).isNotPresent(); - } - - @Test - public void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { - manager.setExpirationTime(of(42, SECONDS)); - manager.putInCache("key", "value"); - - Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); - - assertThat(value).isPresent().contains("value"); - } - - @Test - public void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldReturnValue() { - manager.setDefaultExpirationTime(of(42, SECONDS)); - manager.putInCache("key", "value"); - - - Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); - - assertThat(value).isPresent().contains("value"); - } - - @Test - public void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_shouldUseExpirationTime() { - manager.setDefaultExpirationTime(of(42, SECONDS)); - manager.setExpirationTime(of(2, SECONDS)); - manager.putInCache("key", "value"); - - Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); - - assertThat(value).isNotPresent(); - } - - @Test - public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { - manager.setDefaultExpirationTime(of(42, SECONDS)); - manager.setExpirationTime(of(2, SECONDS)); - manager.putInCache("key", "value"); - manager.resetExpirationTime(); - manager.putInCache("key2", "value2"); - - Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); - Optional<String> value2 = manager.getIfNotExpired("key2", offset(clock, of(40, SECONDS)).instant()); - - assertThat(value).isNotPresent(); - assertThat(value2).isPresent().contains("value2"); - } - -} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java deleted file mode 100644 index 2c246336b..000000000 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import software.amazon.lambda.powertools.parameters.Param; -import software.amazon.lambda.powertools.parameters.ParamManager; -import software.amazon.lambda.powertools.parameters.SSMProvider; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; -import software.amazon.lambda.powertools.parameters.transform.Base64Transformer; -import software.amazon.lambda.powertools.parameters.transform.JsonTransformer; -import software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize; - -public class LambdaParametersAspectTest { - - @Mock - private SSMProvider defaultProvider; - - @Param(key = "/default") - private String defaultValue; - - @Param(key = "/simple", provider = CustomProvider.class) - private String param; - - @Param(key = "/base64", provider = CustomProvider.class, transformer = Base64Transformer.class) - private String basicTransform; - - @Param(key = "/json", provider = CustomProvider.class, transformer = JsonTransformer.class) - private ObjectToDeserialize complexTransform; - - @Param(key = "/json", provider = CustomProvider.class, transformer = JsonTransformer.class) - private AnotherObject wrongTransform; - - @BeforeEach - public void init() { - openMocks(this); - } - - @Test - public void testDefault_ShouldUseSSMProvider() { - try (MockedStatic<ParamManager> mocked = mockStatic(ParamManager.class)) { - mocked.when(() -> ParamManager.getProvider(SSMProvider.class)).thenReturn(defaultProvider); - when(defaultProvider.get("/default")).thenReturn("value"); - - assertThat(defaultValue).isEqualTo("value"); - mocked.verify(() -> ParamManager.getProvider(SSMProvider.class), times(1)); - verify(defaultProvider, times(1)).get("/default"); - - mocked.reset(); - } - } - - @Test - public void testSimple() { - assertThat(param).isEqualTo("value"); - } - - @Test - public void testWithBasicTransform() { - assertThat(basicTransform).isEqualTo("value"); - } - - @Test - public void testWithComplexTransform() { - assertThat(complexTransform) - .isInstanceOf(ObjectToDeserialize.class) - .matches( - o -> o.getFoo().equals("Foo") && - o.getBar() == 42 && - o.getBaz() == 123456789); - } - - @Test - public void testWithComplexTransformWrongTargetClass_ShouldThrowException() { - assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> - { - AnotherObject obj = wrongTransform; - }); - } - -} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java deleted file mode 100644 index 39e69f9e0..000000000 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.parameters.transform; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; -import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; - -import java.util.Base64; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; - -public class TransformationManagerTest { - - TransformationManager manager; - - Class<BasicTransformer> basicTransformer = BasicTransformer.class; - - @BeforeEach - public void setup() { - manager = new TransformationManager(); - } - - @Test - public void setTransformer_shouldTransform() { - manager.setTransformer(json); - - assertThat(manager.shouldTransform()).isTrue(); - } - - @Test - public void notSetTransformer_shouldNotTransform() { - assertThat(manager.shouldTransform()).isFalse(); - } - - @Test - public void performBasicTransformation_noTransformer_shouldThrowException() { - assertThatIllegalStateException() - .isThrownBy(() -> manager.performBasicTransformation("value")); - } - - @Test - public void performBasicTransformation_notBasicTransformer_shouldThrowException() { - manager.setTransformer(json); - - assertThatIllegalStateException() - .isThrownBy(() -> manager.performBasicTransformation("value")); - } - - @Test - public void performBasicTransformation_abstractTransformer_throwsTransformationException() { - manager.setTransformer(basicTransformer); - - assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> manager.performBasicTransformation("value")); - } - - @Test - public void performBasicTransformation_shouldPerformTransformation() { - manager.setTransformer(base64); - - String expectedValue = "bar"; - String value = manager.performBasicTransformation(Base64.getEncoder().encodeToString(expectedValue.getBytes())); - - assertThat(value).isEqualTo(expectedValue); - } - - @Test - public void performComplexTransformation_noTransformer_shouldThrowException() { - assertThatIllegalStateException() - .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); - } - - @Test - public void performComplexTransformation_shouldPerformTransformation() { - manager.setTransformer(json); - - ObjectToDeserialize object = - manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", - ObjectToDeserialize.class); - - assertThat(object).isNotNull(); - } - - @Test - public void performComplexTransformation_throwsTransformationException() { - manager.setTransformer(basicTransformer); - - assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); - } -} diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 2a57f21e3..81603cd4f 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -14,45 +14,21 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> <artifactId>powertools-serialization</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Serialization Utilities</name> - <description> - - </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> + <name>Powertools for AWS Lambda (Java) - Serialization Utilities</name> + <description>Utilities for JSON serialization used across the project.</description> <dependencies> <dependency> @@ -64,8 +40,16 @@ <artifactId>aws-lambda-java-events</artifactId> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-joda</artifactId> </dependency> <!-- Test dependencies --> @@ -74,6 +58,16 @@ <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> @@ -87,6 +81,12 @@ </dependencies> <build> + <resources> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> <plugins> <plugin> <groupId>dev.aspectj</groupId> @@ -96,11 +96,60 @@ <skip>true</skip> </configuration> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> </plugins> </build> -</project> \ No newline at end of file + <profiles> + <profile> + <id>generate-graalvm-files</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization,experimental-class-define-support</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-serialization</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + +</project> diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java index baa6a0367..ac10412fa 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java @@ -14,8 +14,19 @@ package software.amazon.lambda.powertools.utilities; +import java.lang.reflect.Type; +import java.util.function.Supplier; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.joda.JodaModule; + import io.burt.jmespath.JmesPath; import io.burt.jmespath.RuntimeConfiguration; import io.burt.jmespath.function.BaseFunction; @@ -25,18 +36,35 @@ import software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction; import software.amazon.lambda.powertools.utilities.jmespath.JsonFunction; -public class JsonConfig { - private static final ThreadLocal<ObjectMapper> om = ThreadLocal.withInitial(ObjectMapper::new); +public final class JsonConfig { + + private static final Supplier<ObjectMapper> objectMapperSupplier = () -> JsonMapper.builder() + // Don't throw an exception when json has extra fields you are not serializing on. + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + // Ignore null values when writing json. + .defaultPropertyInclusion( + JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.USE_DEFAULTS)) + // Write times as a String instead of a Long so its human-readable. + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + // Sort fields in alphabetical order + .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) + .addModule(new JodaModule()) + .build(); + + private static final ThreadLocal<ObjectMapper> om = ThreadLocal.withInitial(objectMapperSupplier); + private final FunctionRegistry defaultFunctions = FunctionRegistry.defaultRegistry(); + private final FunctionRegistry customFunctions = defaultFunctions.extend( new Base64Function(), new Base64GZipFunction(), - new JsonFunction() - ); + new JsonFunction()); + private final RuntimeConfiguration configuration = new RuntimeConfiguration.Builder() .withSilentTypeErrors(true) .withFunctionRegistry(customFunctions) .build(); + private JmesPath<JsonNode> jmesPath = new JacksonRuntime(configuration, getObjectMapper()); private JsonConfig() { @@ -55,6 +83,23 @@ public ObjectMapper getObjectMapper() { return om.get(); } + /** + * Creates a TypeReference from a Class for use with Jackson deserialization. + * This is useful when you need to convert a Class to a TypeReference for generic type handling. + * + * @param clazz the class to convert to TypeReference + * @param <T> the type parameter + * @return a TypeReference wrapping the provided class + */ + public static <T> TypeReference<T> toTypeReference(Class<T> clazz) { + return new TypeReference<T>() { + @Override + public Type getType() { + return clazz; + } + }; + } + /** * Return the JmesPath used to select sub node of Json * @@ -81,7 +126,7 @@ public <T extends BaseFunction> void addFunction(T function) { jmesPath = new JacksonRuntime(updatedConfig, getObjectMapper()); } - private static class ConfigHolder { - private final static JsonConfig instance = new JsonConfig(); + private static final class ConfigHolder { + private static final JsonConfig instance = new JsonConfig(); } } diff --git a/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json new file mode 100644 index 000000000..e20bd748f --- /dev/null +++ b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json @@ -0,0 +1,427 @@ +[ +{ + "name":"[B" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.KeyDeserializers;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiators;" +}, +{ + "name":"[Lcom.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers;" +}, +{ + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setIsBase64Encoded","parameterTypes":["java.lang.Boolean"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setPathParameters","parameterTypes":["java.util.Map"] }, {"name":"setQueryStringParameters","parameterTypes":["java.util.Map"] }, {"name":"setRequestContext","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext"] }, {"name":"setResource","parameterTypes":["java.lang.String"] }, {"name":"setStageVariables","parameterTypes":["java.util.Map"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAccountId","parameterTypes":["java.lang.String"] }, {"name":"setApiId","parameterTypes":["java.lang.String"] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setIdentity","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setProtocol","parameterTypes":["java.lang.String"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setRequestTime","parameterTypes":["java.lang.String"] }, {"name":"setRequestTimeEpoch","parameterTypes":["java.lang.Long"] }, {"name":"setResourceId","parameterTypes":["java.lang.String"] }, {"name":"setResourcePath","parameterTypes":["java.lang.String"] }, {"name":"setStage","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAccessKey","parameterTypes":["java.lang.String"] }, {"name":"setAccountId","parameterTypes":["java.lang.String"] }, {"name":"setCaller","parameterTypes":["java.lang.String"] }, {"name":"setCognitoAuthenticationProvider","parameterTypes":["java.lang.String"] }, {"name":"setCognitoAuthenticationType","parameterTypes":["java.lang.String"] }, {"name":"setCognitoIdentityId","parameterTypes":["java.lang.String"] }, {"name":"setCognitoIdentityPoolId","parameterTypes":["java.lang.String"] }, {"name":"setSourceIp","parameterTypes":["java.lang.String"] }, {"name":"setUser","parameterTypes":["java.lang.String"] }, {"name":"setUserAgent","parameterTypes":["java.lang.String"] }, {"name":"setUserArn","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setCookies","parameterTypes":["java.util.List"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setIsBase64Encoded","parameterTypes":["boolean"] }, {"name":"setPathParameters","parameterTypes":["java.util.Map"] }, {"name":"setQueryStringParameters","parameterTypes":["java.util.Map"] }, {"name":"setRawPath","parameterTypes":["java.lang.String"] }, {"name":"setRawQueryString","parameterTypes":["java.lang.String"] }, {"name":"setRequestContext","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext"] }, {"name":"setRouteKey","parameterTypes":["java.lang.String"] }, {"name":"setStageVariables","parameterTypes":["java.util.Map"] }, {"name":"setVersion","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAccountId","parameterTypes":["java.lang.String"] }, {"name":"setApiId","parameterTypes":["java.lang.String"] }, {"name":"setDomainName","parameterTypes":["java.lang.String"] }, {"name":"setDomainPrefix","parameterTypes":["java.lang.String"] }, {"name":"setHttp","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Http"] }, {"name":"setRouteKey","parameterTypes":["java.lang.String"] }, {"name":"setStage","parameterTypes":["java.lang.String"] }, {"name":"setTime","parameterTypes":["java.lang.String"] }, {"name":"setTimeEpoch","parameterTypes":["long"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Authorizer", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Authorizer$JWT", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$CognitoIdentity", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$Http", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setMethod","parameterTypes":["java.lang.String"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setProtocol","parameterTypes":["java.lang.String"] }, {"name":"setSourceIp","parameterTypes":["java.lang.String"] }, {"name":"setUserAgent","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent$RequestContext$IAM", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ActiveMQEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setEventSource","parameterTypes":["java.lang.String"] }, {"name":"setEventSourceArn","parameterTypes":["java.lang.String"] }, {"name":"setMessages","parameterTypes":["java.util.List"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$ActiveMQMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBrokerInTime","parameterTypes":["long"] }, {"name":"setBrokerOutTime","parameterTypes":["long"] }, {"name":"setData","parameterTypes":["java.lang.String"] }, {"name":"setDeliveryMode","parameterTypes":["int"] }, {"name":"setDestination","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$Destination"] }, {"name":"setExpiration","parameterTypes":["long"] }, {"name":"setMessageID","parameterTypes":["java.lang.String"] }, {"name":"setMessageType","parameterTypes":["java.lang.String"] }, {"name":"setPriority","parameterTypes":["int"] }, {"name":"setProperties","parameterTypes":["java.util.Map"] }, {"name":"setRedelivered","parameterTypes":["boolean"] }, {"name":"setReplyTo","parameterTypes":["java.lang.String"] }, {"name":"setTimestamp","parameterTypes":["long"] }, {"name":"setType","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$Destination", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setPhysicalName","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setHttpMethod","parameterTypes":["java.lang.String"] }, {"name":"setIsBase64Encoded","parameterTypes":["boolean"] }, {"name":"setPath","parameterTypes":["java.lang.String"] }, {"name":"setQueryStringParameters","parameterTypes":["java.util.Map"] }, {"name":"setRequestContext","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$RequestContext"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$Elb", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setTargetGroupArn","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$RequestContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setElb","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$Elb"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setLogicalResourceId","parameterTypes":["java.lang.String"] }, {"name":"setOldResourceProperties","parameterTypes":["java.util.Map"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setRequestType","parameterTypes":["java.lang.String"] }, {"name":"setResourceProperties","parameterTypes":["java.util.Map"] }, {"name":"setResourceType","parameterTypes":["java.lang.String"] }, {"name":"setServiceToken","parameterTypes":["java.lang.String"] }, {"name":"setStackId","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAwsLogs","parameterTypes":["com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent$AWSLogs"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent$AWSLogs", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setData","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KafkaEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBootstrapServers","parameterTypes":["java.lang.String"] }, {"name":"setEventSource","parameterTypes":["java.lang.String"] }, {"name":"setEventSourceArn","parameterTypes":["java.lang.String"] }, {"name":"setRecords","parameterTypes":["java.util.Map"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KafkaEvent$KafkaEventRecord", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setOffset","parameterTypes":["long"] }, {"name":"setPartition","parameterTypes":["int"] }, {"name":"setTimestamp","parameterTypes":["long"] }, {"name":"setTimestampType","parameterTypes":["java.lang.String"] }, {"name":"setTopic","parameterTypes":["java.lang.String"] }, {"name":"setValue","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setApplicationArn","parameterTypes":["java.lang.String"] }, {"name":"setInvocationId","parameterTypes":["java.lang.String"] }, {"name":"setRecords","parameterTypes":["java.util.List"] }, {"name":"setStreamArn","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent$Record", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setData","parameterTypes":["java.nio.ByteBuffer"] }, {"name":"setKinesisFirehoseRecordMetadata","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent$Record$KinesisFirehoseRecordMetadata"] }, {"name":"setRecordId","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent$Record$KinesisFirehoseRecordMetadata", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setApproximateArrivalTimestamp","parameterTypes":["java.lang.Long"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setApplicationArn","parameterTypes":["java.lang.String"] }, {"name":"setInvocationId","parameterTypes":["java.lang.String"] }, {"name":"setRecords","parameterTypes":["java.util.List"] }, {"name":"setStreamArn","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent$Record", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setData","parameterTypes":["java.nio.ByteBuffer"] }, {"name":"setKinesisStreamRecordMetadata","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent$Record$KinesisStreamRecordMetadata"] }, {"name":"setRecordId","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent$Record$KinesisStreamRecordMetadata", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setApproximateArrivalTimestamp","parameterTypes":["java.lang.Long"] }, {"name":"setPartitionKey","parameterTypes":["java.lang.String"] }, {"name":"setSequenceNumber","parameterTypes":["java.lang.String"] }, {"name":"setShardId","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setRecords","parameterTypes":["java.util.List"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisEvent$KinesisEventRecord", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAwsRegion","parameterTypes":["java.lang.String"] }, {"name":"setEventID","parameterTypes":["java.lang.String"] }, {"name":"setEventName","parameterTypes":["java.lang.String"] }, {"name":"setEventSource","parameterTypes":["java.lang.String"] }, {"name":"setEventSourceARN","parameterTypes":["java.lang.String"] }, {"name":"setEventVersion","parameterTypes":["java.lang.String"] }, {"name":"setInvokeIdentityArn","parameterTypes":["java.lang.String"] }, {"name":"setKinesis","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KinesisEvent$Record"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisEvent$Record", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setKinesisSchemaVersion","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setDeliveryStreamArn","parameterTypes":["java.lang.String"] }, {"name":"setInvocationId","parameterTypes":["java.lang.String"] }, {"name":"setRecords","parameterTypes":["java.util.List"] }, {"name":"setRegion","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent$Record", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setApproximateArrivalTimestamp","parameterTypes":["java.lang.Long"] }, {"name":"setData","parameterTypes":["java.nio.ByteBuffer"] }, {"name":"setRecordId","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.RabbitMQEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setEventSource","parameterTypes":["java.lang.String"] }, {"name":"setEventSourceArn","parameterTypes":["java.lang.String"] }, {"name":"setRmqMessagesByQueue","parameterTypes":["java.util.Map"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$BasicProperties", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAppId","parameterTypes":["java.lang.String"] }, {"name":"setBodySize","parameterTypes":["int"] }, {"name":"setClusterId","parameterTypes":["java.lang.String"] }, {"name":"setContentEncoding","parameterTypes":["java.lang.String"] }, {"name":"setContentType","parameterTypes":["java.lang.String"] }, {"name":"setCorrelationId","parameterTypes":["java.lang.String"] }, {"name":"setDeliveryMode","parameterTypes":["int"] }, {"name":"setExpiration","parameterTypes":["int"] }, {"name":"setHeaders","parameterTypes":["java.util.Map"] }, {"name":"setMessageId","parameterTypes":["java.lang.String"] }, {"name":"setPriority","parameterTypes":["int"] }, {"name":"setReplyTo","parameterTypes":["java.lang.String"] }, {"name":"setTimestamp","parameterTypes":["java.lang.String"] }, {"name":"setType","parameterTypes":["java.lang.String"] }, {"name":"setUserId","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$RabbitMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setBasicProperties","parameterTypes":["com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$BasicProperties"] }, {"name":"setData","parameterTypes":["java.lang.String"] }, {"name":"setRedelivered","parameterTypes":["boolean"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SNSEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setRecords","parameterTypes":["java.util.List"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SNSEvent$MessageAttribute", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setType","parameterTypes":["java.lang.String"] }, {"name":"setValue","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SNSEvent$SNS", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setMessage","parameterTypes":["java.lang.String"] }, {"name":"setMessageAttributes","parameterTypes":["java.util.Map"] }, {"name":"setMessageId","parameterTypes":["java.lang.String"] }, {"name":"setSignature","parameterTypes":["java.lang.String"] }, {"name":"setSignatureVersion","parameterTypes":["java.lang.String"] }, {"name":"setSigningCertUrl","parameterTypes":["java.lang.String"] }, {"name":"setSubject","parameterTypes":["java.lang.String"] }, {"name":"setTimestamp","parameterTypes":["org.joda.time.DateTime"] }, {"name":"setTopicArn","parameterTypes":["java.lang.String"] }, {"name":"setType","parameterTypes":["java.lang.String"] }, {"name":"setUnsubscribeUrl","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SNSEvent$SNSRecord", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setEventSource","parameterTypes":["java.lang.String"] }, {"name":"setEventSubscriptionArn","parameterTypes":["java.lang.String"] }, {"name":"setEventVersion","parameterTypes":["java.lang.String"] }, {"name":"setSns","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SNSEvent$SNS"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setRecords","parameterTypes":["java.util.List"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$MessageAttribute", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAttributes","parameterTypes":["java.util.Map"] }, {"name":"setAwsRegion","parameterTypes":["java.lang.String"] }, {"name":"setBody","parameterTypes":["java.lang.String"] }, {"name":"setEventSource","parameterTypes":["java.lang.String"] }, {"name":"setEventSourceArn","parameterTypes":["java.lang.String"] }, {"name":"setMd5OfBody","parameterTypes":["java.lang.String"] }, {"name":"setMd5OfMessageAttributes","parameterTypes":["java.lang.String"] },{"name":"setMessageAttributes","parameterTypes":["java.util.Map"] }, {"name":"setMessageId","parameterTypes":["java.lang.String"] }, {"name":"setReceiptHandle","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.ScheduledEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setAccount","parameterTypes":["java.lang.String"] }, {"name":"setDetail","parameterTypes":["java.util.Map"] }, {"name":"setDetailType","parameterTypes":["java.lang.String"] }, {"name":"setId","parameterTypes":["java.lang.String"] }, {"name":"setRegion","parameterTypes":["java.lang.String"] }, {"name":"setResources","parameterTypes":["java.util.List"] }, {"name":"setSource","parameterTypes":["java.lang.String"] }, {"name":"setTime","parameterTypes":["org.joda.time.DateTime"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.models.kinesis.Record", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"setApproximateArrivalTimestamp","parameterTypes":["java.util.Date"] }, {"name":"setData","parameterTypes":["java.nio.ByteBuffer"] }, {"name":"setEncryptionType","parameterTypes":["java.lang.String"] }, {"name":"setPartitionKey","parameterTypes":["java.lang.String"] }, {"name":"setSequenceNumber","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudFormationCustomResourceEventMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudWatchLogsEventMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.KinesisEventMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.KinesisEventMixin$RecordMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.SNSEventMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.SNSEventMixin$SNSRecordMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.SQSEventMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.SQSEventMixin$SQSMessageMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.serialization.events.mixins.ScheduledEventMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.tests.EventArgumentsProvider", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.tests.annotations.Event", + "queryAllPublicMethods":true +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Cloneable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Object", + "allDeclaredFields":true +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.function.Consumer", + "queryAllPublicMethods":true +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.joda.time.DateTime", + "methods":[{"name":"parse","parameterTypes":["java.lang.String"] }] +} +] diff --git a/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json new file mode 100644 index 000000000..58b01cd07 --- /dev/null +++ b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json @@ -0,0 +1,57 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/Europe/Amsterdam\\E" + }, { + "pattern":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/ZoneInfoMap\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/alb_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/amq_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/apigw_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/apigw_event_no_body.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/apigwv2_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/cfcr_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/custom_event_map.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/cwl_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/kafip_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/kafka_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/kasip_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/kf_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/kinesis_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/rabbitmq_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/scheduled_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/sns_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/sqs_event.json\\E" + }, { + "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/sqs_event_no_body.json\\E" + }, { + "pattern":"\\Qorg/joda/time/tz/data/Europe/Amsterdam\\E" + }, { + "pattern":"\\Qorg/joda/time/tz/data/ZoneInfoMap\\E" + }]}, + "bundles":[] +} diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java index fcfdb47e3..14fc32231 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java @@ -18,6 +18,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; @@ -34,42 +41,37 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.amazonaws.services.lambda.runtime.tests.annotations.Event; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import software.amazon.lambda.powertools.utilities.model.Basket; +import com.fasterxml.jackson.databind.JsonNode; + import software.amazon.lambda.powertools.utilities.model.Order; import software.amazon.lambda.powertools.utilities.model.Product; -public class EventDeserializerTest { +class EventDeserializerTest { @Test - public void testDeserializeStringAsString_shouldReturnString() { + void testDeserializeStringAsString_shouldReturnString() { String stringEvent = "Hello World"; String result = extractDataFrom(stringEvent).as(String.class); assertThat(result).isEqualTo(stringEvent); } @Test - public void testDeserializeStringAsObject_shouldReturnObject() { + void testDeserializeStringAsObject_shouldReturnObject() { String productStr = "{\"id\":1234, \"name\":\"product\", \"price\":42}"; Product product = extractDataFrom(productStr).as(Product.class); assertProduct(product); } @Test - public void testDeserializeStringArrayAsList_shouldReturnList() { - String productStr = - "[{\"id\":1234, \"name\":\"product\", \"price\":42}, {\"id\":2345, \"name\":\"product2\", \"price\":43}]"; + void testDeserializeStringArrayAsList_shouldReturnList() { + String productStr = "[{\"id\":1234, \"name\":\"product\", \"price\":42}, {\"id\":2345, \"name\":\"product2\", \"price\":43}]"; List<Product> products = extractDataFrom(productStr).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); } @Test - public void testDeserializeStringAsList_shouldThrowException() { + void testDeserializeStringAsList_shouldThrowException() { String productStr = "{\"id\":1234, \"name\":\"product\", \"price\":42}"; assertThatThrownBy(() -> extractDataFrom(productStr).asListOf(Product.class)) .isInstanceOf(EventDeserializationException.class) @@ -77,7 +79,7 @@ public void testDeserializeStringAsList_shouldThrowException() { } @Test - public void testDeserializeMapAsObject_shouldReturnObject() { + void testDeserializeMapAsObject_shouldReturnObject() { Map<String, Object> map = new HashMap<>(); map.put("id", 1234); map.put("name", "product"); @@ -88,29 +90,21 @@ public void testDeserializeMapAsObject_shouldReturnObject() { @ParameterizedTest @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) - public void testDeserializeAPIGWEventBodyAsObject_shouldReturnObject(APIGatewayProxyRequestEvent event) { + void testDeserializeAPIGWEventBodyAsObject_shouldReturnObject(APIGatewayProxyRequestEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } - @ParameterizedTest - @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) - public void testDeserializeAPIGWEventBodyAsWrongObjectType_shouldThrowException(APIGatewayProxyRequestEvent event) { - assertThatThrownBy(() -> extractDataFrom(event).as(Basket.class)) - .isInstanceOf(EventDeserializationException.class) - .hasMessage("Cannot load the event as Basket"); - } - @ParameterizedTest @Event(value = "sns_event.json", type = SNSEvent.class) - public void testDeserializeSNSEventMessageAsObject_shouldReturnObject(SNSEvent event) { + void testDeserializeSNSEventMessageAsObject_shouldReturnObject(SNSEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void testDeserializeSQSEventMessageAsList_shouldReturnList(SQSEvent event) { + void testDeserializeSQSEventMessageAsList_shouldReturnList(SQSEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); @@ -118,7 +112,7 @@ public void testDeserializeSQSEventMessageAsList_shouldReturnList(SQSEvent event @ParameterizedTest @Event(value = "kinesis_event.json", type = KinesisEvent.class) - public void testDeserializeKinesisEventMessageAsList_shouldReturnList(KinesisEvent event) { + void testDeserializeKinesisEventMessageAsList_shouldReturnList(KinesisEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); @@ -126,7 +120,7 @@ public void testDeserializeKinesisEventMessageAsList_shouldReturnList(KinesisEve @ParameterizedTest @Event(value = "kafka_event.json", type = KafkaEvent.class) - public void testDeserializeKafkaEventMessageAsList_shouldReturnList(KafkaEvent event) { + void testDeserializeKafkaEventMessageAsList_shouldReturnList(KafkaEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); @@ -134,7 +128,7 @@ public void testDeserializeKafkaEventMessageAsList_shouldReturnList(KafkaEvent e @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent event) { + void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent event) { assertThatThrownBy(() -> extractDataFrom(event).as(Product.class)) .isInstanceOf(EventDeserializationException.class) .hasMessageContaining("consider using 'asListOf' instead"); @@ -142,7 +136,7 @@ public void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent @ParameterizedTest @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) - public void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGatewayProxyRequestEvent event) { + void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGatewayProxyRequestEvent event) { assertThatThrownBy(() -> extractDataFrom(event).asListOf(Product.class)) .isInstanceOf(EventDeserializationException.class) .hasMessageContaining("consider using 'as' instead") @@ -151,37 +145,29 @@ public void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGateway @ParameterizedTest @Event(value = "custom_event_map.json", type = HashMap.class) - public void testDeserializeAPIGatewayMapEventAsList_shouldThrowException(Map<String, Order> event) { + void testDeserializeAPIGatewayMapEventAsList_shouldThrowException(Map<String, Order> event) { assertThatThrownBy(() -> extractDataFrom(event).asListOf(Order.class)) .isInstanceOf(EventDeserializationException.class) .hasMessage("The content of this event is not a list, consider using 'as' instead"); } @Test - public void testDeserializeEmptyEventAsList_shouldThrowException() { + void testDeserializeEmptyEventAsList_shouldThrowException() { assertThatThrownBy(() -> extractDataFrom(null).asListOf(Product.class)) .isInstanceOf(IllegalStateException.class) .hasMessage("Event content is null: the event may be malformed (missing fields)"); } - @ParameterizedTest - @Event(value = "sqs_event.json", type = SQSEvent.class) - public void testDeserializeSQSEventBodyAsWrongObjectType_shouldThrowException(SQSEvent event) { - assertThatThrownBy(() -> extractDataFrom(event).asListOf(Basket.class)) - .isInstanceOf(EventDeserializationException.class) - .hasMessage("Cannot load the event as a list of Basket"); - } - @ParameterizedTest @Event(value = "apigw_event_no_body.json", type = APIGatewayProxyRequestEvent.class) - public void testDeserializeAPIGatewayNoBody_shouldThrowException(APIGatewayProxyRequestEvent event) { + void testDeserializeAPIGatewayNoBody_shouldThrowException(APIGatewayProxyRequestEvent event) { assertThatThrownBy(() -> extractDataFrom(event).as(Product.class)) .isInstanceOf(IllegalStateException.class) .hasMessage("Event content is null: the event may be malformed (missing fields)"); } @Test - public void testDeserializeAPIGatewayNoBodyAsList_shouldThrowException() { + void testDeserializeAPIGatewayNoBodyAsList_shouldThrowException() { assertThatThrownBy(() -> extractDataFrom(new Object()).asListOf(Product.class)) .isInstanceOf(EventDeserializationException.class) .hasMessage("The content of this event is not a list, consider using 'as' instead"); @@ -189,19 +175,18 @@ public void testDeserializeAPIGatewayNoBodyAsList_shouldThrowException() { @ParameterizedTest @Event(value = "sqs_event_no_body.json", type = SQSEvent.class) - public void testDeserializeSQSEventNoBody_shouldThrowException(SQSEvent event) { + void testDeserializeSQSEventNoBody_shouldThrowException(SQSEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products.get(0)).isNull(); } @Test - public void testDeserializeProductAsProduct_shouldReturnProduct() { + void testDeserializeProductAsProduct_shouldReturnProduct() { Product myProduct = new Product(1234, "product", 42); Product product = extractDataFrom(myProduct).as(Product.class); assertProduct(product); } - private void assertProduct(Product product) { assertThat(product) .isEqualTo(new Product(1234, "product", 42)) @@ -210,28 +195,28 @@ private void assertProduct(Product product) { @ParameterizedTest @Event(value = "scheduled_event.json", type = ScheduledEvent.class) - public void testDeserializeScheduledEventMessageAsObject_shouldReturnObject(ScheduledEvent event) { + void testDeserializeScheduledEventMessageAsObject_shouldReturnObject(ScheduledEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "alb_event.json", type = ApplicationLoadBalancerRequestEvent.class) - public void testDeserializeALBEventMessageAsObjectShouldReturnObject(ApplicationLoadBalancerRequestEvent event) { + void testDeserializeALBEventMessageAsObjectShouldReturnObject(ApplicationLoadBalancerRequestEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "cwl_event.json", type = CloudWatchLogsEvent.class) - public void testDeserializeCWLEventMessageAsObjectShouldReturnObject(CloudWatchLogsEvent event) { + void testDeserializeCWLEventMessageAsObjectShouldReturnObject(CloudWatchLogsEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "kf_event.json", type = KinesisFirehoseEvent.class) - public void testDeserializeKFEventMessageAsListShouldReturnList(KinesisFirehoseEvent event) { + void testDeserializeKFEventMessageAsListShouldReturnList(KinesisFirehoseEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -239,7 +224,7 @@ public void testDeserializeKFEventMessageAsListShouldReturnList(KinesisFirehoseE @ParameterizedTest @Event(value = "amq_event.json", type = ActiveMQEvent.class) - public void testDeserializeAMQEventMessageAsListShouldReturnList(ActiveMQEvent event) { + void testDeserializeAMQEventMessageAsListShouldReturnList(ActiveMQEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -247,7 +232,7 @@ public void testDeserializeAMQEventMessageAsListShouldReturnList(ActiveMQEvent e @ParameterizedTest @Event(value = "rabbitmq_event.json", type = RabbitMQEvent.class) - public void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEvent event) { + void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -255,7 +240,7 @@ public void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEv @ParameterizedTest @Event(value = "kasip_event.json", type = KinesisAnalyticsStreamsInputPreprocessingEvent.class) - public void testDeserializeKasipEventMessageAsListShouldReturnList( + void testDeserializeKasipEventMessageAsListShouldReturnList( KinesisAnalyticsStreamsInputPreprocessingEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); @@ -264,7 +249,7 @@ public void testDeserializeKasipEventMessageAsListShouldReturnList( @ParameterizedTest @Event(value = "kafip_event.json", type = KinesisAnalyticsFirehoseInputPreprocessingEvent.class) - public void testDeserializeKafipEventMessageAsListShouldReturnList( + void testDeserializeKafipEventMessageAsListShouldReturnList( KinesisAnalyticsFirehoseInputPreprocessingEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); @@ -273,16 +258,138 @@ public void testDeserializeKafipEventMessageAsListShouldReturnList( @ParameterizedTest @Event(value = "apigwv2_event.json", type = APIGatewayV2HTTPEvent.class) - public void testDeserializeApiGWV2EventMessageAsObjectShouldReturnObject(APIGatewayV2HTTPEvent event) { + void testDeserializeApiGWV2EventMessageAsObjectShouldReturnObject(APIGatewayV2HTTPEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "cfcr_event.json", type = CloudFormationCustomResourceEvent.class) - public void testDeserializeCfcrEventMessageAsObjectShouldReturnObject(CloudFormationCustomResourceEvent event) { + void testDeserializeCfcrEventMessageAsObjectShouldReturnObject(CloudFormationCustomResourceEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } + @ParameterizedTest + @Event(value = "scheduled_event.json", type = ScheduledEvent.class) + void testSerializeScheduledEvent_shouldReturnValidJson(ScheduledEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("detail")).isTrue(); + } + + @ParameterizedTest + @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) + void testSerializeAPIGatewayEvent_shouldReturnValidJson(APIGatewayProxyRequestEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("body")).isTrue(); + } + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + void testSerializeSQSEvent_shouldReturnValidJson(SQSEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "sns_event.json", type = SNSEvent.class) + void testSerializeSNSEvent_shouldReturnValidJson(SNSEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + void testSerializeKinesisEvent_shouldReturnValidJson(KinesisEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kafka_event.json", type = KafkaEvent.class) + void testSerializeKafkaEvent_shouldReturnValidJson(KafkaEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "alb_event.json", type = ApplicationLoadBalancerRequestEvent.class) + void testSerializeALBEvent_shouldReturnValidJson(ApplicationLoadBalancerRequestEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("body")).isTrue(); + } + + @ParameterizedTest + @Event(value = "cwl_event.json", type = CloudWatchLogsEvent.class) + void testSerializeCWLEvent_shouldReturnValidJson(CloudWatchLogsEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("awsLogs")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kf_event.json", type = KinesisFirehoseEvent.class) + void testSerializeKFEvent_shouldReturnValidJson(KinesisFirehoseEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "amq_event.json", type = ActiveMQEvent.class) + void testSerializeAMQEvent_shouldReturnValidJson(ActiveMQEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("messages")).isTrue(); + } + + @ParameterizedTest + @Event(value = "rabbitmq_event.json", type = RabbitMQEvent.class) + void testSerializeRabbitMQEvent_shouldReturnValidJson(RabbitMQEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("rmqMessagesByQueue")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kasip_event.json", type = KinesisAnalyticsStreamsInputPreprocessingEvent.class) + void testSerializeKasipEvent_shouldReturnValidJson(KinesisAnalyticsStreamsInputPreprocessingEvent event) + throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kafip_event.json", type = KinesisAnalyticsFirehoseInputPreprocessingEvent.class) + void testSerializeKafipEvent_shouldReturnValidJson(KinesisAnalyticsFirehoseInputPreprocessingEvent event) + throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "apigwv2_event.json", type = APIGatewayV2HTTPEvent.class) + void testSerializeApiGWV2Event_shouldReturnValidJson(APIGatewayV2HTTPEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("body")).isTrue(); + } + + @ParameterizedTest + @Event(value = "cfcr_event.json", type = CloudFormationCustomResourceEvent.class) + void testSerializeCfcrEvent_shouldReturnValidJson(CloudFormationCustomResourceEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("resourceProperties")).isTrue(); + } + } diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java index d86af6671..478d3477c 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java @@ -16,21 +16,24 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; + import io.burt.jmespath.Expression; -import java.io.IOException; -import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class Base64FunctionTest { +class Base64FunctionTest { @Test - public void testPowertoolsBase64() throws IOException { - JsonNode event = - JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event.json")); - Expression<JsonNode> expression = - JsonConfig.get().getJmesPath().compile("basket.powertools_base64(hiddenProduct)"); + void testPowertoolsBase64() throws IOException { + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event.json")); + Expression<JsonNode> expression = JsonConfig.get().getJmesPath() + .compile("basket.powertools_base64(hiddenProduct)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{\n" + diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java index eeb605076..6f9f9a592 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java @@ -16,18 +16,21 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; + import io.burt.jmespath.Expression; import io.burt.jmespath.JmesPathType; -import java.io.IOException; -import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class Base64GZipFunctionTest { +class Base64GZipFunctionTest { @Test - public void testConstructor() { + void testConstructor() { Base64GZipFunction base64GZipFunction = new Base64GZipFunction(); assertThat(base64GZipFunction.name()).isEqualTo("powertools_base64_gzip"); assertThat(base64GZipFunction.argumentConstraints().expectedType().toLowerCase()).isEqualTo( @@ -38,18 +41,18 @@ public void testConstructor() { } @Test - public void testPowertoolsGzip() throws IOException { + void testPowertoolsGzip() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); - Expression<JsonNode> expression = - JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(hiddenProduct)"); + Expression<JsonNode> expression = JsonConfig.get().getJmesPath() + .compile("basket.powertools_base64_gzip(hiddenProduct)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{ \"id\": 43242, \"name\": \"FooBar XY\", \"price\": 258}"); } @Test - public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { + void testPowertoolsGzipEmptyJsonAttribute() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); Expression<JsonNode> expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip('')"); @@ -58,7 +61,7 @@ public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { } @Test - public void testPowertoolsGzipWrongArgumentType() throws IOException { + void testPowertoolsGzipWrongArgumentType() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); Expression<JsonNode> expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(null)"); @@ -68,21 +71,20 @@ public void testPowertoolsGzipWrongArgumentType() throws IOException { } @Test - public void testBase64GzipDecompressNull() { + void testBase64GzipDecompressNull() { String result = Base64GZipFunction.decompress(null); assertThat(result).isNull(); } @Test - public void testPowertoolsGzipNotCompressedJsonAttribute() throws IOException { + void testPowertoolsGzipNotCompressedJsonAttribute() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); - Expression<JsonNode> expression = - JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(encodedString)"); + Expression<JsonNode> expression = JsonConfig.get().getJmesPath() + .compile("basket.powertools_base64_gzip(encodedString)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("test"); } - } diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java index 0bfb635fa..028dba9ea 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java @@ -16,17 +16,20 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; + import io.burt.jmespath.Expression; -import java.io.IOException; -import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class JsonFunctionTest { +class JsonFunctionTest { @Test - public void testJsonFunction() throws IOException { + void testJsonFunction() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); Expression<JsonNode> expression = JsonConfig.get().getJmesPath().compile("powertools_json(body)"); @@ -38,7 +41,7 @@ public void testJsonFunction() throws IOException { } @Test - public void testJsonFunctionChild() throws IOException { + void testJsonFunctionChild() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); Expression<JsonNode> expression = JsonConfig.get().getJmesPath().compile("powertools_json(body).list[0].item"); diff --git a/powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json b/powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json new file mode 100644 index 000000000..b781d2609 --- /dev/null +++ b/powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json @@ -0,0 +1,45 @@ +[ +{ + "name":"software.amazon.lambda.powertools.utilities.EventDeserializerTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testDeserializeALBEventMessageAsObjectShouldReturnObject","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent"] }, {"name":"testDeserializeAMQEventMessageAsListShouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ActiveMQEvent"] }, {"name":"testDeserializeAPIGWEventBodyAsObject_shouldReturnObject","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent"] }, {"name":"testDeserializeAPIGatewayEventAsList_shouldThrowException","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent"] }, {"name":"testDeserializeAPIGatewayMapEventAsList_shouldThrowException","parameterTypes":["java.util.Map"] }, {"name":"testDeserializeAPIGatewayNoBodyAsList_shouldThrowException","parameterTypes":[] }, {"name":"testDeserializeAPIGatewayNoBody_shouldThrowException","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent"] }, {"name":"testDeserializeApiGWV2EventMessageAsObjectShouldReturnObject","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent"] }, {"name":"testDeserializeCWLEventMessageAsObjectShouldReturnObject","parameterTypes":["com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent"] }, {"name":"testDeserializeCfcrEventMessageAsObjectShouldReturnObject","parameterTypes":["com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent"] }, {"name":"testDeserializeEmptyEventAsList_shouldThrowException","parameterTypes":[] }, {"name":"testDeserializeKFEventMessageAsListShouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent"] }, {"name":"testDeserializeKafipEventMessageAsListShouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent"] }, {"name":"testDeserializeKafkaEventMessageAsList_shouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KafkaEvent"] }, {"name":"testDeserializeKasipEventMessageAsListShouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent"] }, {"name":"testDeserializeKinesisEventMessageAsList_shouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.KinesisEvent"] }, {"name":"testDeserializeMapAsObject_shouldReturnObject","parameterTypes":[] }, {"name":"testDeserializeProductAsProduct_shouldReturnProduct","parameterTypes":[] }, {"name":"testDeserializeRabbitMQEventMessageAsListShouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.RabbitMQEvent"] }, {"name":"testDeserializeSNSEventMessageAsObject_shouldReturnObject","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SNSEvent"] }, {"name":"testDeserializeSQSEventMessageAsList_shouldReturnList","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SQSEvent"] }, {"name":"testDeserializeSQSEventMessageAsObject_shouldThrowException","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SQSEvent"] }, {"name":"testDeserializeSQSEventNoBody_shouldThrowException","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SQSEvent"] }, {"name":"testDeserializeScheduledEventMessageAsObject_shouldReturnObject","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ScheduledEvent"] }, {"name":"testDeserializeStringArrayAsList_shouldReturnList","parameterTypes":[] }, {"name":"testDeserializeStringAsList_shouldThrowException","parameterTypes":[] }, {"name":"testDeserializeStringAsObject_shouldReturnObject","parameterTypes":[] }, {"name":"testDeserializeStringAsString_shouldReturnString","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.utilities.jmespath.Base64FunctionTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testPowertoolsBase64","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunctionTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testBase64GzipDecompressNull","parameterTypes":[] }, {"name":"testConstructor","parameterTypes":[] }, {"name":"testPowertoolsGzip","parameterTypes":[] }, {"name":"testPowertoolsGzipEmptyJsonAttribute","parameterTypes":[] }, {"name":"testPowertoolsGzipNotCompressedJsonAttribute","parameterTypes":[] }, {"name":"testPowertoolsGzipWrongArgumentType","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.utilities.jmespath.JsonFunctionTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testJsonFunction","parameterTypes":[] }, {"name":"testJsonFunctionChild","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.utilities.model.Product", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setId","parameterTypes":["long"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"setPrice","parameterTypes":["double"] }] +} +] diff --git a/powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json b/powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json new file mode 100644 index 000000000..986bf5b4a --- /dev/null +++ b/powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json @@ -0,0 +1,51 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qalb_event.json\\E" + }, { + "pattern":"\\Qamq_event.json\\E" + }, { + "pattern":"\\Qapigw_event.json\\E" + }, { + "pattern":"\\Qapigw_event_no_body.json\\E" + }, { + "pattern":"\\Qapigwv2_event.json\\E" + }, { + "pattern":"\\Qcfcr_event.json\\E" + }, { + "pattern":"\\Qcustom_event.json\\E" + }, { + "pattern":"\\Qcustom_event_gzip.json\\E" + }, { + "pattern":"\\Qcustom_event_json.json\\E" + }, { + "pattern":"\\Qcustom_event_map.json\\E" + }, { + "pattern":"\\Qcwl_event.json\\E" + }, { + "pattern":"\\Qjunit-platform.properties\\E" + }, { + "pattern":"\\Qkafip_event.json\\E" + }, { + "pattern":"\\Qkafka_event.json\\E" + }, { + "pattern":"\\Qkasip_event.json\\E" + }, { + "pattern":"\\Qkf_event.json\\E" + }, { + "pattern":"\\Qkinesis_event.json\\E" + }, { + "pattern":"\\Qrabbitmq_event.json\\E" + }, { + "pattern":"\\Qscheduled_event.json\\E" + }, { + "pattern":"\\Qsimplelogger.properties\\E" + }, { + "pattern":"\\Qsns_event.json\\E" + }, { + "pattern":"\\Qsqs_event.json\\E" + }, { + "pattern":"\\Qsqs_event_no_body.json\\E" + }]}, + "bundles":[] +} diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml deleted file mode 100644 index c21943fba..000000000 --- a/powertools-sqs/pom.xml +++ /dev/null @@ -1,144 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ Copyright 2023 Amazon.com, Inc. or its affiliates. - ~ Licensed under the Apache License, Version 2.0 (the - ~ "License"); you may not use this file except in compliance - ~ with the License. You may obtain a copy of the License at - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - ~ - --> - -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <artifactId>powertools-sqs</artifactId> - <packaging>jar</packaging> - - <parent> - <artifactId>powertools-parent</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> - </parent> - - <name>Powertools for AWS Lambda (Java) library SQS</name> - <description> - Deprecated: Batch processing is now handled in powertools-batch and large messages in powertools-large-messages modules. - A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. - </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-tests</artifactId> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.payloadoffloading</groupId> - <artifactId>payloadoffloading-common</artifactId> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>sqs</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.awssdk</groupId> - <artifactId>s3</artifactId> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-databind</artifactId> - </dependency> - - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> - - <!-- Test dependencies --> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-params</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjweaver</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> - </plugins> - </build> - -</project> \ No newline at end of file diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java deleted file mode 100644 index 7adc2afe5..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.joining; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.util.ArrayList; -import java.util.List; - -/** - * <p> - * When one or more {@link SQSMessage} fails and if any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} - * during processing of a messages, this exception is with all the details of successful and failed messages. - * </p> - * - * <p> - * This exception can be thrown form: - * <ul> - * <li>{@link SqsBatch}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, Class)}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, SqsMessageHandler)}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)}</li> - * </ul> - * </p> - */ -public class SQSBatchProcessingException extends RuntimeException { - - private final List<Exception> exceptions; - private final List<SQSMessage> failures; - private final List<Object> returnValues; - - public <T> SQSBatchProcessingException(final List<Exception> exceptions, - final List<SQSMessage> failures, - final List<T> successReturns) { - super(exceptions.stream() - .map(Throwable::toString) - .collect(joining("\n"))); - - this.exceptions = new ArrayList<>(exceptions); - this.failures = new ArrayList<>(failures); - this.returnValues = new ArrayList<>(successReturns); - } - - /** - * Details for exceptions that occurred while processing messages in {@link SqsMessageHandler#process(SQSMessage)} - * - * @return List of exceptions that occurred while processing messages - */ - public List<Exception> getExceptions() { - return unmodifiableList(exceptions); - } - - /** - * List of returns from {@link SqsMessageHandler#process(SQSMessage)} that were successfully processed. - * - * @return List of returns from successfully processed messages - */ - public List<Object> successMessageReturnValues() { - return unmodifiableList(returnValues); - } - - /** - * Details of {@link SQSMessage} that failed in {@link SqsMessageHandler#process(SQSMessage)} - * - * @return List of failed messages - */ - public List<SQSMessage> getFailures() { - return unmodifiableList(failures); - } - - @Override - public void printStackTrace() { - for (Exception exception : exceptions) { - exception.printStackTrace(); - } - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java deleted file mode 100644 index 4378fa707..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * {@link SqsBatch} is used to process batch messages in {@link SQSEvent} - * - * <p> - * When using the annotation, implementation of {@link SqsMessageHandler} is required. Annotation will take care of - * calling {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} in the received {@link SQSEvent} - * </p> - * - * <p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a messages, Utility - * will take care of deleting all the successful messages from SQS. When one or more single message fails processing due - * to exception thrown from {@link SqsMessageHandler#process(SQSMessage)}, Lambda execution will fail - * with {@link SQSBatchProcessingException}. - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * <p> - * If you want to suppress the exception even if any message in batch fails, set - * {@link SqsBatch#suppressException()} to true. By default its value is false - * </p> - * - * <p> - * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * <p> - * you can use {@link SqsBatch#nonRetryableExceptions()} to configure such exceptions. - * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - * <p> - * If you want such messages to be deleted instead, set {@link SqsBatch#deleteNonRetryableMessageFromQueue()} to true. - * By default its value is false. - * <p> - * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - * </p> - * - * <pre> - * public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - * - * {@literal @}Override - * {@literal @}{@link SqsBatch (SqsMessageHandler)} - * public String handleRequest(SQSEvent sqsEvent, Context context) { - * - * return "ok"; - * } - * - * public class DummySqsMessageHandler implements SqsMessageHandler<Object>{ - * @Override - * public Object process(SQSEvent.SQSMessage message) { - * throw new UnsupportedOperationException(); - * } - * } - * - * ... - * </pre> - * - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Deprecated -public @interface SqsBatch { - - Class<? extends SqsMessageHandler<Object>> value(); - - boolean suppressException() default false; - - Class<? extends Exception>[] nonRetryableExceptions() default {}; - - boolean deleteNonRetryableMessageFromQueue() default false; -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java deleted file mode 100644 index a3a92cea1..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @deprecated See software.amazon.lambda.powertools.largemessages.LargeMessage in powertools-large-messages module. - * Will be removed in version 2. - * - * <p>{@code SqsLargeMessage} is used to signal that the annotated method - * should be extended to handle large SQS messages which have been offloaded - * to S3</p> - * - * <p>{@code SqsLargeMessage} automatically retrieves and deletes messages - * which have been offloaded to S3 using the {@code amazon-sqs-java-extended-client-lib} - * client library.</p> - * - * <p>This version of the {@code SqsLargeMessage} is compatible with version - * 1.1.0+ of {@code amazon-sqs-java-extended-client-lib}.</p> - * - * <pre> - * <dependency> - * <groupId>com.amazonaws</groupId> - * <artifactId>amazon-sqs-java-extended-client-lib</artifactId> - * <version>1.1.0</version> - * </dependency> - * </pre> - * - * <p>{@code SqsLargeMessage} should be used with the handleRequest method of a class - * which implements {@code com.amazonaws.services.lambda.runtime.RequestHandler} with - * {@code com.amazonaws.services.lambda.runtime.events.SQSEvent} as the first parameter.</p> - * - * <pre> - * public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - * - * {@literal @}Override - * {@literal @}SqsLargeMessage - * public String handleRequest(SQSEvent sqsEvent, Context context) { - * - * // process messages - * - * return "ok"; - * } - * - * ... - * </pre> - * - * <p>Using the default S3 Client {@code AmazonS3 amazonS3 = AmazonS3ClientBuilder.defaultClient();} - * each record received in the SQSEvent {@code SqsLargeMessage} will checked - * to see if it's body contains a payload which has been offloaded to S3. If it - * does then {@code getObject(bucket, key)} will be called and the payload - * retrieved.</p> - * - * <p><b>Note</b>: Retreiving payloads from S3 will increase the duration of the - * Lambda function.</p> - * - * <p>If the request handler method returns then each payload will be deleted - * from S3 using {@code deleteObject(bucket, key)}</p> - * - * <p>To disable deletion of payloads setting the following annotation parameter - * {@code @SqsLargeMessage(deletePayloads=false)}</p> - */ -@Deprecated -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface SqsLargeMessage { - - boolean deletePayloads() default true; -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java deleted file mode 100644 index 0c8f03ee9..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; - -/** - * <p> - * This interface should be implemented for processing {@link SQSMessage} inside {@link SQSEvent} received by lambda - * function. - * </p> - * - * <p> - * It is required by utilities: - * <ul> - * <li>{@link SqsBatch}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, Class)}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, SqsMessageHandler)}</li> - * <li>{@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)}</li> - * </ul> - * </p> - * - * @param <R> Return value type from {@link SqsMessageHandler#process(SQSMessage)} - */ -@FunctionalInterface -public interface SqsMessageHandler<R> { - - R process(SQSMessage message); -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java deleted file mode 100644 index d89642780..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.processMessages; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.lambda.powertools.core.internal.UserAgentConfigurator; -import software.amazon.lambda.powertools.sqs.exception.SkippedMessageDueToFailedBatchException; -import software.amazon.lambda.powertools.sqs.internal.BatchContext; -import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; -import software.amazon.payloadoffloading.PayloadS3Pointer; - -/** - * A class of helper functions to add additional functionality to {@link SQSEvent} processing. - * - * @deprecated Batch processing is now handled in <b>powertools-batch</b> and large messages in <b>powertools-large-messages</b>. - * This class will no longer be available in version 2. - */ -@Deprecated -public final class SqsUtils { - - public static final String SQS = "sqs"; - private static final Logger LOG = LoggerFactory.getLogger(SqsUtils.class); - private static final ObjectMapper objectMapper = new ObjectMapper(); - private static final String MESSAGE_GROUP_ID = "MessageGroupId"; - private static SqsClient client; - private static S3Client s3Client; - - private SqsUtils() { - } - - /** - * This is a utility method when you want to avoid using {@code SqsLargeMessage} annotation. - * Gives you access to enriched messages from S3 in the SQS event produced via extended client lib. - * If all the large S3 payload are successfully retrieved, it will delete them from S3 post success. - * - * @param sqsEvent Event received from SQS Extended client library - * @param messageFunction Function to execute you business logic which provides access to enriched messages from S3 when needed. - * @return Return value from the function. - */ - public static <R> R enrichedMessageFromS3(final SQSEvent sqsEvent, - final Function<List<SQSMessage>, R> messageFunction) { - return enrichedMessageFromS3(sqsEvent, true, messageFunction); - } - - /** - * This is a utility method when you want to avoid using {@code SqsLargeMessage} annotation. - * Gives you access to enriched messages from S3 in the SQS event produced via extended client lib. - * if all the large S3 payload are successfully retrieved, Control if it will delete payload from S3 post success. - * - * @param sqsEvent Event received from SQS Extended client library - * @param messageFunction Function to execute you business logic which provides access to enriched messages from S3 when needed. - * @return Return value from the function. - */ - public static <R> R enrichedMessageFromS3(final SQSEvent sqsEvent, - final boolean deleteS3Payload, - final Function<List<SQSMessage>, R> messageFunction) { - - List<SQSMessage> sqsMessages = sqsEvent.getRecords().stream() - .map(SqsUtils::clonedMessage) - .collect(Collectors.toList()); - - List<PayloadS3Pointer> s3Pointers = processMessages(sqsMessages); - - R returnValue = messageFunction.apply(sqsMessages); - - if (deleteS3Payload) { - s3Pointers.forEach(SqsLargeMessageAspect::deleteMessage); - } - - return returnValue; - } - - /** - * Provides ability to set default {@link SqsClient} to be used by utility. - * If no default configuration is provided, client is instantiated via {@link SqsClient#create()} - * - * @param client {@link SqsClient} to be used by utility - */ - public static void overrideSqsClient(SqsClient client) { - SqsUtils.client = client; - } - - /** - * By default, the S3Client is instantiated via {@link S3Client#create()}. - * This method provides the ability to override the S3Client with your own custom version. - * - * @param s3Client {@link S3Client} to be used by utility - */ - public static void overrideS3Client(S3Client s3Client) { - SqsUtils.s3Client = s3Client; - } - - /** - * - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * - * </p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * <p> - * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - */ - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final Class<? extends SqsMessageHandler<R>> handler) { - return batchProcessor(event, false, handler); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * <p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - * </p> - * - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * <p> - * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - * </p> - * - * <p> - * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - * <p> - * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - * <p> - * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - */ - @SafeVarargs - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final Class<? extends SqsMessageHandler<R>> handler, - final Class<? extends Exception>... nonRetryableExceptions) { - return batchProcessor(event, false, handler, nonRetryableExceptions); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * </p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * <p> - * Exception can also be suppressed if desired. - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing and no suppression enabled. - */ - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final boolean suppressException, - final Class<? extends SqsMessageHandler<R>> handler) { - - SqsMessageHandler<R> handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * <p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - * </p> - * - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * <p> - * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - * </p> - * - * <p> - * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - * <p> - * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - * <p> - * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - */ - @SafeVarargs - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final boolean suppressException, - final Class<? extends SqsMessageHandler<R>> handler, - final Class<? extends Exception>... nonRetryableExceptions) { - - SqsMessageHandler<R> handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance, false, nonRetryableExceptions); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * - * <p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - * </p> - * - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * <p> - * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - * </p> - * - * <p> - * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - * <p> - * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - * <p> - * If you want such messages to be deleted instead, set deleteNonRetryableMessageFromQueue to true. - * <p> - * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param deleteNonRetryableMessageFromQueue If messages with nonRetryableExceptions are to be deleted from SQS queue. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - */ - @SafeVarargs - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final boolean suppressException, - final Class<? extends SqsMessageHandler<R>> handler, - final boolean deleteNonRetryableMessageFromQueue, - final Class<? extends Exception>... nonRetryableExceptions) { - - SqsMessageHandler<R> handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance, deleteNonRetryableMessageFromQueue, - nonRetryableExceptions); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * - * </p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a messages, - * Utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * <p> - * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)} - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message- - * @throws SQSBatchProcessingException if some messages fail during processing. - */ - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final SqsMessageHandler<R> handler) { - return batchProcessor(event, false, handler); - } - - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * - * <p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - * </p> - * - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * <p> - * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - * </p> - * - * <p> - * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - * <p> - * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - * <p> - * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing.The same behaviour will occur if - * for some reason the utility is unable to moved the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - */ - @SafeVarargs - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final SqsMessageHandler<R> handler, - final Class<? extends Exception>... nonRetryableExceptions) { - return batchProcessor(event, false, handler, false, nonRetryableExceptions); - } - - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - * <p> - * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - * </p> - * - * </p> - * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a messages, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * <p> - * Exception can also be suppressed if desired. - * <p> - * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - * </p> - * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing and no suppression enabled. - */ - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final boolean suppressException, - final SqsMessageHandler<R> handler) { - return batchProcessor(event, suppressException, handler, false); - - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - */ - @SafeVarargs - @Deprecated - public static <R> List<R> batchProcessor(final SQSEvent event, - final boolean suppressException, - final SqsMessageHandler<R> handler, - final boolean deleteNonRetryableMessageFromQueue, - final Class<? extends Exception>... nonRetryableExceptions) { - final List<R> handlerReturn = new ArrayList<>(); - - if (client == null) { - client = (SqsClient) SqsClient.builder() - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(SQS)) - .build()); - } - - BatchContext batchContext = new BatchContext(client); - int offset = 0; - boolean failedBatch = false; - while (offset < event.getRecords().size() && !failedBatch) { - // Get the current message and advance to the next. Doing this here - // makes it easier for us to know where we are up to if we have to - // break out of here early. - SQSMessage message = event.getRecords().get(offset); - offset++; - - // If the batch hasn't failed, try process the message - try { - handlerReturn.add(handler.process(message)); - batchContext.addSuccess(message); - } catch (Exception e) { - - // Record the failure - batchContext.addFailure(message, e); - - // If we are trying to process a message that has a messageGroupId, we are on a FIFO queue. A failure - // now stops us from processing the rest of the batch; we break out of the loop leaving unprocessed - // messages in the queue - // https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting - String messageGroupId = message.getAttributes() != null ? - message.getAttributes().get(MESSAGE_GROUP_ID) : null; - if (messageGroupId != null) { - LOG.info( - "A message in a message batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too" - , messageGroupId, message.getMessageId()); - failedBatch = true; - } - LOG.error("Encountered issue processing message: {}", message.getMessageId(), e); - } - } - - // If we have a FIFO batch failure, unprocessed messages will remain on the queue - // past the failed message. We have to add these to the errors - if (offset < event.getRecords().size()) { - event.getRecords() - .subList(offset, event.getRecords().size()) - .forEach(message -> - { - LOG.info("Skipping message {} as another message with a message group failed in this batch", - message.getMessageId()); - batchContext.addFailure(message, new SkippedMessageDueToFailedBatchException()); - }); - } - - batchContext.processSuccessAndHandleFailed(handlerReturn, suppressException, deleteNonRetryableMessageFromQueue, - nonRetryableExceptions); - return handlerReturn; - } - - private static <R> SqsMessageHandler<R> instantiatedHandler(final Class<? extends SqsMessageHandler<R>> handler) { - - try { - if (null == handler.getDeclaringClass()) { - return handler.getDeclaredConstructor().newInstance(); - } - - final Constructor<? extends SqsMessageHandler<R>> constructor = - handler.getDeclaredConstructor(handler.getDeclaringClass()); - constructor.setAccessible(true); - return constructor.newInstance(handler.getDeclaringClass().getDeclaredConstructor().newInstance()); - } catch (Exception e) { - LOG.error("Failed creating handler instance", e); - throw new RuntimeException("Unexpected error occurred. Please raise issue at " + - "https://github.com/aws-powertools/powertools-lambda-java/issues", e); - } - } - - private static SQSMessage clonedMessage(final SQSMessage sqsMessage) { - try { - return objectMapper - .readValue(objectMapper.writeValueAsString(sqsMessage), SQSMessage.class); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - public static ObjectMapper objectMapper() { - return objectMapper; - } - - public static S3Client s3Client() { - if (null == s3Client) { - s3Client = (S3Client) S3Client.builder() - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(SQS)) - .build()); - SqsUtils.s3Client = S3Client.create(); - } - - return s3Client; - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java deleted file mode 100644 index 70cf04fb8..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.lang.String.format; -import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toList; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchResponse; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse; -import software.amazon.awssdk.services.sqs.model.MessageAttributeValue; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import software.amazon.lambda.powertools.sqs.SQSBatchProcessingException; -import software.amazon.lambda.powertools.sqs.SqsUtils; - -public final class BatchContext { - private static final Logger LOG = LoggerFactory.getLogger(BatchContext.class); - private static final Map<String, String> QUEUE_ARN_TO_DLQ_URL_MAPPING = new HashMap<>(); - - private final Map<SQSMessage, Exception> messageToException = new HashMap<>(); - private final List<SQSMessage> success = new ArrayList<>(); - - private final SqsClient client; - - public BatchContext(SqsClient client) { - this.client = client; - } - - public void addSuccess(SQSMessage event) { - success.add(event); - } - - public void addFailure(SQSMessage event, Exception e) { - messageToException.put(event, e); - } - - @SafeVarargs - public final <T> void processSuccessAndHandleFailed(final List<T> successReturns, - final boolean suppressException, - final boolean deleteNonRetryableMessageFromQueue, - final Class<? extends Exception>... nonRetryableExceptions) { - if (hasFailures()) { - - List<Exception> exceptions = new ArrayList<>(); - List<SQSMessage> failedMessages = new ArrayList<>(); - Map<SQSMessage, Exception> nonRetryableMessageToException = new HashMap<>(); - - if (nonRetryableExceptions.length == 0) { - exceptions.addAll(messageToException.values()); - failedMessages.addAll(messageToException.keySet()); - } else { - messageToException.forEach((sqsMessage, exception) -> - { - boolean nonRetryableException = isNonRetryableException(exception, nonRetryableExceptions); - - if (nonRetryableException) { - nonRetryableMessageToException.put(sqsMessage, exception); - } else { - exceptions.add(exception); - failedMessages.add(sqsMessage); - } - }); - } - - List<SQSMessage> messagesToBeDeleted = new ArrayList<>(success); - - if (!nonRetryableMessageToException.isEmpty() && deleteNonRetryableMessageFromQueue) { - messagesToBeDeleted.addAll(nonRetryableMessageToException.keySet()); - } else if (!nonRetryableMessageToException.isEmpty()) { - - boolean isMovedToDlq = moveNonRetryableMessagesToDlqIfConfigured(nonRetryableMessageToException); - - if (!isMovedToDlq) { - exceptions.addAll(nonRetryableMessageToException.values()); - failedMessages.addAll(nonRetryableMessageToException.keySet()); - } - } - - deleteMessagesFromQueue(messagesToBeDeleted); - - processFailedMessages(successReturns, suppressException, exceptions, failedMessages); - } - } - - private <T> void processFailedMessages(List<T> successReturns, - boolean suppressException, - List<Exception> exceptions, - List<SQSMessage> failedMessages) { - if (failedMessages.isEmpty()) { - return; - } - - if (suppressException) { - List<String> messageIds = failedMessages.stream(). - map(SQSMessage::getMessageId) - .collect(toList()); - - LOG.debug(format("[%d] records failed processing, but exceptions are suppressed. " + - "Failed messages %s", failedMessages.size(), messageIds)); - } else { - throw new SQSBatchProcessingException(exceptions, failedMessages, successReturns); - } - } - - private boolean isNonRetryableException(Exception exception, Class<? extends Exception>[] nonRetryableExceptions) { - return Arrays.stream(nonRetryableExceptions) - .anyMatch(aClass -> aClass.isInstance(exception)); - } - - private boolean moveNonRetryableMessagesToDlqIfConfigured( - Map<SQSMessage, Exception> nonRetryableMessageToException) { - Optional<String> dlqUrl = fetchDlqUrl(nonRetryableMessageToException); - - if (!dlqUrl.isPresent()) { - return false; - } - - List<SendMessageBatchRequestEntry> dlqMessages = nonRetryableMessageToException.keySet().stream() - .map(sqsMessage -> - { - Map<String, MessageAttributeValue> messageAttributesMap = new HashMap<>(); - - sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> - { - MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); - - builder - .dataType(messageAttribute.getDataType()) - .stringValue(messageAttribute.getStringValue()); - - if (null != messageAttribute.getBinaryValue()) { - builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); - } - - messageAttributesMap.put(s, builder.build()); - }); - - return SendMessageBatchRequestEntry.builder() - .messageBody(sqsMessage.getBody()) - .id(sqsMessage.getMessageId()) - .messageAttributes(messageAttributesMap) - .build(); - }) - .collect(toList()); - - List<SendMessageBatchResponse> sendMessageBatchResponses = batchRequest(dlqMessages, 10, entriesToSend -> - { - - SendMessageBatchResponse sendMessageBatchResponse = - client.sendMessageBatch(SendMessageBatchRequest.builder() - .entries(entriesToSend) - .queueUrl(dlqUrl.get()) - .build()); - - - LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); - - return sendMessageBatchResponse; - }); - - return sendMessageBatchResponses.stream() - .filter(response -> null != response && response.hasFailed()) - .peek(sendMessageBatchResponse -> LOG.error( - "Failed sending message to the DLQ. Entire batch will be re processed. Check if needed permissions are configured for the function. Response: {}", - sendMessageBatchResponse)) - .count() == 0; - } - - - private Optional<String> fetchDlqUrl(Map<SQSMessage, Exception> nonRetryableMessageToException) { - return nonRetryableMessageToException.keySet().stream() - .findFirst() - .map(sqsMessage -> QUEUE_ARN_TO_DLQ_URL_MAPPING.computeIfAbsent(sqsMessage.getEventSourceArn(), - sourceArn -> - { - String queueUrl = url(sourceArn); - - GetQueueAttributesResponse queueAttributes = - client.getQueueAttributes(GetQueueAttributesRequest.builder() - .attributeNames(QueueAttributeName.REDRIVE_POLICY) - .queueUrl(queueUrl) - .build()); - - return ofNullable(queueAttributes.attributes().get(QueueAttributeName.REDRIVE_POLICY)) - .map(policy -> - { - try { - return SqsUtils.objectMapper().readTree(policy); - } catch (JsonProcessingException e) { - LOG.debug( - "Unable to parse Re drive policy for queue {}. Even if DLQ exists, failed messages will be send back to main queue.", - queueUrl, e); - return null; - } - }) - .map(node -> node.get("deadLetterTargetArn")) - .map(JsonNode::asText) - .map(this::url) - .orElse(null); - })); - } - - private boolean hasFailures() { - return !messageToException.isEmpty(); - } - - private void deleteMessagesFromQueue(final List<SQSMessage> messages) { - if (!messages.isEmpty()) { - - List<DeleteMessageBatchRequestEntry> entries = - messages.stream().map(m -> DeleteMessageBatchRequestEntry.builder() - .id(m.getMessageId()) - .receiptHandle(m.getReceiptHandle()) - .build()).collect(toList()); - - batchRequest(entries, 10, entriesToDelete -> - { - DeleteMessageBatchRequest request = DeleteMessageBatchRequest.builder() - .queueUrl(url(messages.get(0).getEventSourceArn())) - .entries(entriesToDelete) - .build(); - - DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); - - LOG.debug("Response from delete request {}", deleteMessageBatchResponse); - - return deleteMessageBatchResponse; - }); - } - } - - private <T, R> List<R> batchRequest(final List<T> listOFEntries, - final int size, - final Function<List<T>, R> batchLogic) { - - return IntStream.range(0, listOFEntries.size()) - .filter(index -> index % size == 0) - .mapToObj(index -> listOFEntries.subList(index, Math.min(index + size, listOFEntries.size()))) - .map(batchLogic) - .collect(Collectors.toList()); - } - - private String url(String queueArn) { - String[] arnArray = queueArn.split(":"); - return String.format("https://sqs.%s.amazonaws.com/%s/%s", arnArray[3], arnArray[4], arnArray[5]); - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java deleted file mode 100644 index e5176e13a..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.lang.String.format; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.sqs.SqsUtils.s3Client; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.S3Exception; -import software.amazon.awssdk.utils.IoUtils; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; -import software.amazon.payloadoffloading.PayloadS3Pointer; - -@Aspect -public class SqsLargeMessageAspect { - - private static final Logger LOG = LoggerFactory.getLogger(SqsLargeMessageAspect.class); - - public static List<PayloadS3Pointer> processMessages(final List<SQSMessage> records) { - List<PayloadS3Pointer> s3Pointers = new ArrayList<>(); - for (SQSMessage sqsMessage : records) { - if (isBodyLargeMessagePointer(sqsMessage.getBody())) { - - PayloadS3Pointer s3Pointer = Optional.ofNullable(PayloadS3Pointer.fromJson(sqsMessage.getBody())) - .orElseThrow(() -> new FailedProcessingLargePayloadException( - format("Failed processing SQS body to extract S3 details. [ %s ].", - sqsMessage.getBody()))); - - ResponseInputStream<GetObjectResponse> s3Object = callS3Gracefully(s3Pointer, pointer -> - { - ResponseInputStream<GetObjectResponse> response = - s3Client().getObject(GetObjectRequest.builder() - .bucket(pointer.getS3BucketName()) - .key(pointer.getS3Key()) - .build()); - - LOG.debug("Object downloaded with key: " + s3Pointer.getS3Key()); - return response; - }); - - sqsMessage.setBody(readStringFromS3Object(s3Object, s3Pointer)); - s3Pointers.add(s3Pointer); - } - } - - return s3Pointers; - } - - private static boolean isBodyLargeMessagePointer(String record) { - return record.startsWith("[\"software.amazon.payloadoffloading.PayloadS3Pointer\""); - } - - private static String readStringFromS3Object(ResponseInputStream<GetObjectResponse> response, - PayloadS3Pointer s3Pointer) { - try (ResponseInputStream<GetObjectResponse> content = response) { - return IoUtils.toUtf8String(content); - } catch (IOException e) { - LOG.error("Error converting S3 object to String", e); - throw new FailedProcessingLargePayloadException( - format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", - s3Pointer.getS3BucketName(), s3Pointer.getS3Key()), e); - } - } - - public static void deleteMessage(PayloadS3Pointer s3Pointer) { - callS3Gracefully(s3Pointer, pointer -> - { - s3Client().deleteObject(DeleteObjectRequest.builder() - .bucket(pointer.getS3BucketName()) - .key(pointer.getS3Key()) - .build()); - LOG.info("Message deleted from S3: " + s3Pointer.toJson()); - return null; - }); - } - - private static <R> R callS3Gracefully(final PayloadS3Pointer pointer, - final Function<PayloadS3Pointer, R> function) { - try { - return function.apply(pointer); - } catch (S3Exception e) { - LOG.error("A service exception", e); - throw new FailedProcessingLargePayloadException( - format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", - pointer.getS3BucketName(), pointer.getS3Key()), e); - } catch (SdkClientException e) { - LOG.error("Some sort of client exception", e); - throw new FailedProcessingLargePayloadException( - format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", - pointer.getS3BucketName(), pointer.getS3Key()), e); - } - } - - public static boolean placedOnSqsEventRequestHandler(ProceedingJoinPoint pjp) { - return pjp.getArgs().length == 2 - && pjp.getArgs()[0] instanceof SQSEvent - && pjp.getArgs()[1] instanceof Context; - } - - @SuppressWarnings({"EmptyMethod"}) - @Pointcut("@annotation(sqsLargeMessage)") - public void callAt(SqsLargeMessage sqsLargeMessage) { - } - - @Around(value = "callAt(sqsLargeMessage) && execution(@SqsLargeMessage * *.*(..))", argNames = "pjp,sqsLargeMessage") - public Object around(ProceedingJoinPoint pjp, - SqsLargeMessage sqsLargeMessage) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); - - if (isHandlerMethod(pjp) - && placedOnSqsEventRequestHandler(pjp)) { - List<PayloadS3Pointer> pointersToDelete = rewriteMessages((SQSEvent) proceedArgs[0]); - - Object proceed = pjp.proceed(proceedArgs); - - if (sqsLargeMessage.deletePayloads()) { - pointersToDelete.forEach(SqsLargeMessageAspect::deleteMessage); - } - return proceed; - } - - return pjp.proceed(proceedArgs); - } - - private List<PayloadS3Pointer> rewriteMessages(SQSEvent sqsEvent) { - List<SQSMessage> records = sqsEvent.getRecords(); - return processMessages(records); - } - - public static class FailedProcessingLargePayloadException extends RuntimeException { - public FailedProcessingLargePayloadException(String message, Throwable cause) { - super(message, cause); - } - - public FailedProcessingLargePayloadException(String message) { - super(message); - } - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java deleted file mode 100644 index ff0b5b014..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.placedOnSqsEventRequestHandler; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import software.amazon.lambda.powertools.sqs.SqsBatch; - -@Aspect -public class SqsMessageBatchProcessorAspect { - - @SuppressWarnings({"EmptyMethod"}) - @Pointcut("@annotation(sqsBatch)") - public void callAt(SqsBatch sqsBatch) { - } - - @Around(value = "callAt(sqsBatch) && execution(@SqsBatch * *.*(..))", argNames = "pjp,sqsBatch") - public Object around(ProceedingJoinPoint pjp, - SqsBatch sqsBatch) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); - - if (isHandlerMethod(pjp) - && placedOnSqsEventRequestHandler(pjp)) { - - SQSEvent sqsEvent = (SQSEvent) proceedArgs[0]; - - batchProcessor(sqsEvent, - sqsBatch.suppressException(), - sqsBatch.value(), - sqsBatch.deleteNonRetryableMessageFromQueue(), - sqsBatch.nonRetryableExceptions()); - } - - return pjp.proceed(proceedArgs); - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java deleted file mode 100644 index c0c334e78..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.ArgumentCaptor; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; - -class SqsUtilsBatchProcessorTest { - - private static final SqsClient sqsClient = mock(SqsClient.class); - private static final SqsClient interactionClient = mock(SqsClient.class); - private static final ObjectMapper MAPPER = new ObjectMapper(); - private SQSEvent event; - - @BeforeEach - void setUp() throws IOException { - reset(sqsClient, interactionClient); - event = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEvent.json"), SQSEvent.class); - overrideSqsClient(sqsClient); - } - - @Test - void shouldBatchProcessAndNotDeleteMessagesWhenAllSuccess() { - List<String> returnValues = batchProcessor(event, false, (message) -> - { - interactionClient.listQueues(); - return "Success"; - }); - - assertThat(returnValues) - .hasSize(2) - .containsExactly("Success", "Success"); - - verify(interactionClient, times(2)).listQueues(); - verifyNoInteractions(sqsClient); - } - - @ParameterizedTest - @ValueSource(classes = {SampleInnerSqsHandler.class, SampleSqsHandler.class}) - void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess( - Class<? extends SqsMessageHandler<String>> handler) { - List<String> returnValues = batchProcessor(event, handler); - - assertThat(returnValues) - .hasSize(2) - .containsExactly("0", "1"); - - verifyNoInteractions(sqsClient); - } - - @Test - void shouldBatchProcessAndDeleteSuccessMessageOnPartialFailures() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - - SqsMessageHandler<String> failedHandler = (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }; - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains(failedId); - - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); - - verify(interactionClient).listQueues(); - - ArgumentCaptor<DeleteMessageBatchRequest> captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); - verify(sqsClient).deleteMessageBatch(captor.capture()); - - assertThat(captor.getValue()) - .hasFieldOrPropertyWithValue("queueUrl", "https://sqs.us-east-2.amazonaws.com/123456789012/my-queue"); - } - - @Test - void shouldBatchProcessAndFullFailuresInBatch() { - SqsMessageHandler<String> failedHandler = (message) -> - { - throw new RuntimeException(message.getMessageId()); - }; - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .isEmpty(); - - assertThat(e.getFailures()) - .hasSize(2) - .extracting("messageId") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getExceptions()) - .hasSize(2) - .extracting("detailMessage") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); - }); - - verifyNoInteractions(sqsClient); - } - - @Test - void shouldBatchProcessViaClassAndDeleteSuccessMessageOnPartialFailures() { - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(event, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); - - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - - @Test - void shouldBatchProcessAndSuppressExceptions() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - - SqsMessageHandler<String> failedHandler = (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }; - - List<String> returnValues = batchProcessor(event, true, failedHandler); - - assertThat(returnValues) - .hasSize(1) - .contains("Success"); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessViaClassAndSuppressExceptions() { - List<String> returnValues = batchProcessor(event, true, FailureSampleInnerSqsHandler.class); - - assertThat(returnValues) - .hasSize(1) - .contains("Success"); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - HashMap<QueueAttributeName, String> attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - List<String> batchProcessor = batchProcessor(event, (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }, IllegalStateException.class, IllegalArgumentException.class); - - assertThat(batchProcessor) - .hasSize(1); - - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndDeleteNonRetryableException() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - HashMap<QueueAttributeName, String> attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - List<String> batchProcessor = batchProcessor(event, false, (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }, true, IllegalStateException.class, IllegalArgumentException.class); - - assertThat(batchProcessor) - .hasSize(1); - - verify(sqsClient, times(0)).sendMessageBatch(any(SendMessageBatchRequest.class)); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldDeleteSuccessfulMessageInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = - MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(batch25Message, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .hasSize(24) - .contains("Success"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); - - ArgumentCaptor<DeleteMessageBatchRequest> captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); - - verify(sqsClient, times(3)).deleteMessageBatch(captor.capture()); - - assertThat(captor.getAllValues()) - .hasSize(3) - .flatMap(DeleteMessageBatchRequest::entries) - .hasSize(24); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = - MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); - - HashMap<QueueAttributeName, String> attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - List<String> batchProcessor = batchProcessor(batch25Message, (message) -> - { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - interactionClient.listQueues(); - return "Success"; - } - - throw new IllegalStateException("Failed processing"); - }, IllegalStateException.class, IllegalArgumentException.class); - - assertThat(batchProcessor) - .hasSize(1); - - ArgumentCaptor<SendMessageBatchRequest> captor = ArgumentCaptor.forClass(SendMessageBatchRequest.class); - - - verify(sqsClient, times(3)).sendMessageBatch(captor.capture()); - - assertThat(captor.getAllValues()) - .hasSize(3) - .flatMap(SendMessageBatchRequest::entries) - .hasSize(24); - } - - public class SampleInnerSqsHandler implements SqsMessageHandler<String> { - private int counter; - - @Override - public String process(SQSMessage message) { - interactionClient.listQueues(); - return String.valueOf(counter++); - } - } - - public class FailureSampleInnerSqsHandler implements SqsMessageHandler<String> { - @Override - public String process(SQSEvent.SQSMessage message) { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java deleted file mode 100644 index bfc555405..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.lambda.runtime.tests.EventLoader; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchResponse; - -public class SqsUtilsFifoBatchProcessorTest { - - private static SQSEvent sqsBatchEvent; - private MockitoSession session; - - @Mock - private SqsClient sqsClient; - - @Captor - private ArgumentCaptor<DeleteMessageBatchRequest> deleteMessageBatchCaptor; - - public SqsUtilsFifoBatchProcessorTest() throws IOException { - sqsBatchEvent = EventLoader.loadSQSEvent("SqsFifoBatchEvent.json"); - } - - @BeforeEach - public void setup() { - // Establish a strict mocking session. This means that any - // calls to a mock that have not been stubbed will throw - this.session = Mockito.mockitoSession() - .strictness(Strictness.STRICT_STUBS) - .initMocks(this) - .startMocking(); - - overrideSqsClient(sqsClient); - } - - @AfterEach - public void tearDown() { - session.finishMocking(); - } - - @Test - public void processWholeBatch() { - // Act - AtomicInteger processedCount = new AtomicInteger(); - List<Object> results = batchProcessor(sqsBatchEvent, false, (message) -> - { - processedCount.getAndIncrement(); - return true; - }); - - // Assert - assertThat(processedCount.get()).isEqualTo(3); - assertThat(results.size()).isEqualTo(3); - } - - /** - * Check that a failure in the middle of the batch: - * - deletes the successful message explicitly from SQS - * - marks the failed and subsequent message as failed - * - does not delete the failed or subsequent message - */ - @Test - public void singleFailureInMiddleOfBatch() { - // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())) - .thenReturn(DeleteMessageBatchResponse - .builder().build()); - - // Act - AtomicInteger processedCount = new AtomicInteger(); - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> - { - int value = processedCount.getAndIncrement(); - if (value == 1) { - throw new RuntimeException("Whoops"); - } - return true; - })) - - // Assert - .isInstanceOf(SQSBatchProcessingException.class) - .satisfies(e -> - { - List<SQSEvent.SQSMessage> failures = ((SQSBatchProcessingException) e).getFailures(); - assertThat(failures.size()).isEqualTo(2); - List<String> failureIds = failures.stream() - .map(SQSEvent.SQSMessage::getMessageId) - .collect(Collectors.toList()); - assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(1).getMessageId()); - assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(2).getMessageId()); - }); - - DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); - List<String> messageIds = deleteRequest.entries().stream() - .map(DeleteMessageBatchRequestEntry::id) - .collect(Collectors.toList()); - assertThat(deleteRequest.entries().size()).isEqualTo(1); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(0).getMessageId())).isTrue(); - - } - - @Test - public void singleFailureAtEndOfBatch() { - - // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())) - .thenReturn(DeleteMessageBatchResponse - .builder().build()); - - - // Act - AtomicInteger processedCount = new AtomicInteger(); - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> - { - int value = processedCount.getAndIncrement(); - if (value == 2) { - throw new RuntimeException("Whoops"); - } - return true; - })); - - // Assert - DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); - List<String> messageIds = deleteRequest.entries().stream() - .map(DeleteMessageBatchRequestEntry::id) - .collect(Collectors.toList()); - assertThat(deleteRequest.entries().size()).isEqualTo(2); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(0).getMessageId())).isTrue(); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(1).getMessageId())).isTrue(); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(2).getMessageId())).isFalse(); - } - - @Test - public void messageFailureStopsGroupProcessing() { - String groupToFail = sqsBatchEvent.getRecords().get(0).getAttributes().get("MessageGroupId"); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, (message) -> - { - String groupId = message.getAttributes().get("MessageGroupId"); - if (groupId.equals(groupToFail)) { - throw new RuntimeException("Failed processing"); - } - return groupId; - })) - .satisfies(e -> - { - assertThat(e.successMessageReturnValues().size()).isEqualTo(0); - assertThat(e.successMessageReturnValues().contains(groupToFail)).isFalse(); - }); - } - -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java deleted file mode 100644 index afc426976..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import java.util.stream.Stream; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.http.AbortableInputStream; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.S3Exception; -import software.amazon.awssdk.utils.StringInputStream; -import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; - -class SqsUtilsLargeMessageTest { - - private static final String BUCKET_NAME = "ms-extended-sqs-client"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - @Mock - private S3Client s3Client; - - private static Stream<Arguments> exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); - } - - @BeforeEach - void setUp() { - openMocks(this); - SqsUtils.overrideS3Client(s3Client); - } - - @Test - public void testLargeMessage() { - ResponseInputStream<GetObjectResponse> s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - Map<String, String> sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> - { - Map<String, String> someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - assertThat(sqsMessage) - .hasSize(1) - .containsEntry("Message", "A big message"); - - ArgumentCaptor<DeleteObjectRequest> delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); - - verify(s3Client).deleteObject(delete.capture()); - - Assertions.assertThat(delete.getValue()) - .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); - } - - @ParameterizedTest - @ValueSource(booleans = {true, false}) - public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { - ResponseInputStream<GetObjectResponse> s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - Map<String, String> sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, deleteS3Payload, sqsMessages -> - { - Map<String, String> someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - assertThat(sqsMessage) - .hasSize(1) - .containsEntry("Message", "A big message"); - if (deleteS3Payload) { - ArgumentCaptor<DeleteObjectRequest> delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); - - verify(s3Client).deleteObject(delete.capture()); - - Assertions.assertThat(delete.getValue()) - .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); - } else { - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - } - - @Test - public void shouldNotProcessSmallMessageBody() { - ResponseInputStream<GetObjectResponse> s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - SQSEvent sqsEvent = messageWithBody("This is small message"); - - Map<String, String> sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> - { - Map<String, String> someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - assertThat(sqsMessage) - .containsEntry("Message", "This is small message"); - - verifyNoInteractions(s3Client); - } - - @ParameterizedTest - @MethodSource("exception") - public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exception) { - when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(exception); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) - .isThrownBy(() -> SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> sqsMessages.get(0).getBody())) - .withCause(exception); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void shouldFailEntireBatchIfFailedProcessingDownloadMessageFromS3() { - ResponseInputStream<GetObjectResponse> s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new StringInputStream("test") { - @Override - public void close() throws IOException { - throw new IOException("Failed"); - } - })); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) - .isThrownBy(() -> SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> sqsMessages.get(0).getBody())) - .withCauseInstanceOf(IOException.class); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - private SQSEvent messageWithBody(String messageBody) { - SQSMessage sqsMessage = new SQSMessage(); - sqsMessage.setBody(messageBody); - SQSEvent sqsEvent = new SQSEvent(); - sqsEvent.setRecords(singletonList(sqsMessage)); - return sqsEvent; - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java deleted file mode 100644 index 172179057..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class PartialBatchFailureSuppressedHandler implements RequestHandler<SQSEvent, String> { - @Override - @SqsBatch(value = InnerMessageHandler.class, suppressException = true) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - throw new RuntimeException("2e1424d4-f796-459a-8184-9c92662be6da"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java deleted file mode 100644 index 6e3971269..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class PartialBatchPartialFailureHandler implements RequestHandler<SQSEvent, String> { - @Override - @SqsBatch(InnerMessageHandler.class) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - throw new RuntimeException("2e1424d4-f796-459a-8184-9c92662be6da"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java deleted file mode 100644 index acfcd7109..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class PartialBatchSuccessHandler implements RequestHandler<SQSEvent, String> { - @Override - @SqsBatch(InnerMessageHandler.class) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java deleted file mode 100644 index 74ff02e2c..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class SqsMessageHandlerWithNonRetryableHandler implements RequestHandler<SQSEvent, String> { - - @Override - @SqsBatch(value = InnerMessageHandler.class, nonRetryableExceptions = {IllegalStateException.class, - IllegalArgumentException.class}) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - if (message.getMessageId().isEmpty()) { - throw new IllegalArgumentException("Invalid message and was moved to DLQ"); - } - - if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { - throw new RuntimeException("Invalid message and should be reprocessed"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java deleted file mode 100644 index 5b341880e..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class SqsMessageHandlerWithNonRetryableHandlerWithDelete implements RequestHandler<SQSEvent, String> { - - @Override - @SqsBatch(value = InnerMessageHandler.class, - nonRetryableExceptions = {IllegalStateException.class, IllegalArgumentException.class}, - deleteNonRetryableMessageFromQueue = true) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - if (message.getMessageId().isEmpty()) { - throw new IllegalArgumentException("Invalid message and was moved to DLQ"); - } - - if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { - throw new RuntimeException("Invalid message and should be reprocessed"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java deleted file mode 100644 index 535da11c7..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.FailedProcessingLargePayloadException; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.function.Consumer; -import java.util.stream.Stream; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.http.AbortableInputStream; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.S3Exception; -import software.amazon.awssdk.utils.StringInputStream; -import software.amazon.lambda.powertools.sqs.SqsUtils; -import software.amazon.lambda.powertools.sqs.handlers.LambdaHandlerApiGateway; -import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandler; -import software.amazon.lambda.powertools.sqs.handlers.SqsNoDeleteMessageHandler; - -public class SqsLargeMessageAspectTest { - - private static final String BUCKET_NAME = "bucketname"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - private RequestHandler<SQSEvent, String> requestHandler; - @Mock - private Context context; - @Mock - private S3Client s3Client; - - private static Stream<Arguments> exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); - } - - @BeforeEach - void setUp() { - openMocks(this); - setupContext(); - SqsUtils.overrideS3Client(s3Client); - requestHandler = new SqsMessageHandler(); - } - - @Test - public void testLargeMessage() { - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - String response = requestHandler.handleRequest(sqsEvent, context); - - assertThat(response) - .isEqualTo("A big message"); - - ArgumentCaptor<DeleteObjectRequest> delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); - - verify(s3Client).deleteObject(delete.capture()); - - Assertions.assertThat(delete.getValue()) - .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); - } - - @Test - public void shouldNotProcessSmallMessageBody() { - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - - SQSEvent sqsEvent = messageWithBody("This is small message"); - - String response = requestHandler.handleRequest(sqsEvent, context); - - assertThat(response) - .isEqualTo("This is small message"); - - verifyNoInteractions(s3Client); - } - - @ParameterizedTest - @MethodSource("exception") - public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exception) { - when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(exception); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(FailedProcessingLargePayloadException.class) - .isThrownBy(() -> requestHandler.handleRequest(sqsEvent, context)) - .withCause(exception); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void testLargeMessageWithDeletionOff() { - requestHandler = new SqsNoDeleteMessageHandler(); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - String response = requestHandler.handleRequest(sqsEvent, context); - - assertThat(response).isEqualTo("A big message"); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void shouldFailEntireBatchIfFailedProcessingDownloadMessageFromS3() { - ResponseInputStream<GetObjectResponse> s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new StringInputStream("test") { - @Override - public void close() throws IOException { - throw new IOException("Failed"); - } - })); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(FailedProcessingLargePayloadException.class) - .isThrownBy(() -> requestHandler.handleRequest(sqsEvent, context)) - .withCauseInstanceOf(IOException.class); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void shouldNotDoAnyProcessingWhenNotSqsEvent() { - LambdaHandlerApiGateway handler = new LambdaHandlerApiGateway(); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - - APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); - event.setBody(messageBody); - String response = handler.handleRequest(event, context); - - assertThat(response) - .isEqualTo(messageBody); - - verifyNoInteractions(s3Client); - } - - private ResponseInputStream<GetObjectResponse> s3ObjectWithLargeMessage() { - return new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - } - - private SQSEvent messageWithBody(String messageBody) { - SQSMessage sqsMessage = new SQSMessage(); - sqsMessage.setBody(messageBody); - SQSEvent sqsEvent = new SQSEvent(); - sqsEvent.setRecords(singletonList(sqsMessage)); - return sqsEvent; - } - - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java deleted file mode 100644 index c0211cb83..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.util.HashMap; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.BatchResultErrorEntry; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import software.amazon.lambda.powertools.sqs.SQSBatchProcessingException; -import software.amazon.lambda.powertools.sqs.handlers.LambdaHandlerApiGateway; -import software.amazon.lambda.powertools.sqs.handlers.PartialBatchFailureSuppressedHandler; -import software.amazon.lambda.powertools.sqs.handlers.PartialBatchPartialFailureHandler; -import software.amazon.lambda.powertools.sqs.handlers.PartialBatchSuccessHandler; -import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandler; -import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandlerWithDelete; - -public class SqsMessageBatchProcessorAspectTest { - public static final SqsClient interactionClient = mock(SqsClient.class); - private static final SqsClient sqsClient = mock(SqsClient.class); - private static final ObjectMapper MAPPER = new ObjectMapper(); - private final Context context = mock(Context.class); - private SQSEvent event; - private RequestHandler<SQSEvent, String> requestHandler; - - @BeforeEach - void setUp() throws IOException { - overrideSqsClient(sqsClient); - reset(interactionClient); - reset(sqsClient); - setupContext(); - event = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEvent.json"), SQSEvent.class); - requestHandler = new PartialBatchSuccessHandler(); - } - - @Test - void shouldBatchProcessAllMessageSuccessfullyAndNotDeleteFromSQS() { - requestHandler.handleRequest(event, context); - - verify(interactionClient, times(2)).listQueues(); - verify(sqsClient, times(0)).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessMessageWithSuccessDeletedOnFailureInBatchFromSQS() { - requestHandler = new PartialBatchPartialFailureHandler(); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessMessageWithSuccessDeletedOnFailureWithSuppressionInBatchFromSQS() { - requestHandler = new PartialBatchFailureSuppressedHandler(); - - requestHandler.handleRequest(event, context); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldNotTakeEffectOnNonSqsEventHandler() { - LambdaHandlerApiGateway handlerApiGateway = new LambdaHandlerApiGateway(); - - handlerApiGateway.handleRequest(mock(APIGatewayProxyRequestEvent.class), context); - - verifyNoInteractions(sqsClient); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event.getRecords().get(0).setMessageId(""); - - HashMap<QueueAttributeName, String> attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - requestHandler.handleRequest(event, context); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndThrowExceptionForNonRetryableExceptionWhenMoveToDlqReturnFailedResponse() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event.getRecords().get(0).setMessageId(""); - - when(sqsClient.sendMessageBatch(any(SendMessageBatchRequest.class))).thenReturn( - SendMessageBatchResponse.builder() - .failed(BatchResultErrorEntry.builder() - .message("Permission Error") - .code("KMS.AccessDeniedException") - .senderFault(true) - .build()) - .build()); - - HashMap<QueueAttributeName, String> attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - Assertions.assertThatExceptionOfType(SQSBatchProcessingException.class). - isThrownBy(() -> requestHandler.handleRequest(event, context)); - - verify(interactionClient).listQueues(); - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndDeleteNonRetryableExceptionMessage() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandlerWithDelete(); - event.getRecords().get(0).setMessageId(""); - - requestHandler.handleRequest(event, context); - - verify(interactionClient).listQueues(); - ArgumentCaptor<DeleteMessageBatchRequest> captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); - verify(sqsClient).deleteMessageBatch(captor.capture()); - verify(sqsClient, never()).sendMessageBatch(any(SendMessageBatchRequest.class)); - verify(sqsClient, never()).getQueueAttributes(any(GetQueueAttributesRequest.class)); - - assertThat(captor.getValue()) - .satisfies(deleteMessageBatchRequest -> assertThat(deleteMessageBatchRequest.entries()) - .hasSize(2) - .extracting("id") - .contains("", "2e1424d4-f796-459a-8184-9c92662be6da")); - } - - @Test - void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionAndNoDlq() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - - event.getRecords().get(0).setMessageId(""); - event.getRecords() - .forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp")); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .build()); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and was moved to DLQ"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly(""); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient, never()).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionWhenFailedParsingPolicy() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event.getRecords().get(0).setMessageId(""); - event.getRecords() - .forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp-queue")); - - HashMap<QueueAttributeName, String> attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "MalFormedRedrivePolicy"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and was moved to DLQ"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly(""); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient, never()).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlqAndThrowException() throws IOException { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event = MAPPER.readValue(this.getClass().getResource("/threeMessageSqsBatchEvent.json"), SQSEvent.class); - - event.getRecords().get(1).setMessageId(""); - - HashMap<QueueAttributeName, String> attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and should be reprocessed"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly("2e1424d4-f796-459a-9696-9c92662ba5da"); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json b/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json deleted file mode 100644 index 726e45ea1..000000000 --- a/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "Records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185", - "MessageGroupId": "Group1" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649", - "MessageGroupId": "Group2" - }, - "messageAttributes": { - "Attribute3" : { - "binaryValue" : "MTEwMA==", - "dataType" : "Binary" - }, - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - }, - "Attribute1" : { - "stringValue" : "AttributeValue1", - "dataType" : "String" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-9696-9c92662ba5da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649", - "MessageGroupId": "Group1" - }, - "messageAttributes": { - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/sampleSqsBatchEvent.json b/powertools-sqs/src/test/resources/sampleSqsBatchEvent.json deleted file mode 100644 index 8a5fbf309..000000000 --- a/powertools-sqs/src/test/resources/sampleSqsBatchEvent.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/sampleSqsBatchEventBatchSize25.json b/powertools-sqs/src/test/resources/sampleSqsBatchEventBatchSize25.json deleted file mode 100644 index 31b8862ad..000000000 --- a/powertools-sqs/src/test/resources/sampleSqsBatchEventBatchSize25.json +++ /dev/null @@ -1,404 +0,0 @@ -{ - "records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d3", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d4", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b5", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d6", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b7", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d8", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d9", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d10", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d11", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d12", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d13", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d14", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d15", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d16", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d17", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d18", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d19", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d20", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d21", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d22", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d23", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d24", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d25", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d26", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d27", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/threeMessageSqsBatchEvent.json b/powertools-sqs/src/test/resources/threeMessageSqsBatchEvent.json deleted file mode 100644 index b3b61da3b..000000000 --- a/powertools-sqs/src/test/resources/threeMessageSqsBatchEvent.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": { - "Attribute3" : { - "binaryValue" : "MTEwMA==", - "dataType" : "Binary" - }, - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - }, - "Attribute1" : { - "stringValue" : "AttributeValue1", - "dataType" : "String" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-9696-9c92662ba5da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": { - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml deleted file mode 100644 index 4a79d2987..000000000 --- a/powertools-test-suite/pom.xml +++ /dev/null @@ -1,164 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ Copyright 2023 Amazon.com, Inc. or its affiliates. - ~ Licensed under the Apache License, Version 2.0 (the - ~ "License"); you may not use this file except in compliance - ~ with the License. You may obtain a copy of the License at - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - ~ - --> - -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <artifactId>powertools-test-suite</artifactId> - <packaging>jar</packaging> - - <parent> - <artifactId>powertools-parent</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> - </parent> - - <name>Powertools for AWS Lambda (Java) library Test Suite</name> - <description> - A suite of tests for interactions between the various Powertools for AWS Lambda (Java) modules. - </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <properties> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> - - <dependencies> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-jcl</artifactId> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </dependency> - - <!-- Test dependencies --> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjweaver</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.skyscreamer</groupId> - <artifactId>jsonassert</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - <configuration> - <source>${maven.compiler.source}</source> - <target>${maven.compiler.target}</target> - <complianceLevel>${maven.compiler.target}</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> -</project> \ No newline at end of file diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java deleted file mode 100644 index 9e9c464a6..000000000 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.testsuite; - - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; -import com.amazonaws.xray.AWSXRay; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Map; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.ThreadContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.http.AbortableInputStream; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; -import software.amazon.lambda.powertools.sqs.SqsUtils; -import software.amazon.lambda.powertools.testsuite.handler.LoggingOrderMessageHandler; -import software.amazon.lambda.powertools.testsuite.handler.TracingLoggingStreamMessageHandler; - -public class LoggingOrderTest { - - private static final String BUCKET_NAME = "ms-extended-sqs-client"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - - @Mock - private Context context; - - @Mock - private S3Client s3Client; - - @BeforeEach - void setUp() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { - openMocks(this); - SqsUtils.overrideS3Client(s3Client); - ThreadContext.clearAll(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); - //Make sure file is cleaned up before running full stack logging regression - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); - resetLogLevel(Level.INFO); - AWSXRay.beginSegment(LoggingOrderTest.class.getName()); - } - - @AfterEach - void tearDown() { - AWSXRay.endSegment(); - } - - /** - * The SQSEvent payload will be altered by the @SqsLargeMessage annotation. Logging of the event should happen - * after the event has been altered - */ - @Test - public void testThatLoggingAnnotationActsLast() throws IOException { - ResponseInputStream<GetObjectResponse> s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - LoggingOrderMessageHandler requestHandler = new LoggingOrderMessageHandler(); - requestHandler.handleRequest(sqsEvent, context); - - assertThat(Files.lines(Paths.get("target/logfile.json"))) - .hasSize(2) - .satisfies(line -> - { - Map<String, Object> actual = parseToMap(line.get(0)); - - String message = actual.get("message").toString(); - - assertThat(message) - .contains("A big message"); - }); - } - - @Test - public void testLoggingAnnotationActsAfterTracingForStreamingHandler() throws IOException { - - ByteArrayOutputStream output = new ByteArrayOutputStream(); - S3EventNotification s3EventNotification = s3EventNotification(); - - TracingLoggingStreamMessageHandler handler = new TracingLoggingStreamMessageHandler(); - handler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), - output, context); - - assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) - .isNotEmpty(); - } - - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - when(context.getAwsRequestId()).thenReturn("RequestId"); - } - - private void resetLogLevel(Level level) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); - resetLogLevels.setAccessible(true); - resetLogLevels.invoke(null, level); - writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); - } - - private Map<String, Object> parseToMap(String stringAsJson) { - try { - return new ObjectMapper().readValue(stringAsJson, Map.class); - } catch (JsonProcessingException e) { - fail("Failed parsing logger line " + stringAsJson); - return emptyMap(); - } - } - - private S3EventNotification s3EventNotification() { - S3EventNotification.S3EventNotificationRecord record = - new S3EventNotification.S3EventNotificationRecord("us-west-2", - "ObjectCreated:Put", - "aws:s3", - null, - "2.1", - new S3EventNotification.RequestParametersEntity("127.0.0.1"), - new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", - "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), - new S3EventNotification.S3Entity("testConfigRule", - new S3EventNotification.S3BucketEntity("mybucket", - new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), - "arn:aws:s3:::mybucket"), - new S3EventNotification.S3ObjectEntity("HappyFace.jpg", - 1024L, - "d41d8cd98f00b204e9800998ecf8427e", - "096fKKXTRTtl3on89fVO.nfljtsv6qko", - "0055AED6DCD90281E5"), - "1.0"), - new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") - ); - - return new S3EventNotification(singletonList(record)); - } - - private SQSEvent messageWithBody(String messageBody) { - SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage(); - sqsMessage.setBody(messageBody); - SQSEvent sqsEvent = new SQSEvent(); - sqsEvent.setRecords(singletonList(sqsMessage)); - return sqsEvent; - } -} \ No newline at end of file diff --git a/powertools-test-suite/src/test/resources/log4j2.xml b/powertools-test-suite/src/test/resources/log4j2.xml deleted file mode 100644 index 8ac9b34ce..000000000 --- a/powertools-test-suite/src/test/resources/log4j2.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> - <Appenders> - <File name="JsonAppender" fileName="target/logfile.json"> - <LambdaJsonLayout compact="true" eventEol="true"/> - </File> - </Appenders> - - <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> - <AppenderRef ref="JsonAppender"/> - </Logger> - <Logger name="com.amazonaws.auth.profile.internal" level="ERROR" additivity="false"> - <Appender-ref ref="JsonAppender"/> - </Logger> - <Root level="info"> - <AppenderRef ref="JsonAppender"/> - </Root> - </Loggers> -</Configuration> \ No newline at end of file diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index b5de90f7b..545633c50 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -14,8 +14,8 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>powertools-tracing</artifactId> @@ -24,53 +24,35 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> - <name>Powertools for AWS Lambda (Java) library Tracing</name> + <name>Powertools for AWS Lambda (Java) - Tracing</name> <description> A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>aws-core</artifactId> </dependency> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> </dependency> <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -88,6 +70,14 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-aws-sdk-v2-instrumentor</artifactId> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + </dependency> <!-- Test dependencies --> <dependency> @@ -101,18 +91,25 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> <scope>test</scope> </dependency> <dependency> @@ -125,15 +122,81 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_LAMBDA_INITIALIZATION_TYPE>on-demand</AWS_LAMBDA_INITIALIZATION_TYPE> + </environmentVariables> + </configuration> </plugin> </plugins> </build> -</project> \ No newline at end of file + <profiles> + <profile> + <id>generate-graalvm-files</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Dorg.graalvm.nativeimage.imagecode=agent + -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing,experimental-class-define-support + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.11.2</version> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>powertools-tracing</imageName> + <buildArgs> + <buildArg>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--enable-url-protocols=http</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java index 6f17a2e33..432e475a3 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java @@ -50,22 +50,6 @@ public @interface Tracing { String namespace() default ""; - /** - * @deprecated As of release 1.2.0, replaced by captureMode() - * in order to support different modes and support via - * environment variables - */ - @Deprecated - boolean captureResponse() default true; - - /** - * @deprecated As of release 1.2.0, replaced by captureMode() - * in order to support different modes and support via - * environment variables - */ - @Deprecated - boolean captureError() default true; - String segmentName() default ""; CaptureMode captureMode() default CaptureMode.ENVIRONMENT_VAR; diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java index 9fb021548..954ed7da4 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java @@ -14,19 +14,22 @@ package software.amazon.lambda.powertools.tracing; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.serviceName; import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Entity; import com.amazonaws.xray.entities.Subsegment; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A class of helper functions to add additional functionality and ease * of use. */ public final class TracingUtils { + private static final Logger LOG = LoggerFactory.getLogger(TracingUtils.class); private static ObjectMapper objectMapper; /** @@ -36,6 +39,10 @@ public final class TracingUtils { * @param value the value of the annotation */ public static void putAnnotation(String key, String value) { + if (!isValidAnnotationKey(key)) { + LOG.warn("Ignoring annotation with unsupported characters in key: {}", key); + return; + } AWSXRay.getCurrentSubsegmentOptional() .ifPresent(segment -> segment.putAnnotation(key, value)); } @@ -47,10 +54,24 @@ public static void putAnnotation(String key, String value) { * @param value the value of the annotation */ public static void putAnnotation(String key, Number value) { + if (!isValidAnnotationKey(key)) { + LOG.warn("Ignoring annotation with unsupported characters in key: {}", key); + return; + } AWSXRay.getCurrentSubsegmentOptional() .ifPresent(segment -> segment.putAnnotation(key, value)); } + /** + Make sure that the annotation key is valid according to + <a href='https://docs.aws.amazon.com/xray/latest/devguide/xray-api-segmentdocuments.html#api-segmentdocuments-annotations'>the documentation</a>. + + Annotation keys that are added that are invalid are ignored by x-ray. + **/ + private static boolean isValidAnnotationKey(String key) { + return key.matches("^[a-zA-Z0-9_]+$"); + } + /** * Put an annotation to the current subsegment with a Boolean value. * diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java index 62416fce6..a4a48532c 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java @@ -14,11 +14,11 @@ package software.amazon.lambda.powertools.tracing.internal; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isSamLocal; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isColdStart; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isSamLocal; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.serviceName; import static software.amazon.lambda.powertools.tracing.TracingUtils.objectMapper; import com.amazonaws.xray.AWSXRay; @@ -83,9 +83,8 @@ public Object around(ProceedingJoinPoint pjp, private boolean captureResponse(Tracing powerToolsTracing) { switch (powerToolsTracing.captureMode()) { case ENVIRONMENT_VAR: - boolean captureResponse = environmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE"); - return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_RESPONSE") ? captureResponse : - powerToolsTracing.captureResponse(); + return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_RESPONSE") + && environmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE"); case RESPONSE: case RESPONSE_AND_ERROR: return true; @@ -98,9 +97,8 @@ private boolean captureResponse(Tracing powerToolsTracing) { private boolean captureError(Tracing powerToolsTracing) { switch (powerToolsTracing.captureMode()) { case ENVIRONMENT_VAR: - boolean captureError = environmentVariable("POWERTOOLS_TRACER_CAPTURE_ERROR"); - return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_ERROR") ? captureError : - powerToolsTracing.captureError(); + return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_ERROR") + && environmentVariable("POWERTOOLS_TRACER_CAPTURE_ERROR"); case ERROR: case RESPONSE_AND_ERROR: return true; diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptor.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptor.java new file mode 100644 index 000000000..489243517 --- /dev/null +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.tracing.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-tracing module is on the classpath. + */ +public final class TracingUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("tracing"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/jni-config.json b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/jni-config.json new file mode 100644 index 000000000..2c4de0562 --- /dev/null +++ b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/jni-config.json @@ -0,0 +1,26 @@ +[ +{ + "name":"java.lang.Boolean", + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.String", + "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"org.apache.maven.surefire.booter.ForkedBooter", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"sun.instrument.InstrumentationImpl", + "methods":[{"name":"<init>","parameterTypes":["long","boolean","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }] +}, +{ + "name":"sun.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] diff --git a/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/reflect-config.json b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/reflect-config.json new file mode 100644 index 000000000..97e0a7a86 --- /dev/null +++ b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/reflect-config.json @@ -0,0 +1,369 @@ +[ +{ + "name":"[Lcom.fasterxml.jackson.databind.deser.Deserializers;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.ser.BeanSerializerModifier;" +}, +{ + "name":"[Lcom.fasterxml.jackson.databind.ser.Serializers;" +}, +{ + "name":"[Ljava.lang.StackTraceElement;" +}, +{ + "name":"[Ljava.lang.Throwable;" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.Context", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getAwsRequestId","parameterTypes":[] }, {"name":"getClientContext","parameterTypes":[] }, {"name":"getFunctionName","parameterTypes":[] }, {"name":"getFunctionVersion","parameterTypes":[] }, {"name":"getIdentity","parameterTypes":[] }, {"name":"getInvokedFunctionArn","parameterTypes":[] }, {"name":"getLogGroupName","parameterTypes":[] }, {"name":"getLogStreamName","parameterTypes":[] }, {"name":"getLogger","parameterTypes":[] }, {"name":"getMemoryLimitInMB","parameterTypes":[] }, {"name":"getRemainingTimeInMillis","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.entities.Cause", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.xray.entities.Entity", + "queryAllDeclaredMethods":true +}, +{ + "name":"com.amazonaws.xray.entities.EntityImpl", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"getAnnotations","parameterTypes":[] }, {"name":"getAws","parameterTypes":[] }, {"name":"getCause","parameterTypes":[] }, {"name":"getEndTime","parameterTypes":[] }, {"name":"getHttp","parameterTypes":[] }, {"name":"getId","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getNamespace","parameterTypes":[] }, {"name":"getParentId","parameterTypes":[] }, {"name":"getSql","parameterTypes":[] }, {"name":"getStartTime","parameterTypes":[] }, {"name":"getSubsegments","parameterTypes":[] }, {"name":"getTraceId","parameterTypes":[] }, {"name":"isError","parameterTypes":[] }, {"name":"isFault","parameterTypes":[] }, {"name":"isInProgress","parameterTypes":[] }, {"name":"isThrottle","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.entities.Segment", + "queryAllDeclaredMethods":true +}, +{ + "name":"com.amazonaws.xray.entities.SegmentImpl", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getOrigin","parameterTypes":[] }, {"name":"getResourceArn","parameterTypes":[] }, {"name":"getService","parameterTypes":[] }, {"name":"getUser","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.entities.Subsegment", + "queryAllDeclaredMethods":true +}, +{ + "name":"com.amazonaws.xray.entities.SubsegmentImpl", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getNamespace","parameterTypes":[] }, {"name":"getPrecursorIds","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.strategy.sampling.manifest.SamplingRuleManifest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setDefaultRule","parameterTypes":["com.amazonaws.xray.strategy.sampling.rule.SamplingRule"] }, {"name":"setRules","parameterTypes":["java.util.List"] }, {"name":"setVersion","parameterTypes":["int"] }] +}, +{ + "name":"com.amazonaws.xray.strategy.sampling.reservoir.Reservoir", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"com.amazonaws.xray.strategy.sampling.rule.SamplingRule", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setFixedTarget","parameterTypes":["int"] }, {"name":"setRate","parameterTypes":["float"] }] +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.fasterxml.jackson.databind.ser.std.ToStringSerializer", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"double", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.io.InputStream" +}, +{ + "name":"java.io.OutputStream" +}, +{ + "name":"java.io.Serializable", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "fields":[{"name":"handleRequest response"}] +}, +{ + "name":"java.lang.AutoCloseable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String"] }, {"name":"getAnnotatedInterfaces","parameterTypes":[] }, {"name":"getAnnotatedSuperclass","parameterTypes":[] }, {"name":"getDeclaredMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getMethod","parameterTypes":["java.lang.String","java.lang.Class[]"] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNestHost","parameterTypes":[] }, {"name":"getNestMembers","parameterTypes":[] }, {"name":"getPermittedSubclasses","parameterTypes":[] }, {"name":"getRecordComponents","parameterTypes":[] }, {"name":"isNestmateOf","parameterTypes":["java.lang.Class"] }, {"name":"isRecord","parameterTypes":[] }, {"name":"isSealed","parameterTypes":[] }] +}, +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getDefinedPackage","parameterTypes":["java.lang.String"] }, {"name":"getUnnamedModule","parameterTypes":[] }, {"name":"registerAsParallelCapable","parameterTypes":[] }] +}, +{ + "name":"java.lang.Exception", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Module", + "methods":[{"name":"addExports","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"addReads","parameterTypes":["java.lang.Module"] }, {"name":"canRead","parameterTypes":["java.lang.Module"] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPackages","parameterTypes":[] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String"] }, {"name":"isExported","parameterTypes":["java.lang.String","java.lang.Module"] }, {"name":"isNamed","parameterTypes":[] }, {"name":"isOpen","parameterTypes":["java.lang.String","java.lang.Module"] }] +}, +{ + "name":"java.lang.Object", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "fields":[{"name":"handleRequest response"}], + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"getHandleRequest response","parameterTypes":[] }, {"name":"isHandleRequest response","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] +}, +{ + "name":"java.lang.ProcessEnvironment", + "fields":[{"name":"theCaseInsensitiveEnvironment"}, {"name":"theEnvironment"}] +}, +{ + "name":"java.lang.ProcessHandle", + "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime", + "methods":[{"name":"version","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runtime$Version", + "methods":[{"name":"feature","parameterTypes":[] }] +}, +{ + "name":"java.lang.RuntimeException", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.StackTraceElement", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.StackWalker" +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.Thread", + "fields":[{"name":"threadLocalRandomProbe"}], + "methods":[{"name":"getContextClassLoader","parameterTypes":[] }] +}, +{ + "name":"java.lang.Throwable", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"getCause","parameterTypes":[] }, {"name":"getLocalizedMessage","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] }, {"name":"getStackTrace","parameterTypes":[] }, {"name":"getSuppressed","parameterTypes":[] }] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.invoke.MethodHandle", + "methods":[{"name":"bindTo","parameterTypes":["java.lang.Object"] }, {"name":"invokeWithArguments","parameterTypes":["java.lang.Object[]"] }] +}, +{ + "name":"java.lang.invoke.MethodHandles", + "methods":[{"name":"lookup","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandles$Lookup", + "methods":[{"name":"findVirtual","parameterTypes":["java.lang.Class","java.lang.String","java.lang.invoke.MethodType"] }] +}, +{ + "name":"java.lang.invoke.MethodType", + "methods":[{"name":"methodType","parameterTypes":["java.lang.Class","java.lang.Class[]"] }] +}, +{ + "name":"java.lang.reflect.AccessibleObject", + "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] +}, +{ + "name":"java.lang.reflect.AnnotatedArrayType", + "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.AnnotatedType", + "methods":[{"name":"getType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Executable", + "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] +}, +{ + "name":"java.lang.reflect.Parameter", + "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.AbstractMap", + "fields":[{"name":"handleRequest response"}], + "methods":[{"name":"getHandleRequest response","parameterTypes":[] }, {"name":"isHandleRequest response","parameterTypes":[] }] +}, +{ + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] +}, +{ + "name":"java.util.Map", + "fields":[{"name":"handleRequest response"}] +}, +{ + "name":"java.util.concurrent.ConcurrentHashMap", + "fields":[{"name":"handleRequest response"}], + "methods":[{"name":"getHandleRequest response","parameterTypes":[] }, {"name":"isHandleRequest response","parameterTypes":[] }] +}, +{ + "name":"java.util.concurrent.ConcurrentMap", + "fields":[{"name":"handleRequest response"}] +}, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.Striped64", + "fields":[{"name":"base"}, {"name":"cellsBusy"}] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, + +{ + "name":"org.apache.commons.logging.LogFactory" +}, +{ + "name":"org.apache.commons.logging.impl.Jdk14Logger", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }, {"name":"setLogFactory","parameterTypes":["org.apache.commons.logging.LogFactory"] }] +}, +{ + "name":"org.apache.commons.logging.impl.Log4JLogger" +}, +{ + "name":"org.apache.commons.logging.impl.LogFactoryImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"org.apache.commons.logging.impl.WeakHashtable", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"isColdStart"}] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabled", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledExplicitlyForResponseAndError", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForError", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForResponse", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForResponseWithCustomMapper", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForResponseWithCustomMapper$ParentClass", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForStream", + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForStreamWithNoMetaData", + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithException", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithNoMetaData", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, + +{ + "name":"software.amazon.lambda.powertools.tracing.nonhandler.PowerToolNonHandler", + "methods":[{"name":"doSomething","parameterTypes":[] }, {"name":"doSomethingCustomName","parameterTypes":[] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.tracing.internal.TracingUserAgentInterceptor", + "methods":[{"name":"<init>","parameterTypes":[] }] +} +] diff --git a/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/resource-config.json b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/resource-config.json new file mode 100644 index 000000000..64408b8b6 --- /dev/null +++ b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/resource-config.json @@ -0,0 +1,33 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/com.fasterxml.jackson.databind.Module\\E" + }, { + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.commons.logging.LogFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.configuration.Configuration\\E" + }, { + "pattern":"\\QMETA-INF/services/org.assertj.core.presentation.Representation\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qcom/amazonaws/xray/sdk.properties\\E" + }, { + "pattern":"\\Qcom/amazonaws/xray/strategy/sampling/DefaultSamplingRules.json\\E" + }, { + "pattern":"\\Qcommons-logging.properties\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, { + "pattern":"\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" + }]}, + "bundles":[] +} diff --git a/powertools-tracing/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-tracing/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..5f15a8000 --- /dev/null +++ b/powertools-tracing/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.tracing.internal.TracingUserAgentInterceptor diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java index 78283fbc2..db9807dbd 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java @@ -16,19 +16,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.xray.AWSXRay; -import com.amazonaws.xray.entities.Entity; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class TracingUtilsTest { +import com.amazonaws.xray.AWSXRay; +import com.amazonaws.xray.entities.Entity; +class TracingUtilsTest { @BeforeEach void setUp() { AWSXRay.beginSegment("test"); @@ -56,8 +53,7 @@ void shouldSetAnnotationOnCurrentSubSegment() { .contains( entry("stringKey", "val"), entry("numberKey", 10), - entry("booleanKey", false) - ); + entry("booleanKey", false)); } @Test @@ -77,10 +73,8 @@ void shouldSetMetadataOnCurrentSubSegment() { assertThat(AWSXRay.getTraceEntity().getMetadata()) .hasSize(1) .containsKey("service_undefined") - .satisfies(map -> - assertThat(map.get("service_undefined")) - .containsEntry("key", "val") - ); + .satisfies(map -> assertThat(map.get("service_undefined")) + .containsEntry("key", "val")); } @Test @@ -93,21 +87,14 @@ void shouldNotSetMetaDataIfNoCurrentSubSegment() { @Test void shouldInvokeCodeBlockWrappedWithinSubsegment() { - Context test = mock(Context.class); - - TracingUtils.withSubsegment("testSubSegment", subsegment -> - { + TracingUtils.withSubsegment("testSubSegment", subsegment -> { subsegment.putAnnotation("key", "val"); subsegment.putMetadata("key", "val"); - test.getFunctionName(); }); - verify(test).getFunctionName(); - assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getName()) .isEqualTo("## testSubSegment"); @@ -124,22 +111,33 @@ void shouldInvokeCodeBlockWrappedWithinSubsegment() { } @Test - void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { - Context test = mock(Context.class); + void shouldNotAddAnnotationIfInvalidCharacterInKey() { + AWSXRay.beginSubsegment("subSegment"); + String inputKey = "stringKey with spaces"; + TracingUtils.putAnnotation(inputKey, "val"); + AWSXRay.getCurrentSubsegmentOptional() + .ifPresent(segment -> assertThat(segment.getAnnotations()).size().isEqualTo(0)); + } - TracingUtils.withSubsegment("testNamespace", "testSubSegment", subsegment -> - { + @Test + void shouldAddAnnotationIfValidCharactersInKey() { + AWSXRay.beginSubsegment("subSegment"); + String inputKey = "validKey"; + TracingUtils.putAnnotation(inputKey, "val"); + AWSXRay.getCurrentSubsegmentOptional() + .ifPresent(segment -> assertThat(segment.getAnnotations()).size().isEqualTo(1)); + } + + @Test + void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { + TracingUtils.withSubsegment("testNamespace", "testSubSegment", subsegment -> { subsegment.putAnnotation("key", "val"); subsegment.putMetadata("key", "val"); - test.getFunctionName(); }); - verify(test).getFunctionName(); - assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getName()) .isEqualTo("## testSubSegment"); @@ -157,25 +155,18 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { @Test void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedException { - Context test = mock(Context.class); - Entity traceEntity = AWSXRay.getTraceEntity(); - Thread thread = new Thread(() -> withEntitySubsegment("testSubSegment", traceEntity, subsegment -> - { + Thread thread = new Thread(() -> withEntitySubsegment("testSubSegment", traceEntity, subsegment -> { subsegment.putAnnotation("key", "val"); - test.getFunctionName(); })); thread.start(); thread.join(); - verify(test).getFunctionName(); - assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getName()) .isEqualTo("## testSubSegment"); @@ -190,26 +181,19 @@ void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedExce @Test void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws InterruptedException { - Context test = mock(Context.class); - Entity traceEntity = AWSXRay.getTraceEntity(); - Thread thread = - new Thread(() -> withEntitySubsegment("testNamespace", "testSubSegment", traceEntity, subsegment -> - { + Thread thread = new Thread( + () -> withEntitySubsegment("testNamespace", "testSubSegment", traceEntity, subsegment -> { subsegment.putAnnotation("key", "val"); - test.getFunctionName(); })); thread.start(); thread.join(); - verify(test).getFunctionName(); - assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getName()) .isEqualTo("## testSubSegment"); @@ -221,4 +205,4 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter .containsEntry("key", "val"); }); } -} \ No newline at end of file +} diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java index d61206886..b22f7a9af 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java @@ -18,24 +18,23 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import com.amazonaws.xray.AWSXRay; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; + import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.amazonaws.xray.AWSXRay; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import software.amazon.lambda.powertools.tracing.handlers.PowerToolDisabled; import software.amazon.lambda.powertools.tracing.handlers.PowerToolDisabledForStream; import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabled; @@ -47,32 +46,21 @@ import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForStreamWithNoMetaData; import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithException; import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithNoMetaData; -import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithNoMetaDataDeprecated; import software.amazon.lambda.powertools.tracing.nonhandler.PowerToolNonHandler; +@SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "false") +@SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_ERROR", value = "false") class LambdaTracingAspectTest { private RequestHandler<Object, Object> requestHandler; private RequestStreamHandler streamHandler; private PowerToolNonHandler nonHandlerMethod; - @Mock private Context context; - @BeforeAll - static void beforeAll() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_RESPONSE")).thenReturn(null); - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_ERROR")).thenReturn(null); - mocked.when(() -> SystemWrapper.containsKey("POWERTOOLS_TRACER_CAPTURE_RESPONSE")).thenReturn(false); - mocked.when(() -> SystemWrapper.containsKey("POWERTOOLS_TRACER_CAPTURE_ERROR")).thenReturn(false); - } - } - @BeforeEach void setUp() throws IllegalAccessException { - openMocks(this); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); + context = new TestLambdaContext(); requestHandler = new PowerTracerToolEnabled(); streamHandler = new PowerTracerToolEnabledForStream(); nonHandlerMethod = new PowerToolNonHandler(); @@ -93,8 +81,7 @@ void shouldCaptureNonHandlerMethod() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .anySatisfy(segment -> - assertThat(segment.getName()).isEqualTo("## doSomething")); + .anySatisfy(segment -> assertThat(segment.getName()).isEqualTo("## doSomething")); } @Test @@ -106,8 +93,7 @@ void shouldCaptureNonHandlerMethodWithCustomSegmentName() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .anySatisfy(segment -> - assertThat(segment.getName()).isEqualTo("custom")); + .anySatisfy(segment -> assertThat(segment.getName()).isEqualTo("custom")); } @Test @@ -119,8 +105,28 @@ void shouldCaptureTraces() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "true") + void shouldCaptureTracesWithResponseMetadata() { + requestHandler.handleRequest(new Object(), context); + + assertThat(AWSXRay.getTraceEntity()) + .isNotNull(); + + assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) + .hasSize(1) + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -133,6 +139,7 @@ void shouldCaptureTraces() { } @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_ERROR", value = "true") void shouldCaptureTracesWithExceptionMetaData() { requestHandler = new PowerTracerToolEnabledWithException(); @@ -143,8 +150,7 @@ void shouldCaptureTracesWithExceptionMetaData() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -169,8 +175,25 @@ void shouldCaptureTracesForStream() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "streamHandler"); + }); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "true") + void shouldCaptureTracesForStreamWithResponseMetadata() throws IOException { + streamHandler.handleRequest(new ByteArrayInputStream("test".getBytes()), new ByteArrayOutputStream(), context); + + assertThat(AWSXRay.getTraceEntity()) + .isNotNull(); + + assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) + .hasSize(1) + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -211,8 +234,7 @@ void shouldCaptureTracesWithNoMetadata() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -234,8 +256,7 @@ void shouldCaptureTracesForStreamWithNoMetadata() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -247,9 +268,8 @@ void shouldCaptureTracesForStreamWithNoMetadata() throws IOException { } @Test - void shouldCaptureTracesWithNoMetadataDeprecated() { - requestHandler = new PowerTracerToolEnabledWithNoMetaDataDeprecated(); - + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "false") + void shouldNotCaptureTracesIfDisabledViaEnvironmentVariable() { requestHandler.handleRequest(new Object(), context); assertThat(AWSXRay.getTraceEntity()) @@ -257,12 +277,11 @@ void shouldCaptureTracesWithNoMetadataDeprecated() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); + .containsEntry("Service", "lambdaHandler"); assertThat(subsegment.getMetadata()) .isEmpty(); @@ -270,56 +289,27 @@ void shouldCaptureTracesWithNoMetadataDeprecated() { } @Test - void shouldNotCaptureTracesIfDisabledViaEnvironmentVariable() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.containsKey("POWERTOOLS_TRACER_CAPTURE_RESPONSE")).thenReturn(true); - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_RESPONSE")).thenReturn("false"); - - requestHandler.handleRequest(new Object(), context); - - assertThat(AWSXRay.getTraceEntity()) - .isNotNull(); - - assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) - .hasSize(1) - .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); - } - } - - @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "false") void shouldCaptureTracesIfExplicitlyEnabledAndEnvironmentVariableIsDisabled() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_RESPONSE")).thenReturn("false"); - requestHandler = new PowerTracerToolEnabledForResponse(); - - requestHandler.handleRequest(new Object(), context); - - assertThat(AWSXRay.getTraceEntity()) - .isNotNull(); - - assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) - .hasSize(1) - .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - }); - } + requestHandler = new PowerTracerToolEnabledForResponse(); + + requestHandler.handleRequest(new Object(), context); + + assertThat(AWSXRay.getTraceEntity()) + .isNotNull(); + + assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) + .hasSize(1) + .allSatisfy(subsegment -> { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } @Test @@ -333,8 +323,7 @@ void shouldCaptureTracesForSelfReferencingReturnTypesViaCustomMapper() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getMetadata()) .hasSize(1) .containsKey("lambdaHandler"); @@ -350,101 +339,82 @@ void shouldCaptureTracesForSelfReferencingReturnTypesViaCustomMapper() { } @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "false") + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_ERROR", value = "false") void shouldCaptureTracesIfExplicitlyEnabledBothAndEnvironmentVariableIsDisabled() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.containsKey("POWERTOOLS_TRACER_CAPTURE_RESPONSE")).thenReturn(true); - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_RESPONSE")).thenReturn("false"); - mocked.when(() -> SystemWrapper.containsKey("POWERTOOLS_TRACER_CAPTURE_ERROR")).thenReturn(true); - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_ERROR")).thenReturn("false"); - requestHandler = new PowerTracerToolEnabledExplicitlyForResponseAndError(); - - requestHandler.handleRequest(new Object(), context); - - assertThat(AWSXRay.getTraceEntity()) - .isNotNull(); - - assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) - .hasSize(1) - .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - }); - } + requestHandler = new PowerTracerToolEnabledExplicitlyForResponseAndError(); + + requestHandler.handleRequest(new Object(), context); + + assertThat(AWSXRay.getTraceEntity()) + .isNotNull(); + + assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) + .hasSize(1) + .allSatisfy(subsegment -> { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_ERROR", value = "false") void shouldNotCaptureTracesWithExceptionMetaDataIfDisabledViaEnvironmentVariable() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.containsKey("POWERTOOLS_TRACER_CAPTURE_ERROR")).thenReturn(true); - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_ERROR")).thenReturn("false"); - requestHandler = new PowerTracerToolEnabledWithException(); - - Throwable throwable = catchThrowable(() -> requestHandler.handleRequest(new Object(), context)); - - assertThat(throwable) - .isInstanceOf(RuntimeException.class); - - assertThat(AWSXRay.getTraceEntity()) - .isNotNull(); - - assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) - .hasSize(1) - .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); - } + requestHandler = new PowerTracerToolEnabledWithException(); + + Throwable throwable = catchThrowable(() -> requestHandler.handleRequest(new Object(), context)); + + assertThat(throwable) + .isInstanceOf(RuntimeException.class); + + assertThat(AWSXRay.getTraceEntity()) + .isNotNull(); + + assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) + .hasSize(1) + .allSatisfy(subsegment -> { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_ERROR", value = "false") void shouldCaptureTracesWithExceptionMetaDataEnabledExplicitlyAndEnvironmentVariableDisabled() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_TRACER_CAPTURE_ERROR")).thenReturn("false"); - - requestHandler = new PowerTracerToolEnabledForError(); - - Throwable exception = catchThrowable(() -> requestHandler.handleRequest(new Object(), context)); - - assertThat(AWSXRay.getTraceEntity()) - .isNotNull(); - - assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) - .hasSize(1) - .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - - assertThat(subsegment.getMetadata().get("lambdaHandler")) - .satisfies(stringObjectMap -> assertThat(stringObjectMap) - .containsEntry("handleRequest error", exception)); - }); - } - } + requestHandler = new PowerTracerToolEnabledForError(); + + Throwable exception = catchThrowable(() -> requestHandler.handleRequest(new Object(), context)); - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); + assertThat(AWSXRay.getTraceEntity()) + .isNotNull(); + + assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) + .hasSize(1) + .allSatisfy(subsegment -> { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + + assertThat(subsegment.getMetadata().get("lambdaHandler")) + .satisfies(stringObjectMap -> assertThat(stringObjectMap) + .containsEntry("handleRequest error", exception)); + }); } + } diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptorTest.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptorTest.java new file mode 100644 index 000000000..b24f6c6f7 --- /dev/null +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.tracing.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class TracingUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/TRACING/"); + } +} \ No newline at end of file diff --git a/powertools-tracing/src/test/resources/log4j2.xml b/powertools-tracing/src/test/resources/log4j2.xml index 108e32b75..030d11725 100644 --- a/powertools-tracing/src/test/resources/log4j2.xml +++ b/powertools-tracing/src/test/resources/log4j2.xml @@ -2,7 +2,7 @@ <Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> <Appenders> <File name="JsonAppender" fileName="target/logfile.json"> - <LambdaJsonLayout compact="true" eventEol="true"/> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> </File> </Appenders> <Loggers> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index daec3aa99..8dfce6b6a 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,41 +24,23 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.9.0</version> </parent> - <name>Powertools for AWS Lambda (Java) validation library</name> + <name>Powertools for AWS Lambda (Java) - Validation</name> <description> Json schema validation for Lambda events and responses </description> - <url>https://aws.amazon.com/lambda/</url> - <issueManagement> - <system>GitHub Issues</system> - <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> - </issueManagement> - - <scm> - <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> - </scm> - <developers> - <developer> - <name>Powertools for AWS Lambda team</name> - <organization>Amazon Web Services</organization> - <organizationUrl>https://aws.amazon.com/</organizationUrl> - </developer> - </developers> - - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> @@ -80,19 +62,24 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.0.86</version> + <version>1.5.9</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-serialization</artifactId> </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <scope>provided</scope> + </dependency> <!-- Test dependencies --> <dependency> @@ -105,10 +92,9 @@ <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> <scope>test</scope> </dependency> <dependency> @@ -116,6 +102,16 @@ <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-tests</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> @@ -131,15 +127,31 @@ <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> - </plugins> - </build> - -</project> \ No newline at end of file + <profiles> + <profile> + <id>generate-classesloaded-file</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine> + -Xlog:class+load=info:classesloaded.txt + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java index baf5e2465..346dc6fa3 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java @@ -16,10 +16,15 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion; import io.burt.jmespath.JmesPath; import io.burt.jmespath.function.BaseFunction; +import org.crac.Context; +import org.crac.Core; +import org.crac.Resource; +import software.amazon.lambda.powertools.common.internal.ClassPreLoader; import software.amazon.lambda.powertools.utilities.JsonConfig; import software.amazon.lambda.powertools.utilities.jmespath.Base64Function; import software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction; @@ -31,10 +36,15 @@ * For everything but the validation features (factory, schemaVersion), {@link ValidationConfig} * is just a wrapper of {@link JsonConfig}. */ -public class ValidationConfig { +public class ValidationConfig implements Resource { private SpecVersion.VersionFlag jsonSchemaVersion = SpecVersion.VersionFlag.V7; private JsonSchemaFactory factory = JsonSchemaFactory.getInstance(jsonSchemaVersion); + // Static block to ensure CRaC registration happens at class loading time + static { + Core.getGlobalContext().register(get()); + } + private ValidationConfig() { } @@ -47,9 +57,15 @@ public SpecVersion.VersionFlag getSchemaVersion() { } /** - * Set the version of the json schema specifications (default is V7) + * Set the version of the json schema specifications to use if $schema is not + * explicitly specified within the schema (default is V7). If $schema is + * explicitly specified within the schema is explicitly specified within the + * schema, the validator will use the specified dialect. * - * @param version May be V4, V6, V7 or V201909 + * @param version May be V4, V6, V7, V201909 or V202012 + * @see <a href= + * "https://json-schema.org/understanding-json-schema/reference/schema#declaring-a-dialect">Declaring + * a Dialect</a> */ public void setSchemaVersion(SpecVersion.VersionFlag version) { if (version != jsonSchemaVersion) { @@ -96,7 +112,27 @@ public ObjectMapper getObjectMapper() { return JsonConfig.get().getObjectMapper(); } + @Override + public void beforeCheckpoint(Context<? extends Resource> context) throws Exception { + // Initialize key components + getObjectMapper(); + getJmesPath(); + getFactory(); + + // Dummy validation + String sampleSchema = "{\"type\":\"object\"}"; + JsonSchema schema = ValidationUtils.getJsonSchema(sampleSchema); + ValidationUtils.validate("{\"test\":\"dummy\"}", schema); + + ClassPreLoader.preloadClasses(); + } + + @Override + public void afterRestore(Context<? extends Resource> context) throws Exception { + // No action needed after restore + } + private static class ConfigHolder { - private final static ValidationConfig instance = new ValidationConfig(); + private static final ValidationConfig instance = new ValidationConfig(); } } diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java index 4eecb3ab5..35b309f07 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java @@ -21,16 +21,16 @@ import com.fasterxml.jackson.databind.node.JsonNodeType; import com.fasterxml.jackson.databind.node.NullNode; import com.networknt.schema.JsonSchema; +import com.networknt.schema.SchemaLocation; +import com.networknt.schema.SchemaValidatorsConfig; import com.networknt.schema.ValidationMessage; import io.burt.jmespath.Expression; import java.io.ByteArrayOutputStream; -import java.io.InputStream; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import software.amazon.lambda.powertools.validation.internal.ValidationAspect; /** * Validation utility, used to manually validate Json against Json Schema @@ -253,30 +253,32 @@ public static JsonSchema getJsonSchema(String schema, boolean validateSchema) { private static JsonSchema createJsonSchema(String schema) { JsonSchema jsonSchema; + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true) + .preloadJsonSchemaRefMaxNestingDepth(10).build(); if (schema.startsWith(CLASSPATH)) { - String filePath = schema.substring(CLASSPATH.length()); - try (InputStream schemaStream = ValidationAspect.class.getResourceAsStream(filePath)) { - - jsonSchema = ValidationConfig.get().getFactory().getSchema(schemaStream); + try { + jsonSchema = ValidationConfig.get().getFactory().getSchema(SchemaLocation.of(schema), config); } catch (Exception e) { + String filePath = schema.substring(CLASSPATH.length()); throw new IllegalArgumentException( - "'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); + "'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath", e); } } else { - jsonSchema = ValidationConfig.get().getFactory().getSchema(schema); + jsonSchema = ValidationConfig.get().getFactory().getSchema(schema, config); } return jsonSchema; } private static void validateSchema(String schema, JsonSchema jsonSchema) { - String version = ValidationConfig.get().getSchemaVersion().toString(); + String schemaId = jsonSchema.getValidationContext().getMetaSchema().getIri() + .replace("https://json-schema.org", "").replace("http://json-schema.org", ""); try { validate(jsonSchema.getSchemaNode(), - getJsonSchema("classpath:/schemas/meta_schema_" + version)); + getJsonSchema(CLASSPATH + schemaId)); } catch (ValidationException ve) { throw new IllegalArgumentException( - "The schema " + schema + " is not valid, it does not respect the specification " + version, ve); + "The schema " + schema + " is not valid, it does not respect the specification " + schemaId, ve); } } diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java index 6055f8d58..68900d334 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java @@ -16,7 +16,7 @@ import static com.networknt.schema.SpecVersion.VersionFlag.V201909; import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; @@ -40,9 +40,16 @@ import com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent; import com.amazonaws.services.lambda.runtime.events.RabbitMQEvent; import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import com.networknt.schema.JsonSchema; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -51,9 +58,10 @@ import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.validation.Validation; import software.amazon.lambda.powertools.validation.ValidationConfig; +import software.amazon.lambda.powertools.validation.ValidationException; /** - * Aspect for {@link Validation} annotation + * Aspect for {@link Validation} annotation. Internal to Powertools, use the annotation itself. */ @Aspect public class ValidationAspect { @@ -74,6 +82,12 @@ public Object around(ProceedingJoinPoint pjp, ValidationConfig.get().setSchemaVersion(validation.schemaVersion()); } + // we need this result object to be null at this point as validation of API events, if + // it fails, will catch the ValidationException and generate a 400 API response. This response + // will be stored in the result object to prevent executing the lambda + Object validationResult = null; + boolean failFast = false; + if (placedOnRequestHandler(pjp)) { validationNeeded = true; @@ -85,16 +99,19 @@ public Object around(ProceedingJoinPoint pjp, validate(obj, inboundJsonSchema, validation.envelope()); } else if (obj instanceof APIGatewayProxyRequestEvent) { APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) obj; - validate(event.getBody(), inboundJsonSchema); + validationResult = validateAPIGatewayProxyBody(event.getBody(), inboundJsonSchema, null, null); + failFast = true; } else if (obj instanceof APIGatewayV2HTTPEvent) { APIGatewayV2HTTPEvent event = (APIGatewayV2HTTPEvent) obj; - validate(event.getBody(), inboundJsonSchema); + validationResult = validateAPIGatewayV2HTTPBody(event.getBody(), inboundJsonSchema, null, null); + failFast = true; } else if (obj instanceof SNSEvent) { SNSEvent event = (SNSEvent) obj; - event.getRecords().forEach(record -> validate(record.getSNS().getMessage(), inboundJsonSchema)); + event.getRecords() + .forEach(snsRecord -> validate(snsRecord.getSNS().getMessage(), inboundJsonSchema)); } else if (obj instanceof SQSEvent) { SQSEvent event = (SQSEvent) obj; - event.getRecords().forEach(record -> validate(record.getBody(), inboundJsonSchema)); + validationResult = validateSQSEventMessages(event.getRecords(), inboundJsonSchema); } else if (obj instanceof ScheduledEvent) { ScheduledEvent event = (ScheduledEvent) obj; validate(event.getDetail(), inboundJsonSchema); @@ -109,30 +126,32 @@ public Object around(ProceedingJoinPoint pjp, validate(event.getResourceProperties(), inboundJsonSchema); } else if (obj instanceof KinesisEvent) { KinesisEvent event = (KinesisEvent) obj; - event.getRecords() - .forEach(record -> validate(decode(record.getKinesis().getData()), inboundJsonSchema)); + validationResult = validateKinesisEventRecords(event.getRecords(), inboundJsonSchema); } else if (obj instanceof KinesisFirehoseEvent) { KinesisFirehoseEvent event = (KinesisFirehoseEvent) obj; - event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); + event.getRecords() + .forEach(eventRecord -> validate(decode(eventRecord.getData()), inboundJsonSchema)); } else if (obj instanceof KafkaEvent) { KafkaEvent event = (KafkaEvent) obj; event.getRecords().forEach((s, records) -> records.forEach( - record -> validate(decode(record.getValue()), inboundJsonSchema))); + eventRecord -> validate(decode(eventRecord.getValue()), inboundJsonSchema))); } else if (obj instanceof ActiveMQEvent) { ActiveMQEvent event = (ActiveMQEvent) obj; - event.getMessages().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); + event.getMessages().forEach(message -> validate(decode(message.getData()), inboundJsonSchema)); } else if (obj instanceof RabbitMQEvent) { RabbitMQEvent event = (RabbitMQEvent) obj; event.getRmqMessagesByQueue().forEach((s, records) -> records.forEach( - record -> validate(decode(record.getData()), inboundJsonSchema))); + message -> validate(decode(message.getData()), inboundJsonSchema))); } else if (obj instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) obj; - event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); + event.getRecords() + .forEach(eventRecord -> validate(decode(eventRecord.getData()), inboundJsonSchema)); } else if (obj instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) { KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) obj; - event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); + event.getRecords() + .forEach(eventRecord -> validate(decode(eventRecord.getData()), inboundJsonSchema)); } else { LOG.warn("Unhandled event type {}, please use the 'envelope' parameter to specify what to validate", obj.getClass().getName()); @@ -140,33 +159,183 @@ record -> validate(decode(record.getData()), inboundJsonSchema))); } } - Object result = pjp.proceed(proceedArgs); - - if (validationNeeded && !validation.outboundSchema().isEmpty()) { - JsonSchema outboundJsonSchema = getJsonSchema(validation.outboundSchema(), true); - - if (result instanceof APIGatewayProxyResponseEvent) { - APIGatewayProxyResponseEvent response = (APIGatewayProxyResponseEvent) result; - validate(response.getBody(), outboundJsonSchema); - } else if (result instanceof APIGatewayV2HTTPResponse) { - APIGatewayV2HTTPResponse response = (APIGatewayV2HTTPResponse) result; - validate(response.getBody(), outboundJsonSchema); - } else if (result instanceof APIGatewayV2WebSocketResponse) { - APIGatewayV2WebSocketResponse response = (APIGatewayV2WebSocketResponse) result; - validate(response.getBody(), outboundJsonSchema); - } else if (result instanceof ApplicationLoadBalancerResponseEvent) { - ApplicationLoadBalancerResponseEvent response = (ApplicationLoadBalancerResponseEvent) result; - validate(response.getBody(), outboundJsonSchema); - } else if (result instanceof KinesisAnalyticsInputPreprocessingResponse) { - KinesisAnalyticsInputPreprocessingResponse response = - (KinesisAnalyticsInputPreprocessingResponse) result; - response.getRecords().forEach(record -> validate(decode(record.getData()), outboundJsonSchema)); - } else { - LOG.warn("Unhandled response type {}, please use the 'envelope' parameter to specify what to validate", - result.getClass().getName()); + Object result; + + // don't execute the lambda if result was set by previous validation step and should fail fast + // in that case result should already hold a response with validation information + if (failFast && validationResult != null) { + LOG.error("Incoming API event's body failed inbound schema validation."); + return validationResult; + } else { + result = pjp.proceed(proceedArgs); + + if (validationResult != null && result != null) { + // in the case of batches (SQS, Kinesis), we copy the batch item failures to the result + if (result instanceof SQSBatchResponse && validationResult instanceof SQSBatchResponse) { + SQSBatchResponse validationResponse = (SQSBatchResponse) validationResult; + SQSBatchResponse response = (SQSBatchResponse) result; + if (response.getBatchItemFailures() == null) { + response.setBatchItemFailures(validationResponse.getBatchItemFailures()); + } else { + response.getBatchItemFailures().addAll(validationResponse.getBatchItemFailures()); + } + } else if (result instanceof StreamsEventResponse && validationResult instanceof StreamsEventResponse) { + StreamsEventResponse validationResponse = (StreamsEventResponse) validationResult; + StreamsEventResponse response = (StreamsEventResponse) result; + if (response.getBatchItemFailures() == null) { + response.setBatchItemFailures(validationResponse.getBatchItemFailures()); + } else { + response.getBatchItemFailures().addAll(validationResponse.getBatchItemFailures()); + } + } + } + + if (result != null && validationNeeded && !validation.outboundSchema().isEmpty()) { + JsonSchema outboundJsonSchema = getJsonSchema(validation.outboundSchema(), true); + + Object overridenResponse = null; + // The normal behavior of @Validation is to throw an exception if response's validation fails. + // but in the case of APIGatewayProxyResponseEvent and APIGatewayV2HTTPResponse we want to return + // a 400 response with the validation errors instead of throwing an exception. + if (result instanceof APIGatewayProxyResponseEvent) { + APIGatewayProxyResponseEvent response = (APIGatewayProxyResponseEvent) result; + overridenResponse = + validateAPIGatewayProxyBody(response.getBody(), outboundJsonSchema, response.getHeaders(), + response.getMultiValueHeaders()); + } else if (result instanceof APIGatewayV2HTTPResponse) { + APIGatewayV2HTTPResponse response = (APIGatewayV2HTTPResponse) result; + overridenResponse = + validateAPIGatewayV2HTTPBody(response.getBody(), outboundJsonSchema, response.getHeaders(), + response.getMultiValueHeaders()); + // all type of below responses will throw an exception if validation fails + } else if (result instanceof APIGatewayV2WebSocketResponse) { + APIGatewayV2WebSocketResponse response = (APIGatewayV2WebSocketResponse) result; + validate(response.getBody(), outboundJsonSchema); + } else if (result instanceof ApplicationLoadBalancerResponseEvent) { + ApplicationLoadBalancerResponseEvent response = (ApplicationLoadBalancerResponseEvent) result; + validate(response.getBody(), outboundJsonSchema); + } else if (result instanceof KinesisAnalyticsInputPreprocessingResponse) { + KinesisAnalyticsInputPreprocessingResponse response = + (KinesisAnalyticsInputPreprocessingResponse) result; + response.getRecords().forEach(record -> validate(decode(record.getData()), outboundJsonSchema)); + } else { + LOG.warn( + "Unhandled response type {}, please use the 'envelope' parameter to specify what to validate", + result.getClass().getName()); + } + + if (overridenResponse != null) { + result = overridenResponse; + LOG.error("API response failed outbound schema validation."); + } } } return result; } + + /** + * Validate each Kinesis record body. If an error occurs, do not fail the whole batch but only add invalid items in BatchItemFailure. + * Note that the valid records will be decoded twice (during validation and within the handler by the user), which will slightly reduce performance. + * @param records Kinesis records + * @param inboundJsonSchema validation schema + * @return the stream response with items in failure + */ + private StreamsEventResponse validateKinesisEventRecords(List<KinesisEvent.KinesisEventRecord> records, + JsonSchema inboundJsonSchema) { + StreamsEventResponse response = StreamsEventResponse.builder().withBatchItemFailures(new ArrayList<>()).build(); + + ListIterator<KinesisEvent.KinesisEventRecord> listIterator = records.listIterator(); // using iterator to remove while browsing + while (listIterator.hasNext()) { + KinesisEvent.KinesisEventRecord eventRecord = listIterator.next(); + try { + validate(decode(eventRecord.getKinesis().getData()), inboundJsonSchema); + } catch (ValidationException e) { + LOG.error("Validation error on message {}: {}", eventRecord.getKinesis().getSequenceNumber(), + e.getMessage()); + listIterator.remove(); + response.getBatchItemFailures().add(StreamsEventResponse.BatchItemFailure.builder() + .withItemIdentifier(eventRecord.getKinesis().getSequenceNumber()).build()); + } + } + return response; + } + + /** + * Validate each SQS message body. If an error occurs, do not fail the whole batch but only add invalid items in BatchItemFailure. + * + * @param messages SQS messages + * @param inboundJsonSchema validation schema + * @return the SQS batch response + */ + private SQSBatchResponse validateSQSEventMessages(List<SQSEvent.SQSMessage> messages, + JsonSchema inboundJsonSchema) { + SQSBatchResponse response = SQSBatchResponse.builder().withBatchItemFailures(new ArrayList<>()).build(); + ListIterator<SQSEvent.SQSMessage> listIterator = messages.listIterator(); // using iterator to remove while browsing + while (listIterator.hasNext()) { + SQSEvent.SQSMessage message = listIterator.next(); + try { + validate(message.getBody(), inboundJsonSchema); + } catch (ValidationException e) { + LOG.error("Validation error on message {}: {}", message.getMessageId(), e.getMessage()); + listIterator.remove(); + response.getBatchItemFailures() + .add(SQSBatchResponse.BatchItemFailure.builder().withItemIdentifier(message.getMessageId()) + .build()); + } + } + return response; + } + + /** + * Validates the given body against the provided JsonSchema. If validation fails the ValidationException + * will be catched and transformed to a 400, bad request, API response + * + * @param body body of the event to validate + * @param jsonSchema validation schema + * @return null if validation passed, or a 400 response object otherwise + */ + private APIGatewayProxyResponseEvent validateAPIGatewayProxyBody(final String body, final JsonSchema jsonSchema, + final Map<String, String> headers, + Map<String, List<String>> multivalueHeaders) { + APIGatewayProxyResponseEvent result = null; + try { + validate(body, jsonSchema); + } catch (ValidationException e) { + LOG.error("There were validation errors: {}", e.getMessage()); + result = new APIGatewayProxyResponseEvent(); + result.setBody(e.getMessage()); + result.setHeaders(headers == null ? Collections.emptyMap() : headers); + result.setMultiValueHeaders(multivalueHeaders == null ? Collections.emptyMap() : multivalueHeaders); + result.setStatusCode(400); + result.setIsBase64Encoded(false); + } + return result; + } + + /** + * Validates the given body against the provided JsonSchema. If validation fails the ValidationException + * will be catched and transformed to a 400, bad request, API response + * + * @param body body of the event to validate + * @param jsonSchema validation schema + * @return null if validation passed, or a 400 response object otherwise + */ + private APIGatewayV2HTTPResponse validateAPIGatewayV2HTTPBody(final String body, final JsonSchema jsonSchema, + final Map<String, String> headers, + Map<String, List<String>> multivalueHeaders) { + APIGatewayV2HTTPResponse result = null; + try { + validate(body, jsonSchema); + } catch (ValidationException e) { + LOG.error("There were validation errors: {}", e.getMessage()); + result = new APIGatewayV2HTTPResponse(); + result.setBody(e.getMessage()); + result.setHeaders(headers == null ? Collections.emptyMap() : headers); + result.setMultiValueHeaders(multivalueHeaders == null ? Collections.emptyMap() : multivalueHeaders); + result.setStatusCode(400); + result.setIsBase64Encoded(false); + } + return result; + } } diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptor.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptor.java new file mode 100644 index 000000000..ee92ff367 --- /dev/null +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.validation.internal; + +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; + +/** + * Global interceptor that configures the User-Agent for all AWS SDK clients + * when the powertools-validation module is on the classpath. + */ +public final class ValidationUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("validation"); + } + + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + // This is a no-op interceptor. We use this class to configure the PT User-Agent in the static block. It is + // loaded by AWS SDK Global Interceptors. + return context.request(); + } +} diff --git a/powertools-validation/src/main/resources/classesloaded.txt b/powertools-validation/src/main/resources/classesloaded.txt new file mode 100644 index 000000000..adf442b37 --- /dev/null +++ b/powertools-validation/src/main/resources/classesloaded.txt @@ -0,0 +1,7285 @@ +java.lang.Object +java.io.Serializable +java.lang.Comparable +java.lang.CharSequence +java.lang.constant.Constable +java.lang.constant.ConstantDesc +java.lang.String +java.lang.reflect.AnnotatedElement +java.lang.reflect.GenericDeclaration +java.lang.reflect.Type +java.lang.invoke.TypeDescriptor +java.lang.invoke.TypeDescriptor$OfField +java.lang.Class +java.lang.Cloneable +java.lang.ClassLoader +java.lang.System +java.lang.Throwable +java.lang.Error +java.lang.ThreadDeath +java.lang.Exception +java.lang.RuntimeException +java.lang.SecurityManager +java.security.ProtectionDomain +java.security.AccessControlContext +java.security.AccessController +java.security.SecureClassLoader +java.lang.ReflectiveOperationException +java.lang.ClassNotFoundException +java.lang.Record +java.lang.LinkageError +java.lang.NoClassDefFoundError +java.lang.ClassCastException +java.lang.ArrayStoreException +java.lang.VirtualMachineError +java.lang.InternalError +java.lang.OutOfMemoryError +java.lang.StackOverflowError +java.lang.IllegalMonitorStateException +java.lang.ref.Reference +java.lang.ref.SoftReference +java.lang.ref.WeakReference +java.lang.ref.FinalReference +java.lang.ref.PhantomReference +java.lang.ref.Finalizer +java.lang.Runnable +java.lang.Thread +java.lang.Thread$UncaughtExceptionHandler +java.lang.ThreadGroup +java.util.Dictionary +java.util.Map +java.util.Hashtable +java.util.Properties +java.lang.Module +java.lang.reflect.AccessibleObject +java.lang.reflect.Member +java.lang.reflect.Field +java.lang.reflect.Parameter +java.lang.reflect.Executable +java.lang.reflect.Method +java.lang.reflect.Constructor +jdk.internal.reflect.MagicAccessorImpl +jdk.internal.reflect.MethodAccessor +jdk.internal.reflect.MethodAccessorImpl +jdk.internal.reflect.ConstructorAccessor +jdk.internal.reflect.ConstructorAccessorImpl +jdk.internal.reflect.DelegatingClassLoader +jdk.internal.reflect.ConstantPool +jdk.internal.reflect.FieldAccessor +jdk.internal.reflect.FieldAccessorImpl +jdk.internal.reflect.UnsafeFieldAccessorImpl +jdk.internal.reflect.UnsafeStaticFieldAccessorImpl +java.lang.annotation.Annotation +jdk.internal.reflect.CallerSensitive +jdk.internal.reflect.NativeConstructorAccessorImpl +java.lang.invoke.MethodHandle +java.lang.invoke.DirectMethodHandle +java.lang.invoke.VarHandle +java.lang.invoke.MemberName +java.lang.invoke.ResolvedMethodName +java.lang.invoke.MethodHandleNatives +java.lang.invoke.LambdaForm +java.lang.invoke.TypeDescriptor$OfMethod +java.lang.invoke.MethodType +java.lang.BootstrapMethodError +java.lang.invoke.CallSite +jdk.internal.invoke.NativeEntryPoint +java.lang.invoke.MethodHandleNatives$CallSiteContext +java.lang.invoke.ConstantCallSite +java.lang.invoke.MutableCallSite +java.lang.invoke.VolatileCallSite +java.lang.AssertionStatusDirectives +java.lang.Appendable +java.lang.AbstractStringBuilder +java.lang.StringBuffer +java.lang.StringBuilder +jdk.internal.misc.UnsafeConstants +jdk.internal.misc.Unsafe +jdk.internal.module.Modules +java.lang.AutoCloseable +java.io.Closeable +java.io.InputStream +java.io.ByteArrayInputStream +java.net.URL +java.util.jar.Manifest +jdk.internal.loader.BuiltinClassLoader +jdk.internal.loader.ClassLoaders +jdk.internal.loader.ClassLoaders$AppClassLoader +jdk.internal.loader.ClassLoaders$PlatformClassLoader +java.security.CodeSource +java.util.AbstractMap +java.util.concurrent.ConcurrentMap +java.util.concurrent.ConcurrentHashMap +java.lang.Iterable +java.util.Collection +java.util.AbstractCollection +java.util.List +java.util.AbstractList +java.util.RandomAccess +java.util.ArrayList +java.lang.StackTraceElement +java.nio.Buffer +java.lang.StackWalker +java.lang.StackStreamFactory$AbstractStackWalker +java.lang.StackWalker$StackFrame +java.lang.StackFrameInfo +java.lang.LiveStackFrame +java.lang.LiveStackFrameInfo +java.util.concurrent.locks.AbstractOwnableSynchronizer +java.lang.Boolean +java.lang.Character +java.lang.Number +java.lang.Float +java.lang.Double +java.lang.Byte +java.lang.Short +java.lang.Integer +java.lang.Long +java.util.Iterator +java.lang.reflect.RecordComponent +jdk.internal.vm.vector.VectorSupport +jdk.internal.vm.vector.VectorSupport$VectorPayload +jdk.internal.vm.vector.VectorSupport$Vector +jdk.internal.vm.vector.VectorSupport$VectorMask +jdk.internal.vm.vector.VectorSupport$VectorShuffle +java.lang.Integer$IntegerCache +java.lang.Long$LongCache +java.lang.Byte$ByteCache +java.lang.Short$ShortCache +java.lang.Character$CharacterCache +java.util.jar.Attributes$Name +java.util.ImmutableCollections$AbstractImmutableMap +java.util.ImmutableCollections$MapN +sun.util.locale.BaseLocale +jdk.internal.module.ArchivedModuleGraph +java.lang.module.ModuleFinder +jdk.internal.module.SystemModuleFinders$SystemModuleFinder +java.util.ImmutableCollections$AbstractImmutableCollection +java.util.Set +java.util.ImmutableCollections$AbstractImmutableSet +java.util.ImmutableCollections$SetN +java.lang.module.ModuleReference +jdk.internal.module.ModuleReferenceImpl +java.lang.module.ModuleDescriptor +java.lang.module.ModuleDescriptor$Version +java.util.ImmutableCollections$Set12 +java.lang.module.ModuleDescriptor$Requires +java.lang.Enum +java.lang.module.ModuleDescriptor$Requires$Modifier +java.lang.module.ModuleDescriptor$Exports +java.net.URI +java.util.function.Supplier +jdk.internal.module.SystemModuleFinders$2 +jdk.internal.module.ModuleHashes$HashSupplier +jdk.internal.module.SystemModuleFinders$3 +java.lang.module.ModuleDescriptor$Provides +java.util.ImmutableCollections$AbstractImmutableList +java.util.ImmutableCollections$List12 +java.util.ImmutableCollections$ListN +java.lang.module.ModuleDescriptor$Opens +jdk.internal.module.ModuleTarget +jdk.internal.module.ModuleHashes +java.util.Collections$UnmodifiableMap +java.util.HashMap +java.util.Map$Entry +java.util.HashMap$Node +java.lang.module.Configuration +java.lang.module.ResolvedModule +java.util.function.Function +jdk.internal.module.ModuleLoaderMap$Mapper +java.util.ImmutableCollections +java.lang.ModuleLayer +jdk.internal.math.FDBigInteger +java.lang.NullPointerException +java.lang.ArithmeticException +java.io.ObjectStreamField +java.util.Comparator +java.lang.String$CaseInsensitiveComparator +java.lang.Module$ArchivedData +jdk.internal.misc.CDS +java.util.Objects +jdk.internal.access.JavaLangReflectAccess +java.lang.reflect.ReflectAccess +jdk.internal.access.SharedSecrets +java.lang.invoke.MethodHandles +java.lang.invoke.MemberName$Factory +java.security.Guard +java.security.Permission +java.security.BasicPermission +java.lang.reflect.ReflectPermission +java.lang.StringLatin1 +java.lang.invoke.MethodHandles$Lookup +jdk.internal.reflect.Reflection +java.lang.Math +java.util.AbstractSet +java.util.ImmutableCollections$MapN$1 +java.util.ImmutableCollections$MapN$MapNIterator +java.util.KeyValueHolder +java.util.LinkedHashMap$Entry +java.util.HashMap$TreeNode +java.lang.Runtime +java.util.concurrent.locks.Lock +java.util.concurrent.locks.ReentrantLock +java.util.concurrent.ConcurrentHashMap$Segment +java.util.concurrent.ConcurrentHashMap$CounterCell +java.util.concurrent.ConcurrentHashMap$Node +java.util.concurrent.locks.LockSupport +java.util.concurrent.ConcurrentHashMap$ReservationNode +java.security.PrivilegedAction +jdk.internal.reflect.ReflectionFactory$GetReflectionFactoryAction +jdk.internal.reflect.ReflectionFactory +java.lang.ref.Reference$ReferenceHandler +jdk.internal.ref.Cleaner +java.lang.ref.ReferenceQueue +java.lang.ref.ReferenceQueue$Null +java.lang.ref.ReferenceQueue$Lock +jdk.internal.access.JavaLangRefAccess +java.lang.ref.Reference$1 +java.lang.ref.Finalizer$FinalizerThread +jdk.internal.access.JavaLangAccess +java.lang.System$2 +jdk.internal.misc.VM +jdk.internal.util.SystemProps +jdk.internal.util.SystemProps$Raw +java.lang.StringConcatHelper +java.lang.VersionProps +java.util.Arrays +java.lang.CharacterData +java.lang.CharacterDataLatin1 +java.util.HashMap$EntrySet +java.util.HashMap$HashIterator +java.util.HashMap$EntryIterator +jdk.internal.util.StaticProperty +java.io.FileInputStream +java.io.FileDescriptor +jdk.internal.access.JavaIOFileDescriptorAccess +java.io.FileDescriptor$1 +java.io.Flushable +java.io.OutputStream +java.io.FileOutputStream +java.io.FilterInputStream +java.io.BufferedInputStream +java.io.FilterOutputStream +java.io.PrintStream +java.io.BufferedOutputStream +java.io.Writer +java.io.OutputStreamWriter +java.nio.charset.Charset +java.nio.charset.spi.CharsetProvider +sun.nio.cs.StandardCharsets +java.lang.ThreadLocal +java.util.concurrent.atomic.AtomicInteger +sun.security.action.GetPropertyAction +sun.nio.cs.HistoricallyNamedCharset +sun.nio.cs.Unicode +sun.nio.cs.UTF_8 +sun.nio.cs.StreamEncoder +java.nio.charset.CharsetEncoder +sun.nio.cs.UTF_8$Encoder +java.nio.charset.CodingErrorAction +java.nio.ByteBuffer +jdk.internal.misc.ScopedMemoryAccess +jdk.internal.access.JavaNioAccess +java.nio.Buffer$1 +java.nio.HeapByteBuffer +java.nio.ByteOrder +java.io.BufferedWriter +java.lang.Terminator +jdk.internal.misc.Signal$Handler +java.lang.Terminator$1 +jdk.internal.misc.Signal +java.util.Hashtable$Entry +jdk.internal.misc.Signal$NativeHandler +jdk.internal.misc.OSEnvironment +java.util.Collections +java.util.Collections$EmptySet +java.util.Collections$EmptyList +java.util.Collections$EmptyMap +java.lang.IllegalArgumentException +java.lang.invoke.MethodHandleStatics +jdk.internal.module.ModuleBootstrap +sun.invoke.util.VerifyAccess +java.lang.reflect.Modifier +jdk.internal.access.JavaLangModuleAccess +java.lang.module.ModuleDescriptor$1 +java.io.File +java.io.DefaultFileSystem +java.io.FileSystem +java.io.UnixFileSystem +jdk.internal.util.ArraysSupport +jdk.internal.module.ModulePatcher +jdk.internal.module.ModuleBootstrap$Counters +jdk.internal.module.ArchivedBootLayer +jdk.internal.access.JavaNetUriAccess +java.net.URI$1 +jdk.internal.loader.ArchivedClassLoaders +jdk.internal.loader.ClassLoaders$BootClassLoader +java.security.cert.Certificate +java.lang.ClassLoader$ParallelLoaders +java.util.WeakHashMap +java.util.WeakHashMap$Entry +java.util.Collections$SetFromMap +java.util.WeakHashMap$KeySet +jdk.internal.access.JavaSecurityAccess +java.security.ProtectionDomain$JavaSecurityAccessImpl +java.security.ProtectionDomain$Key +java.security.Principal +jdk.internal.loader.NativeLibraries +jdk.internal.loader.ClassLoaderHelper +java.util.HashSet +java.util.Queue +java.util.Deque +java.util.ArrayDeque +jdk.internal.loader.URLClassPath +java.net.URLStreamHandlerFactory +java.net.URL$DefaultFactory +jdk.internal.access.JavaNetURLAccess +java.net.URL$3 +java.io.File$PathStatus +sun.net.www.ParseUtil +java.util.HexFormat +java.net.URLStreamHandler +sun.net.www.protocol.file.Handler +sun.net.util.IPAddressUtil +jdk.internal.util.Preconditions +sun.net.www.protocol.jar.Handler +jdk.internal.module.ServicesCatalog +jdk.internal.loader.AbstractClassLoaderValue +jdk.internal.loader.ClassLoaderValue +java.util.Optional +jdk.internal.loader.BootLoader +jdk.internal.loader.BuiltinClassLoader$LoadedModule +java.util.ImmutableCollections$SetN$SetNIterator +java.util.ImmutableCollections$Set12$1 +java.util.ListIterator +java.util.ImmutableCollections$ListItr +jdk.internal.module.ModuleLoaderMap +jdk.internal.loader.AbstractClassLoaderValue$Memoizer +jdk.internal.module.ServicesCatalog$ServiceProvider +java.util.concurrent.CopyOnWriteArrayList +java.util.HashMap$KeySet +java.util.HashMap$KeyIterator +java.lang.ModuleLayer$Controller +java.lang.invoke.LambdaMetafactory +java.lang.invoke.MethodType$ConcurrentWeakInternSet +java.lang.Void +java.lang.invoke.MethodTypeForm +java.lang.invoke.MethodType$ConcurrentWeakInternSet$WeakEntry +sun.invoke.util.Wrapper +sun.invoke.util.Wrapper$Format +java.lang.invoke.LambdaForm$NamedFunction +java.lang.invoke.DirectMethodHandle$Holder +sun.invoke.util.ValueConversions +java.lang.invoke.MethodHandleImpl +java.lang.invoke.Invokers +java.lang.invoke.LambdaForm$Kind +java.lang.NoSuchMethodException +java.lang.invoke.LambdaForm$BasicType +java.lang.reflect.Array +java.lang.invoke.LambdaForm$Name +java.lang.invoke.LambdaForm$Holder +java.lang.invoke.InvokerBytecodeGenerator +java.lang.invoke.InvokerBytecodeGenerator$2 +java.lang.invoke.MethodHandleImpl$Intrinsic +java.lang.invoke.BootstrapMethodInvoker +java.lang.invoke.VarHandle$AccessMode +java.lang.invoke.VarHandle$AccessType +java.lang.invoke.Invokers$Holder +jdk.internal.access.JavaLangInvokeAccess +java.lang.invoke.MethodHandleImpl$1 +java.lang.invoke.AbstractValidatingLambdaMetafactory +java.lang.invoke.InnerClassLambdaMetafactory +jdk.internal.org.objectweb.asm.Type +sun.security.action.GetBooleanAction +jdk.internal.org.objectweb.asm.Handle +sun.invoke.util.BytecodeDescriptor +jdk.internal.org.objectweb.asm.ConstantDynamic +java.lang.invoke.MethodHandleInfo +java.lang.invoke.InfoFromMemberName +jdk.internal.org.objectweb.asm.ClassVisitor +jdk.internal.org.objectweb.asm.ClassWriter +jdk.internal.org.objectweb.asm.SymbolTable +jdk.internal.org.objectweb.asm.Symbol +jdk.internal.org.objectweb.asm.SymbolTable$Entry +jdk.internal.org.objectweb.asm.ByteVector +java.lang.invoke.LambdaProxyClassArchive +jdk.internal.org.objectweb.asm.MethodVisitor +jdk.internal.org.objectweb.asm.MethodWriter +jdk.internal.org.objectweb.asm.Label +java.lang.invoke.TypeConvertingMethodAdapter +java.lang.invoke.InnerClassLambdaMetafactory$ForwardingMethodGenerator +jdk.internal.org.objectweb.asm.Handler +jdk.internal.org.objectweb.asm.Attribute +jdk.internal.org.objectweb.asm.AnnotationVisitor +jdk.internal.org.objectweb.asm.AnnotationWriter +java.lang.invoke.MethodHandles$Lookup$ClassOption +java.lang.invoke.MethodHandles$Lookup$ClassFile +jdk.internal.org.objectweb.asm.ClassReader +java.lang.StringUTF16 +java.lang.invoke.MethodHandles$Lookup$ClassDefiner +jdk.internal.module.ModuleBootstrap$$Lambda$1/0x00007faab4040850 +java.lang.invoke.InnerClassLambdaMetafactory$1 +java.lang.Class$ReflectionData +java.lang.Class$Atomic +jdk.internal.reflect.DelegatingConstructorAccessorImpl +java.lang.invoke.BoundMethodHandle +java.lang.invoke.ClassSpecializer +java.lang.invoke.BoundMethodHandle$Specializer +java.lang.invoke.ClassSpecializer$1 +java.lang.invoke.ClassSpecializer$SpeciesData +java.lang.invoke.BoundMethodHandle$SpeciesData +java.lang.invoke.ClassSpecializer$Factory +java.lang.invoke.BoundMethodHandle$Specializer$Factory +java.lang.invoke.SimpleMethodHandle +java.lang.NoSuchFieldException +java.lang.invoke.BoundMethodHandle$Species_L +sun.invoke.util.VerifyType +sun.invoke.empty.Empty +java.lang.invoke.DirectMethodHandle$2 +java.lang.invoke.DirectMethodHandle$Accessor +java.lang.invoke.DelegatingMethodHandle +java.lang.invoke.MethodHandleImpl$IntrinsicMethodHandle +java.lang.invoke.DelegatingMethodHandle$Holder +sun.invoke.util.Wrapper$1 +java.lang.invoke.LambdaFormEditor +java.lang.invoke.LambdaFormEditor$TransformKey +java.lang.invoke.LambdaFormBuffer +java.lang.invoke.LambdaFormEditor$Transform +jdk.internal.org.objectweb.asm.Frame +java.lang.invoke.InvokerBytecodeGenerator$ClassData +java.util.ArrayList$Itr +jdk.internal.org.objectweb.asm.FieldVisitor +jdk.internal.org.objectweb.asm.FieldWriter +java.lang.invoke.LambdaForm$MH/0x00007faab4000400 +jdk.internal.ref.CleanerFactory +java.util.concurrent.ThreadFactory +jdk.internal.ref.CleanerFactory$1 +java.lang.ref.Cleaner +java.lang.ref.Cleaner$1 +jdk.internal.ref.CleanerImpl +java.lang.ref.Cleaner$Cleanable +jdk.internal.ref.PhantomCleanable +jdk.internal.ref.CleanerImpl$PhantomCleanableRef +jdk.internal.ref.CleanerImpl$CleanerCleanable +jdk.internal.misc.InnocuousThread +java.util.ArrayList$SubList +java.lang.Module$ReflectionData +java.lang.WeakPairMap +java.lang.WeakPairMap$Pair +java.lang.WeakPairMap$Pair$Lookup +java.util.function.BiFunction +java.lang.Module$$Lambda$2/0x00007faab4040a90 +java.lang.WeakPairMap$WeakRefPeer +java.lang.WeakPairMap$Pair$Weak +java.lang.WeakPairMap$Pair$Weak$1 +java.lang.WeakPairMap$$Lambda$3/0x00007faab40413e8 +java.lang.invoke.DirectMethodHandle$Constructor +java.lang.invoke.StringConcatFactory +java.lang.invoke.StringConcatFactory$1 +java.lang.invoke.StringConcatFactory$2 +java.lang.invoke.StringConcatFactory$3 +sun.launcher.LauncherHelper +java.lang.StringCoding +java.util.zip.ZipConstants +java.util.zip.ZipFile +java.util.jar.JarFile +jdk.internal.access.JavaUtilZipFileAccess +java.util.zip.ZipFile$1 +jdk.internal.access.JavaUtilJarAccess +java.util.jar.JavaUtilJarAccessImpl +java.lang.Runtime$Version +java.util.zip.ZipFile$CleanableResource +java.util.zip.ZipCoder +java.util.zip.ZipCoder$UTF8ZipCoder +java.util.zip.ZipFile$Source +sun.nio.fs.DefaultFileSystemProvider +java.nio.file.spi.FileSystemProvider +sun.nio.fs.AbstractFileSystemProvider +sun.nio.fs.UnixFileSystemProvider +sun.nio.fs.LinuxFileSystemProvider +java.nio.file.OpenOption +java.nio.file.StandardOpenOption +java.nio.file.FileSystem +sun.nio.fs.UnixFileSystem +sun.nio.fs.LinuxFileSystem +java.nio.file.Watchable +java.nio.file.Path +sun.nio.fs.UnixPath +sun.nio.fs.Util +sun.nio.fs.UnixNativeDispatcher +jdk.internal.loader.NativeLibraries$LibraryPaths +jdk.internal.loader.NativeLibraries$1 +java.util.ArrayDeque$DeqIterator +jdk.internal.loader.NativeLibrary +jdk.internal.loader.NativeLibraries$NativeLibraryImpl +java.util.concurrent.ConcurrentHashMap$CollectionView +java.util.concurrent.ConcurrentHashMap$ValuesView +java.util.concurrent.ConcurrentHashMap$Traverser +java.util.concurrent.ConcurrentHashMap$BaseIterator +java.util.Enumeration +java.util.concurrent.ConcurrentHashMap$ValueIterator +java.nio.file.attribute.BasicFileAttributes +java.nio.file.attribute.PosixFileAttributes +sun.nio.fs.UnixFileAttributes +sun.nio.fs.UnixFileStoreAttributes +sun.nio.fs.UnixMountEntry +java.util.zip.ZipFile$Source$Key +java.nio.file.CopyOption +java.nio.file.LinkOption +java.nio.file.Files +java.nio.file.attribute.DosFileAttributes +java.nio.file.attribute.AttributeView +java.nio.file.attribute.FileAttributeView +java.nio.file.attribute.BasicFileAttributeView +java.nio.file.attribute.DosFileAttributeView +java.nio.file.attribute.UserDefinedFileAttributeView +sun.nio.fs.UnixFileAttributeViews +sun.nio.fs.DynamicFileAttributeView +sun.nio.fs.AbstractBasicFileAttributeView +sun.nio.fs.UnixFileAttributeViews$Basic +sun.nio.fs.NativeBuffers +jdk.internal.misc.TerminatingThreadLocal +sun.nio.fs.NativeBuffers$1 +jdk.internal.misc.TerminatingThreadLocal$1 +java.lang.ThreadLocal$ThreadLocalMap +java.lang.ThreadLocal$ThreadLocalMap$Entry +java.util.IdentityHashMap +java.util.IdentityHashMap$KeySet +sun.nio.fs.NativeBuffer +sun.nio.fs.NativeBuffer$Deallocator +sun.nio.fs.UnixFileAttributes$UnixAsBasicFileAttributes +java.io.DataOutput +java.io.DataInput +java.io.RandomAccessFile +jdk.internal.access.JavaIORandomAccessFileAccess +java.io.RandomAccessFile$2 +java.io.FileCleanable +java.util.zip.ZipFile$Source$End +java.util.zip.ZipUtils +java.util.concurrent.TimeUnit +java.nio.file.attribute.FileTime +jdk.internal.perf.PerfCounter +jdk.internal.perf.Perf$GetPerfAction +jdk.internal.perf.Perf +jdk.internal.perf.PerfCounter$CoreCounters +sun.nio.ch.DirectBuffer +java.nio.MappedByteBuffer +java.nio.DirectByteBuffer +java.nio.Bits +java.util.concurrent.atomic.AtomicLong +jdk.internal.misc.VM$BufferPool +java.nio.Bits$1 +java.nio.LongBuffer +java.nio.DirectLongBufferU +java.util.zip.ZipEntry +java.util.jar.JarEntry +java.util.jar.JarFile$JarFileEntry +java.util.zip.ZipFile$ZipFileInputStream +java.util.zip.InflaterInputStream +java.util.zip.ZipFile$ZipFileInflaterInputStream +java.util.zip.Inflater +java.util.zip.Inflater$InflaterZStreamRef +java.util.zip.ZipFile$InflaterCleanupAction +sun.security.util.SignatureFileVerifier +sun.security.util.Debug +java.util.Locale +sun.util.locale.LocaleUtils +sun.security.action.GetIntegerAction +java.util.jar.JarVerifier +java.security.CodeSigner +java.io.ByteArrayOutputStream +java.util.jar.Attributes +java.util.LinkedHashMap +java.util.jar.Manifest$FastInputStream +java.io.RandomAccessFile$1 +sun.net.util.URLUtil +java.security.PrivilegedExceptionAction +jdk.internal.loader.URLClassPath$3 +jdk.internal.loader.URLClassPath$Loader +jdk.internal.loader.URLClassPath$JarLoader +jdk.internal.loader.URLClassPath$JarLoader$1 +jdk.internal.loader.FileURLMapper +jdk.internal.util.jar.JarIndex +java.util.StringTokenizer +jdk.internal.loader.Resource +jdk.internal.loader.URLClassPath$JarLoader$2 +java.lang.NamedPackage +java.lang.Package +java.lang.Package$VersionInfo +sun.nio.ByteBuffered +java.util.zip.Checksum +java.util.zip.CRC32 +java.util.zip.Checksum$1 +java.security.SecureClassLoader$CodeSourceKey +java.security.SecureClassLoader$1 +java.security.PermissionCollection +sun.security.util.LazyCodeSourcePermissionCollection +java.security.Permissions +java.lang.RuntimePermission +java.security.BasicPermissionCollection +java.security.AllPermission +java.security.UnresolvedPermission +java.security.SecureClassLoader$DebugHolder +org.apache.maven.surefire.booter.ForkedBooter +org.apache.maven.surefire.api.fork.ForkNodeArguments +org.apache.maven.plugin.surefire.log.api.ConsoleLogger +java.lang.SecurityException +java.security.AccessControlException +java.io.IOException +org.apache.maven.surefire.api.provider.CommandListener +org.apache.maven.surefire.api.report.ReporterFactory +java.util.concurrent.ConcurrentHashMap$ForwardingNode +org.apache.maven.surefire.api.provider.CommandChainReader +java.lang.InterruptedException +java.util.concurrent.Executor +java.util.concurrent.ExecutorService +java.util.concurrent.ScheduledExecutorService +java.lang.PublicMethods$MethodList +java.lang.PublicMethods$Key +java.util.concurrent.Semaphore +java.util.concurrent.locks.AbstractQueuedSynchronizer +java.util.concurrent.Semaphore$Sync +java.util.concurrent.Semaphore$NonfairSync +org.apache.maven.surefire.booter.BooterDeserializer +org.apache.maven.surefire.booter.SystemPropertyManager +java.util.Properties$LineReader +java.util.Properties$EntrySet +java.util.concurrent.ConcurrentHashMap$EntrySetView +java.util.Collections$SynchronizedCollection +java.util.Collections$SynchronizedSet +java.util.concurrent.ConcurrentHashMap$EntryIterator +java.util.concurrent.ConcurrentHashMap$MapEntry +java.util.Collections$UnmodifiableCollection +java.util.Collections$UnmodifiableSet +java.util.Collections$UnmodifiableCollection$1 +org.apache.maven.surefire.booter.KeyValueSource +org.apache.maven.surefire.booter.PropertiesWrapper +java.lang.IllegalStateException +java.io.FileInputStream$1 +org.apache.maven.surefire.booter.TypeEncodedValue +org.apache.maven.surefire.api.testset.DirectoryScannerParameters +org.apache.maven.surefire.api.util.RunOrder +org.apache.maven.surefire.api.testset.RunOrderParameters +org.apache.maven.surefire.api.testset.TestArtifactInfo +org.apache.maven.surefire.api.testset.TestRequest +org.apache.maven.surefire.api.testset.TestFilter +org.apache.maven.surefire.api.testset.GenericTestPattern +org.apache.maven.surefire.api.testset.TestListResolver +java.util.Collections$SingletonSet +org.apache.maven.surefire.api.testset.IncludedExcludedPatterns +java.util.LinkedHashSet +java.util.Collections$1 +org.apache.maven.surefire.shared.utils.StringUtils +java.lang.IndexOutOfBoundsException +java.lang.StringIndexOutOfBoundsException +org.apache.maven.surefire.api.testset.ResolvedTest +org.apache.maven.surefire.api.testset.ResolvedTest$Type +org.apache.maven.surefire.api.testset.ResolvedTest$ClassMatcher +org.apache.maven.surefire.api.testset.ResolvedTest$MethodMatcher +org.apache.maven.surefire.api.report.ReporterConfiguration +org.apache.maven.surefire.api.booter.Shutdown +java.lang.Class$3 +jdk.internal.reflect.NativeMethodAccessorImpl +jdk.internal.reflect.DelegatingMethodAccessorImpl +org.apache.maven.surefire.booter.ProviderConfiguration +org.apache.maven.surefire.api.cli.CommandLineOption +org.apache.maven.surefire.api.booter.DumpErrorSingleton +org.apache.maven.surefire.api.util.internal.DumpFileUtils +java.lang.ProcessEnvironment +java.lang.ProcessEnvironment$ExternalData +java.lang.ProcessEnvironment$Variable +java.lang.ProcessEnvironment$Value +java.lang.ProcessEnvironment$StringEnvironment +java.lang.management.ManagementFactory +java.lang.IncompatibleClassChangeError +java.lang.NoSuchMethodError +java.lang.invoke.LambdaForm$DMH/0x00007faab4006000 +java.lang.management.ManagementFactory$$Lambda$4/0x00007faab40451f8 +java.lang.management.PlatformManagedObject +java.lang.management.RuntimeMXBean +java.lang.management.ManagementFactory$PlatformMBeanFinder +java.lang.management.ManagementFactory$PlatformMBeanFinder$1 +java.io.FilePermission +jdk.internal.access.JavaIOFilePermissionAccess +java.io.FilePermission$1 +sun.security.util.FilePermCompat +sun.security.util.SecurityProperties +java.security.Security +jdk.internal.access.JavaSecuritySystemConfiguratorAccess +java.security.Security$1 +java.security.Security$2 +java.security.SystemConfigurator +java.security.SystemConfigurator$1 +sun.security.util.PropertyExpander +java.net.URLConnection +sun.net.www.URLConnection +sun.net.www.protocol.file.FileURLConnection +sun.net.www.MessageHeader +sun.net.ProgressMonitor +sun.net.ProgressMeteringPolicy +sun.net.DefaultProgressMeteringPolicy +jdk.internal.access.JavaSecurityPropertiesAccess +java.security.Security$3 +sun.management.spi.PlatformMBeanProvider +java.util.ServiceLoader +java.util.ServiceLoader$ModuleServicesLookupIterator +java.util.ServiceLoader$LazyClassPathLookupIterator +java.util.ServiceLoader$2 +java.util.ServiceLoader$3 +java.util.concurrent.CopyOnWriteArrayList$COWIterator +com.sun.management.internal.PlatformMBeanProviderImpl +java.util.ServiceLoader$1 +java.util.ServiceLoader$Provider +java.util.ServiceLoader$ProviderImpl +com.sun.management.internal.PlatformMBeanProviderImpl$$Lambda$5/0x00007faab40468a0 +sun.management.spi.PlatformMBeanProvider$PlatformComponent +com.sun.management.internal.PlatformMBeanProviderImpl$1 +java.util.stream.BaseStream +java.util.stream.Stream +java.util.Spliterators +java.util.Spliterators$EmptySpliterator +java.util.Spliterator +java.util.Spliterators$EmptySpliterator$OfRef +java.util.Spliterator$OfPrimitive +java.util.Spliterator$OfInt +java.util.Spliterators$EmptySpliterator$OfInt +java.util.Spliterator$OfLong +java.util.Spliterators$EmptySpliterator$OfLong +java.util.Spliterator$OfDouble +java.util.Spliterators$EmptySpliterator$OfDouble +java.util.Spliterators$ArraySpliterator +java.util.stream.StreamSupport +java.util.stream.PipelineHelper +java.util.stream.AbstractPipeline +java.util.stream.ReferencePipeline +java.util.stream.ReferencePipeline$Head +java.util.stream.StreamOpFlag +java.util.stream.StreamOpFlag$Type +java.util.stream.StreamOpFlag$MaskBuilder +java.util.EnumMap +java.util.EnumMap$1 +sun.reflect.annotation.AnnotationParser +java.util.stream.Collectors +java.util.stream.Collector$Characteristics +java.util.EnumSet +java.util.RegularEnumSet +java.util.stream.Collector +java.util.stream.Collectors$CollectorImpl +java.util.stream.Collectors$$Lambda$30/0x800000041 +java.util.function.BiConsumer +java.lang.invoke.DirectMethodHandle$Interface +java.util.stream.Collectors$$Lambda$22/0x800000035 +java.util.function.BinaryOperator +java.util.stream.Collectors$$Lambda$25/0x80000003c +java.util.stream.Collectors$$Lambda$27/0x80000003e +java.util.stream.ReduceOps +java.util.stream.TerminalOp +java.util.stream.ReduceOps$ReduceOp +java.util.stream.ReduceOps$3 +java.util.stream.StreamShape +java.util.stream.ReduceOps$Box +java.util.function.Consumer +java.util.stream.Sink +java.util.stream.TerminalSink +java.util.stream.ReduceOps$AccumulatingSink +java.util.stream.ReduceOps$3ReducingSink +com.sun.management.internal.PlatformMBeanProviderImpl$2 +com.sun.management.internal.PlatformMBeanProviderImpl$3 +com.sun.management.internal.PlatformMBeanProviderImpl$4 +javax.management.DynamicMBean +com.sun.management.DiagnosticCommandMBean +javax.management.NotificationBroadcaster +javax.management.NotificationEmitter +sun.management.NotificationEmitterSupport +com.sun.management.internal.DiagnosticCommandImpl +sun.management.ManagementFactoryHelper +sun.management.VMManagement +sun.management.VMManagementImpl +com.sun.management.internal.PlatformMBeanProviderImpl$5 +java.util.Collections$UnmodifiableList +java.util.Collections$UnmodifiableRandomAccessList +jdk.management.jfr.internal.FlightRecorderMXBeanProvider +java.util.concurrent.Callable +java.util.Collections$EmptyEnumeration +java.lang.management.DefaultPlatformMBeanProvider +java.lang.management.DefaultPlatformMBeanProvider$1 +java.lang.management.DefaultPlatformMBeanProvider$2 +java.lang.management.DefaultPlatformMBeanProvider$3 +java.lang.management.DefaultPlatformMBeanProvider$4 +java.lang.management.DefaultPlatformMBeanProvider$5 +java.lang.management.DefaultPlatformMBeanProvider$6 +java.lang.management.DefaultPlatformMBeanProvider$7 +java.lang.management.DefaultPlatformMBeanProvider$8 +sun.management.ManagementFactoryHelper$LoggingMXBeanAccess +sun.management.ManagementFactoryHelper$LoggingMXBeanAccess$1 +java.util.logging.LogManager +java.lang.management.DefaultPlatformMBeanProvider$9 +java.lang.management.DefaultPlatformMBeanProvider$10 +java.lang.management.DefaultPlatformMBeanProvider$11 +jdk.management.jfr.FlightRecorderMXBean +jdk.management.jfr.internal.FlightRecorderMXBeanProvider$SingleMBeanComponent +java.util.Collections$SingletonList +java.util.HashMap$Values +java.util.HashMap$HashMapSpliterator +java.util.HashMap$ValueSpliterator +java.util.function.Predicate +java.lang.management.ManagementFactory$PlatformMBeanFinder$$Lambda$10/0x00007faab404c288 +java.util.stream.ReferencePipeline$StatelessOp +java.util.stream.ReferencePipeline$2 +java.lang.management.ManagementFactory$PlatformMBeanFinder$$Lambda$11/0x00007faab404c4e0 +java.util.stream.ReduceOps$2 +java.util.stream.ReduceOps$2ReducingSink +java.util.stream.Sink$ChainedReference +java.util.stream.ReferencePipeline$2$1 +sun.management.RuntimeImpl +java.util.Collections$SingletonMap +java.util.Collections$2 +java.lang.invoke.LambdaForm$DMH/0x00007faab4006400 +sun.management.spi.PlatformMBeanProvider$PlatformComponent$$Lambda$12/0x00007faab404d2d0 +sun.management.spi.PlatformMBeanProvider$PlatformComponent$$Lambda$13/0x00007faab404d528 +java.util.stream.ReferencePipeline$3 +java.util.stream.Collectors$$Lambda$14/0x00007faab404d770 +java.util.stream.Collectors$$Lambda$15/0x00007faab404d990 +java.util.stream.Collectors$$Lambda$16/0x00007faab404dbc0 +java.util.stream.ReferencePipeline$3$1 +sun.management.Util +java.lang.management.ManagementPermission +java.util.Arrays$ArrayList +java.util.Arrays$ArrayItr +org.apache.maven.surefire.booter.ClassLoaderConfiguration +org.apache.maven.surefire.booter.AbstractPathConfiguration +org.apache.maven.surefire.booter.ClasspathConfiguration +org.apache.maven.surefire.booter.Classpath +java.net.MalformedURLException +java.net.URLClassLoader +org.apache.maven.surefire.booter.IsolatedClassLoader +org.apache.maven.surefire.booter.SurefireExecutionException +org.apache.maven.surefire.booter.ProcessCheckerType +org.apache.maven.surefire.booter.StartupConfiguration +org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory +java.util.Spliterators$1Adapter +java.util.HashMap$ValueIterator +jdk.internal.module.Resources +jdk.internal.loader.BuiltinClassLoader$2 +jdk.internal.loader.BuiltinClassLoader$5 +java.lang.module.ModuleReader +jdk.internal.module.SystemModuleFinders$SystemModuleReader +jdk.internal.module.SystemModuleFinders$SystemImage +jdk.internal.jimage.ImageReaderFactory +java.nio.file.Paths +java.nio.file.FileSystems +java.nio.file.FileSystems$DefaultFileSystemHolder +java.nio.file.FileSystems$DefaultFileSystemHolder$1 +java.net.URI$Parser +jdk.internal.jimage.ImageReaderFactory$1 +jdk.internal.jimage.ImageReader +jdk.internal.jimage.BasicImageReader +jdk.internal.jimage.ImageReader$SharedImageReader +jdk.internal.jimage.BasicImageReader$1 +jdk.internal.jimage.NativeImageBuffer +jdk.internal.jimage.NativeImageBuffer$1 +jdk.internal.jimage.ImageHeader +java.nio.IntBuffer +java.nio.DirectIntBufferU +java.nio.DirectByteBufferR +java.nio.DirectIntBufferRU +jdk.internal.jimage.ImageStrings +jdk.internal.jimage.ImageStringsReader +jdk.internal.jimage.decompressor.Decompressor +jdk.internal.jimage.ImageLocation +java.util.Collections$EmptyIterator +jdk.internal.loader.BuiltinClassLoader$1 +java.lang.CompoundEnumeration +jdk.internal.loader.URLClassPath$1 +jdk.internal.loader.URLClassPath$FileLoader +java.util.SortedSet +java.util.NavigableSet +java.util.TreeSet +java.util.SortedMap +java.util.NavigableMap +java.util.TreeMap +java.util.TreeMap$Entry +java.util.TreeMap$KeySet +java.util.TreeMap$PrivateEntryIterator +java.util.TreeMap$KeyIterator +java.lang.Readable +java.io.Reader +java.io.BufferedReader +java.io.InputStreamReader +sun.nio.cs.StreamDecoder +java.nio.charset.CharsetDecoder +sun.nio.cs.UTF_8$Decoder +java.util.Vector +java.nio.CharBuffer +java.nio.HeapCharBuffer +java.nio.charset.CoderResult +java.util.AbstractSequentialList +java.util.LinkedList +java.util.LinkedList$Node +java.net.JarURLConnection +sun.net.www.protocol.jar.JarURLConnection +sun.net.www.protocol.jar.URLJarFile$URLJarFileCloseController +sun.net.www.protocol.jar.JarFileFactory +sun.net.www.protocol.jar.URLJarFile +sun.nio.fs.UnixFileKey +sun.net.www.protocol.jar.URLJarFile$URLJarFileEntry +sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream +java.util.LinkedHashMap$LinkedKeySet +java.util.LinkedHashMap$LinkedHashIterator +java.util.LinkedHashMap$LinkedKeyIterator +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory +org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory +org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder +org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder +org.apache.maven.surefire.api.util.internal.DaemonThreadFactory +java.util.concurrent.Executors +java.util.concurrent.Executors$DefaultThreadFactory +org.apache.maven.surefire.api.util.internal.DaemonThreadFactory$NamedThreadFactory +java.util.concurrent.AbstractExecutorService +java.util.concurrent.ThreadPoolExecutor +java.util.concurrent.ScheduledThreadPoolExecutor +java.util.concurrent.RejectedExecutionHandler +java.util.concurrent.ThreadPoolExecutor$AbortPolicy +java.util.concurrent.BlockingQueue +java.util.AbstractQueue +java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue +java.util.concurrent.Future +java.util.concurrent.RunnableFuture +java.util.concurrent.Delayed +java.util.concurrent.ScheduledFuture +java.util.concurrent.RunnableScheduledFuture +java.util.concurrent.locks.ReentrantLock$Sync +java.util.concurrent.locks.ReentrantLock$NonfairSync +java.util.concurrent.locks.Condition +java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject +org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory +java.net.URISyntaxException +java.util.concurrent.ExecutionException +java.net.SocketAddress +java.net.InetSocketAddress +java.nio.channels.Channel +java.nio.channels.AsynchronousChannel +java.nio.channels.AsynchronousByteChannel +org.apache.maven.surefire.booter.ForkedNodeArg +java.lang.UnsupportedOperationException +org.apache.maven.plugin.surefire.log.api.NullConsoleLogger +org.apache.maven.surefire.api.util.internal.Channels +org.apache.maven.surefire.api.util.internal.Channels$2 +org.apache.maven.surefire.api.util.internal.Channels$1 +java.nio.channels.WritableByteChannel +org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel +java.nio.channels.ReadableByteChannel +org.apache.maven.surefire.api.util.internal.AbstractNoninterruptibleWritableChannel +org.apache.maven.surefire.api.util.internal.Channels$4 +java.nio.channels.ClosedChannelException +java.nio.channels.NonWritableChannelException +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory$1 +java.util.concurrent.FutureTask +java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask +java.lang.invoke.VarHandles +java.lang.invoke.VarHandleInts$FieldInstanceReadOnly +java.lang.invoke.VarHandleInts$FieldInstanceReadWrite +java.lang.invoke.VarHandle$1 +jdk.internal.util.Preconditions$1 +java.lang.invoke.VarHandleGuards +java.lang.invoke.VarForm +java.lang.invoke.VarHandleReferences$FieldInstanceReadOnly +java.lang.invoke.VarHandleReferences$FieldInstanceReadWrite +java.util.concurrent.FutureTask$WaitNode +java.util.concurrent.Executors$RunnableAdapter +java.util.concurrent.ThreadPoolExecutor$Worker +java.lang.Thread$State +java.util.concurrent.TimeUnit$1 +java.time.temporal.TemporalUnit +java.time.temporal.ChronoUnit +java.time.temporal.TemporalAmount +java.time.Duration +java.math.BigInteger +org.apache.maven.surefire.api.stream.AbstractStreamEncoder +org.apache.maven.surefire.booter.stream.EventEncoder +org.apache.maven.surefire.booter.spi.EventChannelEncoder +java.lang.invoke.VarHandle$AccessDescriptor +java.lang.AssertionError +org.apache.maven.surefire.api.booter.ForkedProcessEventType +java.util.concurrent.ForkJoinPool$ManagedBlocker +java.util.concurrent.locks.AbstractQueuedSynchronizer$Node +java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode +org.apache.maven.surefire.api.report.ReportEntry +java.util.concurrent.atomic.AtomicBoolean +org.apache.maven.surefire.booter.spi.CommandChannelDecoder +org.apache.maven.surefire.api.stream.MalformedChannelException +org.apache.maven.surefire.api.stream.AbstractStreamDecoder +org.apache.maven.surefire.booter.stream.CommandDecoder +org.apache.maven.surefire.api.util.internal.AbstractNoninterruptibleReadableChannel +org.apache.maven.surefire.api.util.internal.Channels$3 +java.nio.channels.NonReadableChannelException +java.io.EOFException +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$MalformedFrameException +java.io.FileNotFoundException +org.apache.maven.surefire.api.stream.SegmentType +org.apache.maven.surefire.api.booter.Constants +java.nio.charset.StandardCharsets +sun.nio.cs.US_ASCII +sun.nio.cs.ISO_8859_1 +sun.nio.cs.UTF_16BE +sun.nio.cs.UTF_16LE +sun.nio.cs.UTF_16 +org.apache.maven.surefire.api.booter.MasterProcessCommand +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$Segment +org.apache.maven.surefire.booter.ForkedBooter$8 +org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils +java.lang.ApplicationShutdownHooks +java.lang.ApplicationShutdownHooks$1 +java.lang.Shutdown +java.lang.Shutdown$Lock +org.apache.maven.surefire.api.booter.ForkingReporterFactory +org.apache.maven.surefire.api.report.RunListener +org.apache.maven.surefire.api.report.TestOutputReceiver +org.apache.maven.surefire.api.report.TestReportListener +org.apache.maven.surefire.api.booter.ForkingRunListener +org.apache.maven.surefire.booter.CommandReader +org.apache.maven.surefire.api.testset.TestSetFailedException +java.util.concurrent.ConcurrentLinkedQueue +java.util.concurrent.ConcurrentLinkedQueue$Node +org.apache.maven.surefire.booter.CommandReader$CommandRunnable +java.util.concurrent.atomic.AtomicReference +java.util.concurrent.CountDownLatch +java.util.concurrent.CountDownLatch$Sync +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$Memento +org.apache.maven.surefire.booter.PpidChecker +org.apache.maven.surefire.booter.PpidChecker$ProcessInfoConsumer +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$BufferedStream +org.apache.maven.surefire.booter.PpidChecker$1 +org.apache.maven.surefire.booter.PpidChecker$2 +org.apache.maven.surefire.api.stream.AbstractStreamDecoder$StreamReadStatus +org.apache.maven.surefire.booter.stream.CommandDecoder$1 +java.lang.NoSuchFieldError +org.apache.maven.surefire.shared.lang3.SystemUtils +org.apache.maven.surefire.api.booter.Command +org.apache.maven.surefire.booter.CommandReader$1 +java.util.concurrent.ConcurrentLinkedQueue$Itr +org.apache.maven.surefire.shared.lang3.SystemProperties +org.apache.maven.surefire.shared.lang3.function.Suppliers +org.apache.maven.surefire.shared.lang3.function.Suppliers$$Lambda$17/0x00007faab4010000 +org.apache.maven.surefire.shared.lang3.StringUtils +java.util.regex.Pattern +java.util.regex.Pattern$Node +java.util.regex.Pattern$LastNode +java.util.regex.Pattern$GroupHead +java.util.regex.CharPredicates +java.lang.Character$Subset +java.lang.Character$UnicodeBlock +java.util.regex.Pattern$CharPredicate +java.lang.invoke.LambdaForm$DMH/0x00007faab4014000 +java.util.regex.CharPredicates$$Lambda$18/0x00007faab405bdc8 +java.util.regex.Pattern$BmpCharPredicate +java.util.regex.Pattern$CharProperty +java.util.regex.Pattern$Qtype +java.util.regex.Pattern$BmpCharProperty +java.util.regex.Pattern$CharPropertyGreedy +java.util.regex.Pattern$SliceNode +java.util.regex.Pattern$Slice +java.util.regex.Pattern$Begin +java.util.regex.Pattern$First +java.util.regex.Pattern$Start +java.util.regex.Pattern$StartS +java.util.regex.Pattern$TreeInfo +org.apache.maven.surefire.shared.lang3.JavaVersion +org.apache.maven.surefire.shared.lang3.SystemProperties$$Lambda$19/0x00007faab4010678 +org.apache.maven.surefire.shared.lang3.math.NumberUtils +java.lang.NumberFormatException +java.math.BigDecimal +jdk.internal.math.FloatingDecimal +jdk.internal.math.FloatingDecimal$BinaryToASCIIConverter +jdk.internal.math.FloatingDecimal$ExceptionalBinaryToASCIIBuffer +jdk.internal.math.FloatingDecimal$BinaryToASCIIBuffer +jdk.internal.math.FloatingDecimal$1 +jdk.internal.math.FloatingDecimal$ASCIIToBinaryConverter +jdk.internal.math.FloatingDecimal$PreparedASCIIToBinaryBuffer +jdk.internal.math.FloatingDecimal$ASCIIToBinaryBuffer +org.apache.maven.surefire.shared.lang3.SystemUtils$$Lambda$20/0x00007faab4010ca0 +java.util.regex.Pattern$GroupTail +java.util.regex.CharPredicates$$Lambda$16/0x800000024 +java.util.regex.Pattern$BmpCharPropertyGreedy +java.util.regex.Pattern$$Lambda$18/0x800000028 +java.util.regex.Pattern$Ques +java.util.regex.Pattern$BranchConn +java.util.regex.Pattern$Branch +java.util.regex.ASCII +java.util.regex.Pattern$Curly +java.util.regex.CharPredicates$$Lambda$17/0x800000025 +java.util.regex.Pattern$Dollar +java.util.regex.Pattern$BitClass +org.apache.maven.surefire.booter.ForkedBooter$4 +org.apache.maven.surefire.api.booter.BiProperty +org.apache.maven.surefire.booter.ForkedBooter$3 +org.apache.maven.surefire.booter.ForkedBooter$PingScheduler +org.apache.maven.surefire.api.provider.ProviderParameters +org.apache.maven.surefire.api.booter.BaseProviderFactory +org.apache.maven.surefire.api.util.DirectoryScanner +org.apache.maven.surefire.api.util.ScanResult +org.apache.maven.surefire.api.util.RunOrderCalculator +org.apache.maven.surefire.api.util.ReflectionUtils +org.apache.maven.surefire.api.util.SurefireReflectionException +java.lang.reflect.InvocationTargetException +java.lang.IllegalAccessException +org.apache.maven.surefire.api.provider.SurefireProvider +org.apache.maven.surefire.api.provider.AbstractProvider +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +org.junit.platform.launcher.Launcher +org.apache.maven.surefire.api.util.ScannerFilter +java.io.StringReader +java.io.UncheckedIOException +org.apache.maven.surefire.junitplatform.LazyLauncher +org.junit.platform.launcher.TagFilter +org.junit.platform.commons.JUnitException +org.junit.platform.commons.util.PreconditionViolationException +org.junit.platform.commons.PreconditionViolationException +org.junit.platform.engine.Filter +org.junit.platform.launcher.PostDiscoveryFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$24/0x00007faab4016200 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$25/0x00007faab4016440 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$26/0x00007faab4016678 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$27/0x00007faab40168b8 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$28/0x00007faab4016af0 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$29/0x00007faab4016d40 +org.apache.maven.surefire.junitplatform.TestMethodFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$30/0x00007faab40171f0 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$31/0x00007faab4017430 +org.junit.platform.launcher.EngineFilter +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$32/0x00007faab40178c0 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$33/0x00007faab4017b00 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$34/0x00007faab4017d38 +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$35/0x00007faab4015000 +org.junit.platform.launcher.TestExecutionListener +org.apache.maven.surefire.report.RunModeSetter +org.apache.maven.surefire.junitplatform.RunListenerAdapter +org.apache.maven.surefire.api.report.OutputReportEntry +org.apache.maven.surefire.api.report.TestSetReportEntry +org.apache.maven.surefire.api.report.StackTraceWriter +org.apache.maven.surefire.report.ClassMethodIndexer +org.apache.maven.surefire.api.report.RunMode +org.apache.maven.surefire.api.report.ConsoleOutputCapture +org.apache.maven.surefire.api.report.ConsoleOutputCapture$ForwardingPrintStream +org.apache.maven.surefire.api.report.ConsoleOutputCapture$NullOutputStream +java.util.logging.Logger +java.util.logging.Handler +java.util.logging.Level +java.util.logging.Level$KnownLevel +java.util.logging.Level$KnownLevel$$Lambda$13/0x800000021 +java.util.logging.Level$KnownLevel$$Lambda$14/0x800000022 +java.util.logging.Logger$LoggerBundle +java.util.logging.Logger$ConfigurationData +java.util.logging.LogManager$1 +java.util.logging.LogManager$LoggerContext +java.util.logging.LogManager$SystemLoggerContext +java.util.logging.LogManager$LogNode +java.util.Collections$SynchronizedMap +java.util.logging.LogManager$Cleaner +java.util.logging.LoggingPermission +sun.util.logging.internal.LoggingProviderImpl$LogManagerAccess +java.util.logging.LogManager$LoggingProviderAccess +java.lang.System$LoggerFinder +jdk.internal.logger.DefaultLoggerFinder +sun.util.logging.internal.LoggingProviderImpl +java.util.logging.LogManager$2 +java.util.logging.LogManager$RootLogger +java.util.logging.LogManager$LoggerWeakRef +java.lang.invoke.MethodHandleImpl$AsVarargsCollector +java.lang.invoke.BoundMethodHandle$Species_LL +java.lang.invoke.LambdaForm$MH/0x00007faab401c000 +java.util.logging.LogManager$VisitedLoggers +java.util.logging.LogManager$LoggerContext$1 +java.util.concurrent.ConcurrentHashMap$KeySetView +java.util.Collections$3 +java.util.concurrent.ConcurrentHashMap$KeyIterator +java.util.Hashtable$Enumerator +java.util.logging.Level$$Lambda$12/0x800000010 +java.util.ArrayList$ArrayListSpliterator +java.util.logging.Level$KnownLevel$$Lambda$15/0x800000023 +java.util.stream.ReferencePipeline$7 +java.util.stream.FindOps +java.util.stream.FindOps$FindSink +java.util.stream.FindOps$FindSink$OfRef +java.util.stream.FindOps$FindOp +java.util.stream.FindOps$FindSink$OfRef$$Lambda$40/0x80000004b +java.util.stream.FindOps$FindSink$OfRef$$Lambda$38/0x800000049 +java.util.stream.FindOps$FindSink$OfRef$$Lambda$39/0x80000004a +java.util.stream.FindOps$FindSink$OfRef$$Lambda$37/0x800000048 +java.util.stream.ReferencePipeline$7$1 +java.util.stream.Streams$AbstractStreamBuilderImpl +java.util.stream.Stream$Builder +java.util.stream.Streams$StreamBuilderImpl +java.util.stream.Streams +java.util.IdentityHashMap$Values +java.lang.System$Logger +sun.util.logging.PlatformLogger$Bridge +sun.util.logging.PlatformLogger$ConfigurableBridge +jdk.internal.logger.BootstrapLogger +jdk.internal.logger.BootstrapLogger$DetectBackend +jdk.internal.logger.BootstrapLogger$DetectBackend$1 +jdk.internal.logger.BootstrapLogger$LoggingBackend +jdk.internal.logger.BootstrapLogger$RedirectedLoggers +jdk.internal.logger.BootstrapLogger$BootstrapExecutors +java.util.logging.LogManager$4 +java.util.logging.Logger$SystemLoggerHelper +java.util.logging.Logger$SystemLoggerHelper$1 +jdk.internal.logger.DefaultLoggerFinder$1 +org.apache.maven.surefire.junitplatform.TestPlanScannerFilter +org.apache.maven.surefire.api.util.DefaultScanResult +jdk.internal.loader.URLClassPath$FileLoader$1 +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder +org.junit.platform.engine.ConfigurationParameters +org.junit.platform.engine.EngineDiscoveryRequest +org.junit.platform.launcher.LauncherDiscoveryRequest +org.junit.platform.engine.reporting.OutputDirectoryProvider +org.junit.platform.engine.DiscoverySelector +org.junit.platform.engine.discovery.DiscoverySelectors +org.junit.platform.commons.util.Preconditions +org.junit.platform.commons.util.StringUtils +org.junit.platform.commons.util.StringUtils$TwoPartSplitResult +java.util.regex.CharPredicates$$Lambda$44/0x00007faab405d900 +org.junit.platform.engine.discovery.ClassSelector +java.lang.invoke.LambdaForm$DMH/0x00007faab401c400 +org.junit.platform.commons.util.Preconditions$$Lambda$45/0x00007faab401a5e0 +org.junit.platform.commons.util.Preconditions$$Lambda$46/0x00007faab401a818 +java.lang.invoke.LambdaForm$DMH/0x00007faab401c800 +java.lang.invoke.DirectMethodHandle$Special +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$47/0x00007faab401aa50 +org.junit.platform.launcher.core.LauncherConfigurationParameters +org.junit.platform.commons.logging.LoggerFactory +org.junit.platform.commons.logging.Logger +org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger +org.junit.platform.launcher.core.LauncherConfigurationParameters$Builder +org.junit.platform.launcher.core.LauncherConfigurationParameters$Builder$$Lambda$48/0x00007faab401b790 +org.junit.platform.commons.util.CollectionUtils +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$2 +org.junit.platform.commons.util.ClassLoaderUtils +org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$3 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$49/0x00007faab401e498 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$50/0x00007faab401e6e0 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners +org.junit.platform.engine.EngineDiscoveryListener +org.junit.platform.launcher.LauncherDiscoveryListener +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType$$Lambda$51/0x00007faab401f180 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType$$Lambda$52/0x00007faab401f3a0 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$53/0x00007faab401f7b8 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$54/0x00007faab401fa10 +org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener +org.junit.platform.engine.EngineDiscoveryListener$1 +org.junit.platform.launcher.LauncherDiscoveryListener$1 +org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$$Lambda$55/0x00007faab401d4c0 +org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider +java.util.regex.Pattern$$Lambda$56/0x00007faab405df20 +java.util.regex.Pattern$CharPredicate$$Lambda$57/0x00007faab405e180 +java.util.regex.Pattern$CharPredicate$$Lambda$21/0x80000002d +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$59/0x00007faab401d928 +org.junit.platform.launcher.core.DefaultDiscoveryRequest +org.junit.platform.launcher.LauncherSession +org.junit.platform.launcher.core.LauncherFactory +org.junit.platform.launcher.core.LauncherConfig +org.junit.platform.launcher.core.LauncherConfig$Builder +org.junit.platform.launcher.core.DefaultLauncherConfig +org.junit.platform.launcher.core.DefaultLauncherSession +org.junit.platform.launcher.LauncherInterceptor +org.junit.platform.launcher.core.DefaultLauncherSession$1 +org.junit.platform.launcher.core.LauncherConfigurationParameters$$Lambda$60/0x00007faab4020dd0 +org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor +org.junit.platform.launcher.LauncherSessionListener +org.junit.platform.launcher.core.LauncherFactory$$Lambda$61/0x00007faab4021448 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore +org.junit.platform.launcher.core.LauncherFactory$$Lambda$62/0x00007faab4021898 +org.junit.platform.engine.support.store.NamespacedHierarchicalStoreException +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CloseAction +java.lang.invoke.LambdaForm$DMH/0x00007faab4024000 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CloseAction$$Lambda$63/0x00007faab4021f40 +java.util.stream.SliceOps +java.util.stream.ReferencePipeline$StatefulOp +java.util.stream.SliceOps$1 +org.junit.platform.launcher.core.DefaultLauncherSession$$Lambda$64/0x00007faab4022160 +java.util.stream.ReduceOps$1 +java.util.stream.ReduceOps$1ReducingSink +java.util.stream.SliceOps$1$1 +org.junit.platform.launcher.LauncherInterceptor$Invocation +java.lang.invoke.LambdaForm$DMH/0x00007faab4024400 +org.junit.platform.launcher.core.DefaultLauncherSession$$Lambda$65/0x00007faab40225a8 +org.junit.platform.launcher.core.ListenerRegistry +org.junit.platform.launcher.listeners.session.LauncherSessionListeners +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$66/0x00007faab4022c08 +org.junit.platform.launcher.core.ServiceLoaderRegistry +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$67/0x00007faab4023050 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$68/0x00007faab40232a0 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$69/0x00007faab40234e8 +org.junit.platform.commons.util.ServiceLoaderUtils +java.util.ServiceLoader$ProviderSpliterator +org.junit.platform.commons.util.ServiceLoaderUtils$$Lambda$70/0x00007faab4023948 +org.junit.platform.commons.util.ServiceLoaderUtils$$Lambda$71/0x00007faab4023ba0 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$72/0x00007faab4026000 +java.lang.invoke.LambdaForm$DMH/0x00007faab4024800 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$73/0x00007faab4026228 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$74/0x00007faab4026460 +org.junit.platform.launcher.LauncherSessionListener$1 +org.junit.platform.launcher.core.DelegatingLauncher +org.junit.platform.launcher.core.InterceptingLauncher +org.junit.platform.launcher.core.DefaultLauncherSession$$Lambda$75/0x00007faab4026db0 +org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry +org.junit.platform.engine.TestEngine +org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry$$Lambda$76/0x00007faab40271d8 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$77/0x00007faab4027400 +org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine +org.junit.jupiter.engine.JupiterTestEngine +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService +org.junit.jupiter.engine.config.JupiterConfiguration +org.junit.platform.engine.TestDescriptor +org.junit.platform.engine.support.hierarchical.EngineExecutionContext +org.junit.platform.launcher.core.LauncherFactory$$Lambda$78/0x00007faab4025400 +org.junit.platform.launcher.core.DefaultLauncher +org.junit.platform.launcher.TestPlan +org.junit.platform.launcher.core.InternalTestPlan +org.junit.platform.launcher.core.LauncherListenerRegistry +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$79/0x00007faab4024c00 +org.junit.platform.launcher.core.CompositeTestExecutionListener +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$80/0x00007faab40282a0 +org.junit.platform.launcher.core.EngineExecutionOrchestrator +org.junit.platform.engine.EngineExecutionListener +org.junit.platform.launcher.TestPlan$Visitor +org.junit.platform.launcher.core.DiscoveryIssueException +org.junit.platform.launcher.core.DefaultLauncher$$Lambda$81/0x00007faab4028d68 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator +org.junit.platform.launcher.core.EngineDiscoveryResultValidator +org.junit.platform.launcher.core.EngineIdValidator +org.junit.platform.launcher.core.EngineIdValidator$$Lambda$82/0x00007faab40295d0 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$83/0x00007faab40297f8 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$84/0x00007faab4029a30 +org.junit.platform.commons.util.ClassNamePatternFilterUtils +org.junit.platform.launcher.core.LauncherFactory$$Lambda$85/0x00007faab4029e70 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$86/0x00007faab402a0b0 +java.lang.invoke.LambdaForm$DMH/0x00007faab402c000 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$87/0x00007faab402a300 +org.junit.platform.launcher.core.ServiceLoaderRegistry$$Lambda$88/0x00007faab402a558 +org.junit.platform.launcher.listeners.UniqueIdTrackingListener +org.junit.platform.launcher.core.LauncherFactory$$Lambda$89/0x00007faab402aa40 +org.junit.platform.launcher.core.LauncherFactory$$Lambda$90/0x00007faab402ac78 +org.junit.platform.launcher.core.InterceptingLauncher$$Lambda$91/0x00007faab402aeb0 +org.junit.platform.launcher.core.LauncherPhase +org.junit.platform.engine.UniqueId +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$92/0x00007faab402b748 +org.junit.platform.launcher.core.DiscoveryIssueCollector +org.junit.platform.engine.TestSource +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener +org.junit.platform.launcher.core.DelegatingLauncherDiscoveryRequest +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$1 +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$93/0x00007faab402e830 +org.junit.platform.launcher.core.EngineFilterer +org.junit.platform.engine.FilterResult +org.junit.platform.launcher.core.EngineFilterer$$Lambda$94/0x00007faab402eeb0 +java.lang.invoke.LambdaForm$DMH/0x00007faab402c400 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$95/0x00007faab402f0f8 +java.util.stream.MatchOps$MatchKind +java.util.stream.MatchOps +java.util.stream.MatchOps$MatchOp +java.util.stream.MatchOps$BooleanTerminalSink +java.util.stream.MatchOps$$Lambda$96/0x00007faab4060150 +java.util.stream.MatchOps$1MatchSink +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$97/0x00007faab402f348 +org.junit.platform.engine.UniqueIdFormat +java.io.UnsupportedEncodingException +java.util.Formatter +java.util.regex.Pattern$$Lambda$19/0x800000029 +java.util.regex.Pattern$BmpCharPredicate$$Lambda$20/0x80000002b +java.util.Locale$Category +java.util.Formatter$Conversion +java.util.Formatter$FormatString +java.util.Formatter$FormatSpecifier +java.util.Formatter$Flags +java.util.Formatter$FixedString +java.util.Formattable +java.util.regex.Pattern$$Lambda$100/0x00007faab4060cd8 +java.lang.invoke.LambdaForm$DMH/0x00007faab402c800 +org.junit.platform.engine.UniqueIdFormat$$Lambda$101/0x00007faab402f790 +java.net.URLEncoder +java.util.BitSet +java.io.CharArrayWriter +org.junit.platform.engine.UniqueIdFormat$$Lambda$102/0x00007faab402f9d0 +org.junit.platform.engine.UniqueIdFormat$$Lambda$103/0x00007faab402fc10 +org.junit.platform.engine.UniqueIdFormat$$Lambda$104/0x00007faab402d000 +org.junit.platform.engine.UniqueIdFormat$$Lambda$105/0x00007faab402d240 +org.junit.platform.engine.UniqueIdFormat$$Lambda$106/0x00007faab402d480 +org.junit.platform.engine.UniqueId$Segment +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$107/0x00007faab402d8e0 +org.junit.jupiter.engine.config.CachingJupiterConfiguration +org.junit.jupiter.engine.config.DefaultJupiterConfiguration +org.junit.jupiter.api.parallel.ExecutionMode +org.junit.jupiter.api.TestInstance$Lifecycle +org.junit.jupiter.api.io.CleanupMode +org.junit.jupiter.api.extension.TestInstantiationAwareExtension$ExtensionContextScope +org.junit.jupiter.engine.config.EnumConfigurationParameterConverter +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter +org.junit.jupiter.api.DisplayNameGenerator +org.junit.jupiter.api.MethodOrderer +org.junit.jupiter.api.ClassOrderer +org.junit.jupiter.api.io.TempDirFactory +org.junit.platform.engine.support.hierarchical.Node +org.junit.platform.engine.support.descriptor.AbstractTestDescriptor +org.junit.platform.engine.support.descriptor.EngineDescriptor +org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor +org.junit.jupiter.engine.extension.ExtensionRegistry +org.junit.jupiter.api.extension.ExtensionContext +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver +org.junit.platform.engine.support.discovery.SelectorResolver +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$InitializationContext +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$Builder +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$108/0x00007faab40330b8 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$Builder$$Lambda$109/0x00007faab40332f8 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$110/0x00007faab4033540 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$111/0x00007faab4033780 +org.junit.platform.engine.TestDescriptor$Visitor +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$112/0x00007faab4033bc0 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter +org.junit.platform.engine.DiscoveryIssue +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$113/0x00007faab4034200 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$114/0x00007faab4034448 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$DefaultInitializationContext +org.junit.platform.engine.DiscoveryFilter +org.junit.platform.engine.discovery.ClassNameFilter +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$115/0x00007faab4034d00 +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$116/0x00007faab4034f58 +org.junit.platform.engine.discovery.PackageNameFilter +org.junit.platform.engine.CompositeFilter +org.junit.platform.engine.CompositeFilter$1 +org.junit.platform.engine.CompositeFilter$1$$Lambda$117/0x00007faab4035818 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$$Lambda$118/0x00007faab4035a68 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver$$Lambda$119/0x00007faab4035cb0 +java.util.stream.Collectors$$Lambda$120/0x00007faab4061750 +java.util.stream.Collectors$$Lambda$121/0x00007faab4061980 +org.junit.platform.engine.support.discovery.ClassContainerSelectorResolver +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$122/0x00007faab4036400 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$123/0x00007faab4036650 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$124/0x00007faab40368a0 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$125/0x00007faab4036af8 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod +org.junit.jupiter.engine.discovery.predicates.IsTestMethod +org.junit.jupiter.api.Test +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition +org.junit.jupiter.engine.discovery.predicates.IsTestMethod$$Lambda$126/0x00007faab4037620 +org.junit.platform.commons.support.ModifierSupport +java.lang.invoke.LambdaForm$DMH/0x00007faab4038000 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$127/0x00007faab4037a58 +java.lang.invoke.MethodHandle$1 +java.lang.invoke.LambdaForm$DMH/0x00007faab4038400 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$128/0x00007faab4037ca8 +java.lang.invoke.LambdaForm$DMH/0x00007faab4038800 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$129/0x00007faab403c000 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$130/0x00007faab403c258 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$131/0x00007faab403c4a8 +java.lang.invoke.LambdaForm$DMH/0x00007faab4038c00 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition$$Lambda$132/0x00007faab403c6f0 +org.junit.platform.commons.util.ReflectionUtils +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$133/0x00007faab403cb50 +org.junit.jupiter.engine.discovery.predicates.IsTestableMethod$$Lambda$134/0x00007faab403cda0 +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod +org.junit.jupiter.api.DynamicNode +java.util.regex.MatchResult +java.util.regex.Matcher +java.util.regex.IntHashSet +org.junit.jupiter.api.TestFactory +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod$$Lambda$135/0x00007faab403d670 +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod$$Lambda$136/0x00007faab403d8a0 +org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod$$Lambda$137/0x00007faab403daf8 +java.util.function.Predicate$$Lambda$138/0x00007faab4062210 +org.junit.jupiter.engine.discovery.predicates.IsTestTemplateMethod +org.junit.jupiter.api.TestTemplate +org.junit.jupiter.engine.discovery.predicates.IsTestTemplateMethod$$Lambda$139/0x00007faab403e1a8 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$140/0x00007faab403e3d8 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$141/0x00007faab403e628 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$142/0x00007faab403e870 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$143/0x00007faab403eac0 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$144/0x00007faab403ed00 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$145/0x00007faab403ef50 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$146/0x00007faab403f190 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$147/0x00007faab403f3e0 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$148/0x00007faab403f620 +org.junit.jupiter.engine.discovery.predicates.TestClassPredicates$$Lambda$149/0x00007faab403f870 +org.junit.jupiter.engine.discovery.ClassSelectorResolver +org.junit.jupiter.engine.descriptor.ResourceLockAware +org.junit.jupiter.engine.descriptor.TestClassAware +org.junit.jupiter.engine.descriptor.Validatable +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor +org.junit.jupiter.engine.descriptor.ClassTestDescriptor +org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor +org.junit.jupiter.api.extension.ClassTemplateInvocationContext +org.junit.jupiter.engine.descriptor.Filterable +org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor +org.junit.jupiter.engine.discovery.MethodSelectorResolver +org.junit.jupiter.engine.discovery.MethodFinder +java.util.regex.Pattern$$Lambda$150/0x00007faab4062468 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$TestDescriptorFactory +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor +org.junit.jupiter.engine.extension.ExtensionRegistrar +java.lang.invoke.LambdaForm$DMH/0x00007faab4084000 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$151/0x00007faab4080b00 +org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor +org.junit.jupiter.engine.descriptor.DynamicNodeTestDescriptor +org.junit.jupiter.engine.descriptor.DynamicContainerTestDescriptor +org.junit.jupiter.engine.descriptor.DynamicTestTestDescriptor +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$152/0x00007faab4082038 +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$153/0x00007faab40827f0 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor +org.junit.jupiter.engine.discovery.ClassOrderingVisitor +org.junit.jupiter.api.ClassOrdererContext +org.junit.platform.commons.util.LruCache +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$154/0x00007faab40836f0 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$155/0x00007faab4083938 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$156/0x00007faab4083b78 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$157/0x00007faab4086000 +org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter$$Lambda$158/0x00007faab4083dc8 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$MessageGenerator +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer$$Lambda$159/0x00007faab4086660 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer$$Lambda$160/0x00007faab4086880 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$161/0x00007faab4086aa0 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$162/0x00007faab4086cf0 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor +org.junit.jupiter.api.MethodOrdererContext +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$163/0x00007faab4087378 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$164/0x00007faab40875c8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$165/0x00007faab4087808 +java.lang.invoke.LambdaForm$MH/0x00007faab4084400 +java.util.Comparator$$Lambda$166/0x00007faab40628c0 +java.util.Collections$ReverseComparator +java.util.Comparators$NaturalOrderComparator +java.util.Collections$ReverseComparator2 +java.util.function.UnaryOperator +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$167/0x00007faab4087a50 +org.junit.jupiter.engine.discovery.DiscoverySelectorResolver$$Lambda$168/0x00007faab4087cb0 +org.junit.platform.engine.CompositeTestDescriptorVisitor +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution +org.junit.platform.engine.support.discovery.SelectorResolver$Context +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$DefaultContext +org.junit.platform.engine.support.discovery.SelectorResolver$Match +org.junit.platform.engine.support.discovery.SelectorResolver$Match$$Lambda$169/0x00007faab4085ab8 +org.junit.platform.engine.support.discovery.SelectorResolver$Match$Type +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$170/0x00007faab40849f8 +org.junit.platform.launcher.core.DefaultDiscoveryRequest$$Lambda$171/0x00007faab4084c50 +java.util.ArrayDeque$$Lambda$172/0x00007faab4063970 +org.junit.platform.engine.discovery.UniqueIdSelector +org.junit.platform.engine.support.discovery.SelectorResolver$Resolution +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$173/0x00007faab4088460 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$174/0x00007faab40886a8 +org.junit.platform.engine.discovery.ClasspathResourceSelector +org.junit.platform.engine.discovery.ClasspathRootSelector +org.junit.platform.commons.support.ReflectionSupport +org.junit.platform.commons.util.ClasspathScannerLoader +org.junit.platform.commons.support.scanning.ClasspathScanner +java.util.Spliterators$IteratorSpliterator +jdk.internal.misc.ScopedMemoryAccess$Scope +org.junit.platform.commons.support.scanning.DefaultClasspathScanner +java.nio.file.FileVisitor +org.junit.platform.commons.util.ClasspathScannerLoader$$Lambda$175/0x00007faab40895e8 +org.junit.platform.commons.function.Try +java.lang.invoke.LambdaForm$DMH/0x00007faab408c000 +org.junit.platform.commons.util.ClasspathScannerLoader$$Lambda$176/0x00007faab4089a58 +java.lang.invoke.LambdaForm$DMH/0x00007faab408c400 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$177/0x00007faab4089c88 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$178/0x00007faab4089ec0 +org.junit.platform.commons.function.Try$Failure +org.junit.platform.commons.function.Try$Success +org.junit.platform.commons.function.Try$$Lambda$179/0x00007faab408a598 +java.util.regex.Pattern$1 +org.junit.platform.engine.discovery.ClassSelector$$Lambda$180/0x00007faab408a7c0 +org.junit.jupiter.api.Nested +org.junit.platform.commons.support.AnnotationSupport +org.junit.platform.commons.util.AnnotationUtils +java.lang.annotation.Inherited +sun.reflect.generics.parser.SignatureParser +sun.reflect.generics.tree.Tree +sun.reflect.generics.tree.TypeTree +sun.reflect.generics.tree.TypeArgument +sun.reflect.generics.tree.ReturnType +sun.reflect.generics.tree.TypeSignature +sun.reflect.generics.tree.BaseType +sun.reflect.generics.tree.FieldTypeSignature +sun.reflect.generics.tree.SimpleClassTypeSignature +sun.reflect.generics.tree.ClassTypeSignature +sun.reflect.generics.scope.Scope +sun.reflect.generics.scope.AbstractScope +sun.reflect.generics.scope.ClassScope +sun.reflect.generics.factory.GenericsFactory +sun.reflect.generics.factory.CoreReflectionFactory +sun.reflect.generics.visitor.TypeTreeVisitor +sun.reflect.generics.visitor.Reifier +java.lang.annotation.Target +java.lang.reflect.GenericArrayType +sun.reflect.annotation.AnnotationType +sun.reflect.annotation.AnnotationType$1 +java.lang.annotation.ElementType +java.lang.annotation.Retention +java.lang.annotation.Documented +java.lang.annotation.RetentionPolicy +sun.reflect.annotation.ExceptionProxy +sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy +sun.reflect.annotation.AnnotationParser$1 +java.lang.reflect.InvocationHandler +sun.reflect.annotation.AnnotationInvocationHandler +java.lang.reflect.Proxy +java.lang.ClassValue +java.lang.reflect.Proxy$1 +java.lang.ClassValue$Entry +java.lang.ClassValue$Identity +java.lang.ClassValue$Version +jdk.internal.loader.AbstractClassLoaderValue$Sub +java.lang.reflect.Proxy$$Lambda$181/0x00007faab406bf50 +java.lang.reflect.Proxy$ProxyBuilder +java.lang.PublicMethods +java.util.LinkedHashMap$LinkedValues +java.util.LinkedHashMap$LinkedValueIterator +java.lang.reflect.Proxy$ProxyBuilder$$Lambda$182/0x00007faab406cb68 +java.lang.module.ModuleDescriptor$Modifier +java.lang.module.ModuleDescriptor$Builder +jdk.internal.module.Checks +java.lang.module.ModuleDescriptor$Builder$$Lambda$1/0x800000002 +java.lang.reflect.Proxy$$Lambda$184/0x00007faab406cd98 +java.lang.reflect.ProxyGenerator +java.lang.reflect.ProxyGenerator$ProxyMethod +java.util.StringJoiner +java.lang.reflect.ProxyGenerator$$Lambda$185/0x00007faab406d4e8 +java.lang.reflect.ProxyGenerator$$Lambda$186/0x00007faab406d728 +java.lang.reflect.ProxyGenerator$PrimitiveTypeInfo +jdk.internal.org.objectweb.asm.Edge +jdk.proxy1.$Proxy0 +java.lang.reflect.Proxy$ProxyBuilder$1 +sun.reflect.annotation.AnnotationParser$$Lambda$187/0x00007faab406e218 +java.lang.invoke.LambdaForm$DMH/0x00007faab408c800 +jdk.proxy1.$Proxy1 +jdk.proxy1.$Proxy2 +org.apiguardian.api.API +org.apiguardian.api.API$Status +jdk.proxy2.$Proxy3 +java.lang.reflect.UndeclaredThrowableException +java.lang.Class$AnnotationData +org.junit.platform.commons.util.KotlinReflectionUtils +org.junit.platform.commons.function.Try$Transformer +org.junit.platform.commons.util.KotlinReflectionUtils$$Lambda$188/0x00007faab408bcc8 +org.junit.jupiter.api.ClassTemplate +jdk.proxy1.$Proxy4 +org.junit.platform.commons.annotation.Testable +jdk.proxy2.$Proxy5 +org.junit.platform.commons.util.ReflectionUtils$HierarchyTraversalMode +org.junit.platform.commons.util.ReflectionUtils$$Lambda$189/0x00007faab408ea90 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$190/0x00007faab408ece0 +com.amazonaws.services.lambda.runtime.events.SQSEvent +com.amazonaws.services.lambda.runtime.events.KinesisEvent +com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent +com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse +com.amazonaws.services.lambda.runtime.RequestHandler +software.amazon.lambda.powertools.validation.handlers.SQSWithWrongEnvelopeHandler +org.junit.platform.commons.util.ReflectionUtils$$Lambda$191/0x00007faab408fc88 +java.util.Arrays$LegacyMergeSort +java.util.TimSort +org.junit.jupiter.params.ParameterizedTest +org.junit.jupiter.params.ArgumentCountValidationMode +org.junit.jupiter.api.extension.ExtendWith +jdk.proxy2.$Proxy6 +com.amazonaws.services.lambda.runtime.tests.annotations.Event +org.junit.jupiter.params.provider.ArgumentsSource +jdk.proxy2.$Proxy7 +java.util.LinkedHashMap$LinkedEntrySet +java.util.LinkedHashMap$LinkedEntryIterator +jdk.proxy2.$Proxy8 +java.lang.annotation.Repeatable +sun.reflect.annotation.AnnotationParser$$Lambda$192/0x00007faab406f5a0 +org.junit.jupiter.api.extension.Extension +org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider +org.junit.jupiter.params.ParameterizedInvocationContextProvider +org.junit.jupiter.params.ParameterizedTestExtension +jdk.proxy2.$Proxy9 +jdk.internal.reflect.ClassFileConstants +jdk.internal.reflect.AccessorGenerator +jdk.internal.reflect.MethodAccessorGenerator +jdk.internal.reflect.ByteVectorFactory +jdk.internal.reflect.ByteVector +jdk.internal.reflect.ByteVectorImpl +jdk.internal.reflect.ClassFileAssembler +jdk.internal.reflect.UTF8 +jdk.internal.reflect.Label +jdk.internal.reflect.Label$PatchInfo +jdk.internal.reflect.MethodAccessorGenerator$1 +jdk.internal.reflect.ClassDefiner +jdk.internal.reflect.ClassDefiner$1 +jdk.internal.reflect.GeneratedConstructorAccessor1 +java.lang.Class$1 +jdk.internal.reflect.BootstrapConstructorAccessorImpl +org.junit.jupiter.api.extension.Extensions +jdk.proxy1.$Proxy10 +sun.reflect.annotation.AnnotationInvocationHandler$1 +org.junit.jupiter.params.provider.ArgumentsProvider +org.junit.jupiter.params.support.AnnotationConsumer +com.amazonaws.services.lambda.runtime.tests.EventArgumentsProvider +jdk.proxy2.$Proxy11 +org.junit.jupiter.params.provider.ArgumentsSources +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$193/0x00007faab40919f0 +org.junit.jupiter.api.extension.ExtensionConfigurationException +org.junit.jupiter.api.extension.TestInstanceFactoryContext +org.junit.jupiter.api.extension.TestInstantiationAwareExtension +org.junit.jupiter.api.extension.TestInstantiationException +org.junit.jupiter.engine.execution.ConditionEvaluator +org.junit.jupiter.engine.execution.ConditionEvaluationException +org.junit.jupiter.api.extension.ConditionEvaluationResult +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker +org.junit.jupiter.api.extension.ReflectiveInvocationContext +org.junit.jupiter.api.extension.InvocationInterceptor$Invocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain +org.junit.jupiter.engine.descriptor.DisplayNameUtils +org.junit.jupiter.api.DisplayNameGenerator$Standard +org.junit.jupiter.api.DisplayNameGenerator$Simple +org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores +org.junit.jupiter.api.DisplayNameGenerator$IndicativeSentences +org.junit.jupiter.api.DisplayNameGenerator$IndicativeSentences$$Lambda$194/0x00007faab4096000 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$195/0x00007faab4096250 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$196/0x00007faab4096470 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$197/0x00007faab40966a8 +org.junit.platform.engine.support.descriptor.ClassSource +org.junit.jupiter.api.DisplayName +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$198/0x00007faab4096cf0 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$199/0x00007faab4096f30 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$200/0x00007faab4097180 +org.junit.jupiter.api.DisplayNameGeneration +java.util.AbstractList$Itr +java.util.AbstractList$ListItr +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$201/0x00007faab40975c0 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$202/0x00007faab4097800 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$203/0x00007faab4097a40 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$204/0x00007faab4097c88 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$205/0x00007faab4095000 +org.junit.jupiter.engine.config.DefaultJupiterConfiguration$$Lambda$206/0x00007faab4095248 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo$$Lambda$207/0x00007faab4095678 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo$$Lambda$208/0x00007faab40958a0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo$$Lambda$209/0x00007faab4095ac8 +org.junit.jupiter.api.Tag +org.junit.jupiter.api.Tags +org.junit.platform.commons.util.AnnotationUtils$$Lambda$210/0x00007faab4094a00 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$211/0x00007faab4094c28 +java.lang.invoke.LambdaForm$DMH/0x00007faab4094400 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$212/0x00007faab4098000 +org.junit.platform.engine.TestTag +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$213/0x00007faab4098468 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$214/0x00007faab40986a8 +org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$$Lambda$215/0x00007faab40988c8 +java.lang.invoke.LambdaForm$DMH/0x00007faab409c000 +java.util.function.Function$$Lambda$216/0x00007faab40722d0 +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils +org.junit.jupiter.api.TestInstance +jdk.internal.reflect.GeneratedConstructorAccessor2 +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils$$Lambda$217/0x00007faab4098f10 +org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils$$Lambda$218/0x00007faab4099150 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$219/0x00007faab4099378 +java.lang.invoke.LambdaForm$DMH/0x00007faab409c800 +org.junit.jupiter.engine.config.EnumConfigurationParameterConverter$$Lambda$220/0x00007faab40997b8 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$1 +org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$DefaultExclusiveResourceCollector +org.junit.jupiter.api.parallel.ResourceLock +jdk.internal.reflect.GeneratedConstructorAccessor3 +org.junit.jupiter.api.parallel.ResourceLocks +jdk.internal.reflect.GeneratedConstructorAccessor4 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$LifecycleMethods +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$221/0x00007faab409a6a0 +java.lang.invoke.LambdaForm$DMH/0x00007faab409d400 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$$Lambda$222/0x00007faab409a8d8 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils +org.junit.jupiter.api.BeforeAll +org.junit.platform.commons.support.HierarchyTraversalMode +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$223/0x00007faab409b368 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$224/0x00007faab409b5b0 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$225/0x00007faab409b800 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$226/0x00007faab409ba48 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$227/0x00007faab409bca0 +java.util.function.IntFunction +org.junit.platform.commons.util.ReflectionUtils$$Lambda$228/0x00007faab409e000 +java.util.stream.Nodes +java.util.stream.Node +java.util.stream.Nodes$EmptyNode +java.util.stream.Nodes$EmptyNode$OfRef +java.util.stream.Node$OfPrimitive +java.util.stream.Node$OfInt +java.util.stream.Nodes$EmptyNode$OfInt +java.util.stream.Node$OfLong +java.util.stream.Nodes$EmptyNode$OfLong +java.util.stream.Node$OfDouble +java.util.stream.Nodes$EmptyNode$OfDouble +java.util.stream.Node$Builder +java.util.stream.AbstractSpinedBuffer +java.util.stream.SpinedBuffer +java.util.stream.Nodes$SpinedNodeBuilder +org.junit.platform.commons.util.ReflectionUtils$$Lambda$229/0x00007faab409e220 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$230/0x00007faab409e478 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$231/0x00007faab409e698 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$232/0x00007faab409e8f0 +java.util.stream.DistinctOps +java.util.stream.DistinctOps$1 +org.junit.platform.commons.util.CollectionUtils$$Lambda$233/0x00007faab409eb10 +java.util.stream.DistinctOps$1$2 +jdk.proxy2.$Proxy12 +org.junit.jupiter.params.provider.MethodSource +jdk.proxy2.$Proxy13 +org.junit.jupiter.params.provider.MethodSources +org.junit.jupiter.params.provider.AnnotationBasedArgumentsProvider +org.junit.jupiter.params.provider.MethodArgumentsProvider +software.amazon.lambda.powertools.validation.internal.ResponseEventsArgumentsProvider +org.junit.jupiter.api.BeforeEach +jdk.proxy2.$Proxy14 +software.amazon.lambda.powertools.validation.internal.HandledResponseEventsArgumentsProvider +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$234/0x00007faab409dc88 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$235/0x00007faab40a0000 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$236/0x00007faab40a0250 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition$$Lambda$237/0x00007faab40a0498 +java.util.stream.ReferencePipeline$15 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$238/0x00007faab40a06d0 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$239/0x00007faab40a0918 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$240/0x00007faab40a0b68 +org.junit.platform.engine.support.discovery.DiscoveryIssueReporter$Condition$$Lambda$241/0x00007faab40a0db0 +java.util.stream.ReferencePipeline$15$1 +org.junit.jupiter.api.AfterAll +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$242/0x00007faab40a1208 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$243/0x00007faab40a1450 +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$244/0x00007faab40a16a0 +org.junit.jupiter.api.AfterEach +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$245/0x00007faab40a1ae8 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$246/0x00007faab40a1d30 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$247/0x00007faab40a1f58 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$248/0x00007faab40a2180 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$249/0x00007faab40a23c8 +org.junit.platform.engine.SelectorResolutionResult +org.junit.platform.engine.SelectorResolutionResult$Status +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$250/0x00007faab40a2c60 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$251/0x00007faab40a2e98 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$252/0x00007faab40a30e8 +java.util.stream.ForEachOps +java.util.stream.ForEachOps$ForEachOp +java.util.stream.ForEachOps$ForEachOp$OfRef +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$253/0x00007faab40a3320 +org.junit.platform.commons.util.ReflectionUtils$CycleErrorHandling +org.junit.platform.commons.util.ReflectionUtils$CycleErrorHandling$1 +org.junit.platform.commons.util.ReflectionUtils$CycleErrorHandling$2 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$254/0x00007faab40a3e50 +java.lang.invoke.LambdaForm$DMH/0x00007faab40a8000 +java.util.function.Predicate$$Lambda$255/0x00007faab40754b8 +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$256/0x00007faab40a4088 +java.util.function.Predicate$$Lambda$257/0x00007faab4075710 +java.util.stream.Streams$ConcatSpliterator +java.util.stream.Streams$ConcatSpliterator$OfRef +java.util.stream.Streams$2 +org.junit.platform.engine.discovery.NestedClassSelector +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$258/0x00007faab40a4530 +java.util.stream.AbstractPipeline$$Lambda$259/0x00007faab40760d8 +java.util.stream.StreamSpliterators$AbstractWrappingSpliterator +java.util.stream.StreamSpliterators$WrappingSpliterator +org.junit.jupiter.engine.discovery.ClassSelectorResolver$$Lambda$260/0x00007faab40a4778 +java.util.stream.StreamSpliterators +java.util.stream.StreamSpliterators$WrappingSpliterator$$Lambda$261/0x00007faab4076a38 +org.junit.platform.engine.discovery.MethodSelector +org.junit.platform.engine.discovery.MethodSelector$$Lambda$262/0x00007faab40a4c08 +org.junit.platform.commons.util.ClassUtils +org.junit.platform.commons.util.ClassUtils$$Lambda$263/0x00007faab40a5050 +java.util.stream.Collectors$$Lambda$31/0x800000042 +java.util.stream.Collectors$$Lambda$23/0x80000003a +java.util.stream.Collectors$$Lambda$26/0x80000003d +java.util.stream.Collectors$$Lambda$28/0x80000003f +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$268/0x00007faab40a5298 +org.junit.platform.engine.discovery.IterationSelector +org.junit.platform.engine.discovery.DirectorySelector +org.junit.platform.engine.discovery.FileSelector +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$269/0x00007faab40a5ba8 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$270/0x00007faab40a5dd0 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$271/0x00007faab40a6000 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$272/0x00007faab40a6248 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$273/0x00007faab40a6498 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$274/0x00007faab40a66d8 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$275/0x00007faab40a6920 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$276/0x00007faab40a6b48 +org.junit.platform.commons.util.ClassUtils$$Lambda$277/0x00007faab40a6d90 +org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$$Lambda$278/0x00007faab40a6fd0 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$279/0x00007faab40a71f8 +org.junit.jupiter.api.DisplayNameGenerator$$Lambda$280/0x00007faab40a7430 +org.junit.platform.engine.support.descriptor.MethodSource +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo$$Lambda$281/0x00007faab40a7aa8 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo$$Lambda$282/0x00007faab40a7cd0 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo$$Lambda$283/0x00007faab40ac000 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$284/0x00007faab40ac238 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$285/0x00007faab40ac478 +org.junit.platform.commons.util.AnnotationUtils$$Lambda$286/0x00007faab40ac6c8 +java.util.function.BiPredicate +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter$WithoutIndexFiltering +org.junit.jupiter.engine.descriptor.DynamicDescendantFilter$Mode +org.junit.jupiter.engine.discovery.MethodSelectorResolver$$Lambda$287/0x00007faab40ad288 +java.util.HashMap$KeySpliterator +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$VoidMethodInterceptorCall +org.junit.jupiter.api.extension.InvocationInterceptor +java.lang.invoke.LambdaForm$DMH/0x00007faab40a8400 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$288/0x00007faab40ad8b0 +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$$Lambda$289/0x00007faab40adcd0 +org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$$Lambda$290/0x00007faab40adef8 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$291/0x00007faab40ae130 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$292/0x00007faab40ae368 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$293/0x00007faab40ae5a8 +org.junit.jupiter.api.ClassDescriptor +org.junit.jupiter.engine.discovery.AbstractAnnotatedDescriptorWrapper +org.junit.jupiter.engine.discovery.DefaultClassDescriptor +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$294/0x00007faab40aee58 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$295/0x00007faab40af098 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$296/0x00007faab40af2f0 +org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$$Lambda$297/0x00007faab40af538 +org.junit.jupiter.api.Order +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$298/0x00007faab40af970 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$299/0x00007faab40afba8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$300/0x00007faab40aa000 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$301/0x00007faab40aa238 +org.junit.platform.engine.TestDescriptor$$Lambda$302/0x00007faab40aa478 +org.junit.jupiter.api.TestClassOrder +jdk.internal.reflect.GeneratedConstructorAccessor5 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$303/0x00007faab40aa6b0 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$304/0x00007faab40aa8f0 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$305/0x00007faab40aab30 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$306/0x00007faab40aad78 +org.junit.jupiter.engine.discovery.ClassOrderingVisitor$$Lambda$307/0x00007faab40aafa0 +org.junit.jupiter.api.TestMethodOrder +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$308/0x00007faab40ab3e0 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$309/0x00007faab40ab620 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$310/0x00007faab40ab860 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$311/0x00007faab40abaa0 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$312/0x00007faab40abcc8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$313/0x00007faab40a9000 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$314/0x00007faab40a9248 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$315/0x00007faab40a9468 +org.junit.jupiter.api.MethodDescriptor +org.junit.jupiter.engine.discovery.DefaultMethodDescriptor +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$316/0x00007faab40a9af8 +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$317/0x00007faab40a9d38 +org.junit.platform.engine.support.hierarchical.Node$ExecutionMode +org.junit.jupiter.engine.discovery.MethodOrderingVisitor$$Lambda$318/0x00007faab40b0000 +org.junit.jupiter.engine.descriptor.Validatable$$Lambda$319/0x00007faab40b0238 +org.junit.jupiter.api.extension.ClassTemplateInvocationLifecycleMethod +org.junit.jupiter.engine.descriptor.LifecycleMethodUtils$$Lambda$320/0x00007faab40b0670 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$321/0x00007faab40b08a8 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$322/0x00007faab40b0ad0 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$323/0x00007faab40b0cf8 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$324/0x00007faab40b0f38 +org.junit.jupiter.engine.descriptor.DisplayNameUtils$$Lambda$325/0x00007faab40b1188 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$326/0x00007faab40b13c0 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$327/0x00007faab40b15e8 +org.junit.platform.launcher.core.EngineDiscoveryResultValidator$$Lambda$328/0x00007faab40b1810 +org.junit.platform.engine.TestDescriptor$Type +org.junit.platform.launcher.core.EngineDiscoveryResultValidator$$Lambda$329/0x00007faab40b1e78 +org.junit.platform.launcher.EngineDiscoveryResult +org.junit.platform.launcher.EngineDiscoveryResult$Status +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$330/0x00007faab40b2700 +java.util.Collections$UnmodifiableList$1 +java.util.ArrayList$ListItr +org.junit.platform.commons.util.ExceptionUtils +java.io.StringWriter +org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener$$Lambda$331/0x00007faab40b2b40 +org.junit.platform.launcher.core.DiscoveryIssueNotifier +org.junit.platform.engine.DiscoveryIssue$Severity +org.junit.platform.launcher.core.LauncherDiscoveryResult$EngineResultInfo +org.junit.platform.launcher.core.EngineFilterer$$Lambda$332/0x00007faab40b3420 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$333/0x00007faab40b3660 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$334/0x00007faab40b38b0 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$335/0x00007faab40b3af0 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$336/0x00007faab40b3d30 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$337/0x00007faab40b3f88 +org.junit.platform.launcher.core.EngineFilterer$$Lambda$338/0x00007faab40b41a8 +java.lang.invoke.LambdaForm$DMH/0x00007faab40b8000 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$339/0x00007faab40b43f8 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$340/0x00007faab40b4620 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$341/0x00007faab40b4858 +java.lang.invoke.LambdaForm$DMH/0x00007faab40b8400 +org.junit.platform.launcher.core.EngineDiscoveryOrchestrator$$Lambda$342/0x00007faab40b4a88 +org.junit.platform.engine.TestDescriptor$$Lambda$343/0x00007faab40b4ca8 +org.junit.platform.launcher.core.LauncherDiscoveryResult +org.junit.platform.launcher.listeners.discovery.CompositeLauncherDiscoveryListener$$Lambda$344/0x00007faab40b5150 +org.junit.platform.launcher.core.LauncherPhase$$Lambda$345/0x00007faab40b5388 +org.junit.platform.engine.ConfigurationParameters$$Lambda$346/0x00007faab40b55c8 +org.junit.platform.launcher.core.LauncherDiscoveryResult$$Lambda$347/0x00007faab40b5810 +org.junit.platform.launcher.core.LauncherDiscoveryResult$$Lambda$348/0x00007faab40b5a60 +org.junit.platform.launcher.TestPlan$$Lambda$349/0x00007faab40b5ca0 +org.junit.platform.launcher.TestPlan$$Lambda$350/0x00007faab40b5ec8 +org.junit.platform.launcher.TestIdentifier +org.junit.platform.launcher.TestIdentifier$SerializedForm +java.io.ObjectStreamClass +java.io.ObjectStreamClass$Caches +java.io.ClassCache +java.io.ObjectStreamClass$Caches$1 +java.io.ClassCache$1 +java.io.ObjectStreamClass$Caches$2 +java.lang.ClassValue$ClassValueMap +java.io.Externalizable +java.io.ObjectStreamClass$2 +jdk.internal.reflect.UnsafeFieldAccessorFactory +jdk.internal.reflect.UnsafeQualifiedStaticFieldAccessorImpl +jdk.internal.reflect.UnsafeQualifiedStaticLongFieldAccessorImpl +java.util.ComparableTimSort +jdk.internal.reflect.SerializationConstructorAccessorImpl +jdk.internal.reflect.GeneratedSerializationConstructorAccessor1 +java.io.ObjectOutput +java.io.ObjectStreamConstants +java.io.ObjectOutputStream +java.io.ObjectInput +java.io.ObjectInputStream +java.lang.Class$$Lambda$351/0x00007faab407a758 +java.lang.CloneNotSupportedException +java.io.ClassCache$CacheRef +java.io.ObjectStreamClass$FieldReflectorKey +java.io.ObjectStreamClass$FieldReflector +org.junit.platform.launcher.TestIdentifier$$Lambda$352/0x00007faab40b6528 +org.junit.platform.launcher.TestPlan$$Lambda$353/0x00007faab40b6768 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$354/0x00007faab40b69a8 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$355/0x00007faab40b6be0 +software.amazon.lambda.powertools.validation.ValidationUtilsTest +com.fasterxml.jackson.core.TreeNode +com.fasterxml.jackson.databind.JsonSerializable +com.fasterxml.jackson.databind.JsonSerializable$Base +com.fasterxml.jackson.databind.JsonNode +software.amazon.lambda.powertools.validation.model.Product +software.amazon.lambda.powertools.validation.model.MyCustomEvent +jdk.internal.reflect.GeneratedConstructorAccessor6 +org.apache.maven.surefire.api.util.TestsToRun +org.apache.maven.surefire.api.util.DefaultRunOrderCalculator +java.util.random.RandomGenerator +java.util.Random +org.apache.maven.surefire.api.util.CloseableIterator +org.apache.maven.surefire.api.util.TestsToRun$ClassesIterator +java.util.NoSuchElementException +org.apache.maven.surefire.junitplatform.JUnitPlatformProvider$$Lambda$356/0x00007faab40bcbd8 +org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder$$Lambda$357/0x00007faab40bce10 +org.junit.platform.launcher.core.InterceptingLauncher$$Lambda$358/0x00007faab40bd048 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$359/0x00007faab40bd270 +org.junit.platform.launcher.core.CompositeTestExecutionListener$EagerTestExecutionListener +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$360/0x00007faab40bd6a8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$361/0x00007faab40bd900 +org.junit.platform.engine.reporting.ReportEntry +java.lang.invoke.LambdaForm$DMH/0x00007faab40b9000 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$362/0x00007faab40bdd58 +org.junit.platform.launcher.core.StreamInterceptingTestExecutionListener +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$363/0x00007faab40be268 +org.junit.platform.engine.EngineExecutionListener$1 +org.junit.platform.launcher.core.IterationOrder +org.junit.platform.launcher.core.IterationOrder$1 +org.junit.platform.launcher.core.IterationOrder$2 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$364/0x00007faab40bf000 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$365/0x00007faab40bf238 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$366/0x00007faab40bf460 +org.junit.platform.launcher.core.CompositeEngineExecutionListener +org.junit.platform.launcher.core.ListenerRegistry$$Lambda$367/0x00007faab40bf918 +org.junit.platform.launcher.core.ExecutionListenerAdapter +org.junit.platform.launcher.core.DelegatingEngineExecutionListener +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$368/0x00007faab40bfdd8 +org.junit.platform.launcher.core.OutcomeDelayingEngineExecutionListener +org.junit.platform.engine.ExecutionRequest +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$369/0x00007faab40ba9d8 +org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext +org.junit.jupiter.engine.descriptor.LauncherStoreFacade +org.junit.jupiter.api.extension.ExtensionContext$Store +org.junit.jupiter.engine.descriptor.LauncherStoreFacade$$Lambda$370/0x00007faab40bb528 +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext$State +org.junit.platform.engine.support.hierarchical.ThrowableCollector$Factory +org.junit.platform.engine.support.hierarchical.ThrowableCollector +org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector +org.junit.jupiter.engine.JupiterTestEngine$$Lambda$371/0x00007faab40b9a40 +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor +org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService$TestTask +org.junit.platform.engine.support.hierarchical.NodeTreeWalker +org.junit.platform.engine.support.hierarchical.LockManager +org.junit.platform.engine.support.hierarchical.ResourceLock +java.util.concurrent.locks.ReadWriteLock +org.junit.platform.engine.support.hierarchical.SingleLock +org.junit.platform.engine.support.hierarchical.ExclusiveResource +org.junit.platform.engine.support.hierarchical.ExclusiveResource$LockMode +org.junit.platform.engine.support.hierarchical.ExclusiveResource$$Lambda$372/0x00007faab40c0d38 +org.junit.platform.engine.support.hierarchical.ExclusiveResource$$Lambda$373/0x00007faab40c0f78 +java.util.Comparator$$Lambda$374/0x00007faab407b718 +java.lang.invoke.LambdaForm$DMH/0x00007faab40c4000 +java.util.Comparator$$Lambda$375/0x00007faab407b9b8 +org.junit.platform.engine.support.hierarchical.ExclusiveResource$$Lambda$376/0x00007faab40c11b8 +org.junit.platform.engine.support.hierarchical.LockManager$$Lambda$377/0x00007faab40c13f8 +java.util.concurrent.locks.ReentrantReadWriteLock +java.util.concurrent.locks.ReentrantReadWriteLock$Sync +java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync +java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter +java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock +java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock +org.junit.platform.commons.util.CollectionUtils$$Lambda$378/0x00007faab40c1638 +org.junit.platform.engine.support.hierarchical.NodeUtils +org.junit.platform.engine.support.hierarchical.NodeUtils$1 +org.junit.platform.engine.support.hierarchical.NodeExecutionAdvisor +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$379/0x00007faab40c1f68 +org.junit.platform.engine.support.hierarchical.NopLock +org.junit.jupiter.api.parallel.ResourceLocksProvider +org.junit.jupiter.engine.descriptor.ClassTestDescriptor$$Lambda$380/0x00007faab40c2630 +org.junit.platform.engine.support.hierarchical.NodeTreeWalker$$Lambda$381/0x00007faab40c2878 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$382/0x00007faab40c2ab0 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$383/0x00007faab40c2cd8 +org.junit.jupiter.engine.descriptor.ResourceLockAware$1 +org.junit.jupiter.api.parallel.ResourceLockTarget +java.util.ArrayDeque$DeqSpliterator +org.junit.jupiter.engine.descriptor.ResourceLockAware$$Lambda$384/0x00007faab40c35a8 +org.junit.jupiter.engine.descriptor.ResourceLockAware$$Lambda$385/0x00007faab40c37e8 +org.junit.jupiter.engine.descriptor.ResourceLockAware$$Lambda$386/0x00007faab40c3a30 +org.junit.platform.engine.support.hierarchical.NodeTestTaskContext +org.junit.platform.engine.support.hierarchical.NodeTestTask +org.junit.platform.engine.support.hierarchical.Node$DynamicTestExecutor +java.lang.invoke.LambdaForm$DMH/0x00007faab40c4400 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$387/0x00007faab40c6458 +org.opentest4j.IncompleteExecutionException +org.opentest4j.TestAbortedException +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector$$Lambda$388/0x00007faab40c6b38 +org.junit.platform.commons.util.UnrecoverableExceptions +org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector$$Lambda$389/0x00007faab40c6f98 +org.junit.platform.engine.support.hierarchical.ThrowableCollector$Executable +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$390/0x00007faab40c73b8 +org.junit.jupiter.engine.extension.MutableExtensionRegistry +org.junit.jupiter.engine.extension.MutableExtensionRegistry$Entry +org.junit.jupiter.api.extension.ExecutionCondition +org.junit.jupiter.engine.extension.DisabledCondition +org.junit.jupiter.api.extension.TestInstancePreDestroyCallback +org.junit.jupiter.api.extension.AfterAllCallback +org.junit.jupiter.engine.extension.AutoCloseExtension +org.junit.jupiter.api.extension.BeforeAllCallback +org.junit.jupiter.api.extension.BeforeEachCallback +org.junit.jupiter.engine.extension.TimeoutExtension +org.junit.jupiter.api.Timeout +org.junit.jupiter.api.extension.ExtensionContext$Namespace +org.junit.jupiter.engine.extension.RepeatedTestExtension +org.junit.jupiter.api.extension.TestTemplateInvocationContext +org.junit.jupiter.api.extension.ParameterResolver +org.junit.jupiter.engine.extension.TestInfoParameterResolver +org.junit.jupiter.api.TestInfo +org.junit.jupiter.engine.extension.TestReporterParameterResolver +org.junit.jupiter.api.TestReporter +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$391/0x00007faab40c8cc0 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$392/0x00007faab40c8ef8 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$393/0x00007faab40c9130 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$Entry$$Lambda$394/0x00007faab40c9358 +org.junit.jupiter.engine.extension.TempDirectory +org.junit.jupiter.api.extension.AnnotatedElementContext +org.junit.jupiter.engine.extension.TempDirectory$Scope +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$395/0x00007faab40c9c68 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$396/0x00007faab40c9eb0 +org.junit.jupiter.engine.extension.ExtensionContextInternal +org.junit.jupiter.engine.descriptor.AbstractExtensionContext +org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext +org.junit.jupiter.api.extension.ExecutableInvoker +org.junit.jupiter.engine.execution.DefaultExecutableInvoker +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$397/0x00007faab40cb118 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$398/0x00007faab40cb358 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$399/0x00007faab40cb578 +org.junit.jupiter.engine.execution.NamespaceAwareStore +org.junit.jupiter.api.extension.ExtensionContextException +org.junit.platform.engine.support.store.Namespace +java.lang.invoke.LambdaForm$DMH/0x00007faab40cc000 +org.junit.jupiter.engine.descriptor.AbstractExtensionContext$$Lambda$400/0x00007faab40ce000 +org.junit.jupiter.engine.execution.JupiterEngineExecutionContext$Builder +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$401/0x00007faab40ce460 +org.junit.platform.engine.support.hierarchical.Node$SkipResult +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$402/0x00007faab40ce8a8 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$403/0x00007faab40ceae0 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$404/0x00007faab40ced08 +org.junit.platform.launcher.TestPlan$$Lambda$405/0x00007faab40cef40 +org.junit.platform.launcher.TestPlan$$Lambda$406/0x00007faab40cf160 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$407/0x00007faab40cf388 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$408/0x00007faab40cf5c0 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$409/0x00007faab40cf7e8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$410/0x00007faab40cfa20 +org.junit.platform.engine.UniqueIdFormat$$Lambda$411/0x00007faab40cfc48 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$412/0x00007faab40cd000 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$413/0x00007faab40cd258 +org.junit.platform.engine.support.hierarchical.Node$Invocation +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$414/0x00007faab40cd680 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$415/0x00007faab40cd8a8 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$416/0x00007faab40cdad0 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$417/0x00007faab40cdd18 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor +java.util.concurrent.CancellationException +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$418/0x00007faab40cca50 +org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService$$Lambda$419/0x00007faab40ccc88 +org.junit.jupiter.engine.descriptor.ExtensionUtils +java.util.function.ToIntFunction +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$420/0x00007faab40d0000 +java.util.Comparator$$Lambda$421/0x00007faab407d3d8 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$422/0x00007faab40d0220 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$423/0x00007faab40d0470 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$424/0x00007faab40d06b0 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$LateInitEntry +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$425/0x00007faab40d0b38 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$426/0x00007faab40d0d70 +software.amazon.lambda.powertools.validation.Validation +org.aspectj.lang.Signature +com.amazonaws.services.lambda.runtime.Context +org.aspectj.lang.JoinPoint +org.aspectj.lang.ProceedingJoinPoint +software.amazon.lambda.powertools.validation.internal.ValidationAspect +org.junit.platform.commons.util.ReflectionUtils$$Lambda$427/0x00007faab40d1bd8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$428/0x00007faab40d1e70 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$429/0x00007faab40d20c0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$430/0x00007faab40d22e0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$431/0x00007faab40d2538 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$432/0x00007faab40d2758 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$433/0x00007faab40d29b0 +java.util.stream.SortedOps +java.util.stream.SortedOps$OfRef +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$434/0x00007faab40d2bd0 +java.util.stream.SortedOps$AbstractRefSortingSink +java.util.stream.SortedOps$RefSortingSink +java.util.stream.SortedOps$RefSortingSink$$Lambda$435/0x00007faab407e2f8 +org.junit.jupiter.api.extension.TestInstanceFactory +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$436/0x00007faab40d3008 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$437/0x00007faab40d3248 +org.junit.jupiter.engine.extension.MutableExtensionRegistry$$Lambda$438/0x00007faab40d34a0 +org.junit.jupiter.engine.extension.ExtensionRegistry$$Lambda$439/0x00007faab40d36e8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$440/0x00007faab40d3908 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$441/0x00007faab40d3b58 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$442/0x00007faab40d3d78 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$443/0x00007faab40d3fa0 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$444/0x00007faab40d41e8 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$445/0x00007faab40d4428 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$446/0x00007faab40d4660 +org.junit.jupiter.engine.execution.BeforeEachMethodAdapter +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$447/0x00007faab40d4a98 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$448/0x00007faab40d4ce0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$449/0x00007faab40d4f18 +org.junit.jupiter.engine.execution.AfterEachMethodAdapter +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$450/0x00007faab40d5340 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$451/0x00007faab40d5588 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$452/0x00007faab40d57c0 +org.junit.jupiter.engine.descriptor.ExtensionUtils$$Lambda$453/0x00007faab40d5a10 +org.junit.jupiter.api.extension.RegisterExtension +org.mockito.Mock +org.mockito.stubbing.Answer +org.mockito.Answers +org.mockito.Mock$Strictness +org.mockito.invocation.InvocationOnMock +org.mockito.internal.stubbing.defaultanswers.GloballyConfiguredAnswer +org.mockito.internal.stubbing.defaultanswers.ReturnsSmartNulls +org.mockito.internal.stubbing.defaultanswers.RetrieveGenericsForDefaultAnswers$AnswerCallback +org.mockito.internal.stubbing.defaultanswers.ReturnsMoreEmptyValues +org.mockito.internal.stubbing.defaultanswers.ReturnsEmptyValues +org.mockito.internal.stubbing.defaultanswers.ReturnsMocks +org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs +org.mockito.invocation.DescribedInvocation +org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs$ReturnsDeepStubsSerializationFallback +org.mockito.stubbing.ValidableAnswer +org.mockito.internal.stubbing.answers.CallsRealMethods +org.mockito.exceptions.base.MockitoException +org.mockito.internal.stubbing.defaultanswers.TriesToReturnSelf +jdk.proxy2.$Proxy15 +org.junit.jupiter.engine.descriptor.ClassExtensionContext +org.junit.jupiter.engine.execution.TestInstancesProvider +org.junit.jupiter.api.extension.TestInstances +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$454/0x00007faab40d9650 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$455/0x00007faab40d9888 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$456/0x00007faab40d9ad0 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$FilterType +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$457/0x00007faab40da150 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$458/0x00007faab40da3a0 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$459/0x00007faab40da5e0 +org.junit.platform.commons.util.ClassNamePatternFilterUtils$$Lambda$460/0x00007faab40da828 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$461/0x00007faab40daa78 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$462/0x00007faab40dacc0 +org.junit.jupiter.api.Disabled +org.junit.jupiter.engine.extension.DisabledCondition$$Lambda$463/0x00007faab40db110 +org.junit.jupiter.engine.execution.ConditionEvaluator$$Lambda$464/0x00007faab40db358 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$465/0x00007faab40db580 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$466/0x00007faab40db7d8 +org.junit.platform.launcher.TestIdentifier$$Lambda$467/0x00007faab40dba30 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$468/0x00007faab40dbc70 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$469/0x00007faab40dbeb8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$470/0x00007faab40dc100 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$471/0x00007faab40dc320 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$472/0x00007faab40dc570 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$473/0x00007faab40dc7b0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$474/0x00007faab40dca08 +org.apache.maven.surefire.api.report.SimpleReportEntry +org.apache.maven.surefire.api.util.internal.ClassMethod +org.apache.maven.surefire.report.ClassMethodIndexer$$Lambda$475/0x00007faab40dd160 +org.apache.maven.surefire.api.util.internal.ImmutableMap +org.apache.maven.surefire.booter.spi.EventChannelEncoder$StackTrace +java.lang.StrictMath +java.nio.StringCharBuffer +org.junit.jupiter.engine.descriptor.CallbackSupport$CallbackInvoker +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$476/0x00007faab40ddd50 +org.junit.jupiter.engine.descriptor.CallbackSupport +org.junit.jupiter.engine.descriptor.CallbackSupport$$Lambda$477/0x00007faab40de178 +org.junit.jupiter.engine.extension.TimeoutDuration +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$478/0x00007faab40de5c8 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$479/0x00007faab40de808 +org.junit.jupiter.api.Timeout$ThreadMode +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$480/0x00007faab40dec88 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$481/0x00007faab40deec8 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$482/0x00007faab40df100 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$483/0x00007faab40df358 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$484/0x00007faab40df590 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$485/0x00007faab40df7e0 +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$486/0x00007faab40dfa28 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$CompositeKey +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$StoredValue +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$487/0x00007faab40e0210 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$MemoizingSupplier +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$488/0x00007faab40e0688 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$489/0x00007faab40e08b0 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$MemoizingSupplier$Failure +org.junit.jupiter.api.io.TempDir +org.junit.platform.commons.util.AnnotationUtils$$Lambda$490/0x00007faab40e1100 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$491/0x00007faab40e1358 +org.junit.jupiter.engine.descriptor.ClassExtensionContext$$Lambda$492/0x00007faab40e1590 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$493/0x00007faab40e17d0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$494/0x00007faab40e1a10 +java.util.stream.Nodes$ArrayNode +java.util.stream.Nodes$FixedNodeBuilder +org.junit.jupiter.api.extension.TemplateInvocationValidationException +org.junit.jupiter.params.ParameterizedDeclarationContext +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext +org.junit.jupiter.engine.descriptor.TemplateExecutor +org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor$TestTemplateExecutor +java.lang.invoke.LambdaForm$DMH/0x00007faab40e4000 +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$495/0x00007faab40e2a60 +org.junit.jupiter.api.RepeatedTest +org.junit.jupiter.params.ParameterizedTestContext +org.junit.jupiter.params.ResolverFacade +org.junit.jupiter.params.support.ParameterDeclaration +org.junit.jupiter.params.support.ParameterDeclarations +org.junit.jupiter.params.ResolverFacade$ResolvableParameterDeclaration +org.junit.jupiter.params.support.FieldContext +org.junit.jupiter.params.ResolverFacade$FieldParameterDeclaration +org.junit.jupiter.params.ResolverFacade$Resolver +org.junit.jupiter.api.extension.ParameterResolutionException +org.junit.jupiter.params.ResolverFacade$ExecutableParameterDeclaration +org.junit.jupiter.api.extension.ParameterContext +org.junit.jupiter.params.aggregator.ArgumentsAccessor +org.junit.jupiter.params.aggregator.AggregateWith +java.util.TreeMap$Values +java.util.TreeMap$ValueIterator +org.junit.jupiter.params.ResolverFacade$DefaultParameterDeclarations +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$496/0x00007faab40e6f48 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$497/0x00007faab40e7170 +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$498/0x00007faab40e7398 +org.junit.jupiter.engine.execution.NamespaceAwareStore$$Lambda$499/0x00007faab40e75c0 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$500/0x00007faab40e77e8 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PartialFormatter +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$501/0x00007faab40e5000 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$502/0x00007faab40e5228 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$CachingByArgumentsLengthPartialFormatter +java.lang.invoke.LambdaForm$DMH/0x00007faab40e4400 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$503/0x00007faab40e5698 +java.lang.invoke.LambdaForm$DMH/0x00007faab40e4800 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$ArgumentSetNameFormatter +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PartialFormatters +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$ArgumentsContext +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PartialFormatter$$Lambda$504/0x00007faab40e4c00 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$505/0x00007faab40e8000 +java.lang.invoke.LambdaForm$DMH/0x00007faab40ec000 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$506/0x00007faab40e8228 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$507/0x00007faab40e8468 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$PlaceholderPosition +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$508/0x00007faab40e88a0 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$509/0x00007faab40e8cc0 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$510/0x00007faab40e8f00 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$511/0x00007faab40e9148 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$512/0x00007faab40e9390 +org.junit.jupiter.params.provider.Arguments +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$513/0x00007faab40e97d8 +org.junit.jupiter.params.ParameterizedInvocationContextProvider$$Lambda$514/0x00007faab40e9a20 +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$515/0x00007faab40e9c48 +org.junit.jupiter.params.ParameterizedTestSpiInstantiator +org.junit.jupiter.params.ParameterizedTestSpiInstantiator$$Lambda$516/0x00007faab40ea088 +org.junit.jupiter.engine.execution.ParameterResolutionUtils +org.junit.jupiter.engine.execution.ExtensionContextSupplier +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$517/0x00007faab40ea6b8 +org.junit.jupiter.params.support.AnnotationConsumerInitializer +org.junit.jupiter.params.support.AnnotationConsumerInitializer$AnnotationConsumingMethodSignature +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$518/0x00007faab40eaf00 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$519/0x00007faab40eb140 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$520/0x00007faab40eb390 +java.lang.invoke.LambdaForm$DMH/0x00007faab40ec400 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$521/0x00007faab40eb5d8 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$522/0x00007faab40eb830 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$523/0x00007faab40eba70 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$524/0x00007faab40ebcb0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$525/0x00007faab40ee000 +java.lang.reflect.Executable$$Lambda$526/0x00007faab407f5c8 +java.lang.reflect.Executable$$Lambda$527/0x00007faab407f808 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$528/0x00007faab40ee220 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$529/0x00007faab40ee470 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$530/0x00007faab40ee690 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$531/0x00007faab40ee8e8 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$532/0x00007faab40eeb08 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$533/0x00007faab40eed60 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$534/0x00007faab40eefa0 +org.junit.jupiter.params.support.AnnotationConsumerInitializer$$Lambda$535/0x00007faab40ef1e0 +com.amazonaws.services.lambda.runtime.tests.EventLoader +com.amazonaws.services.lambda.runtime.tests.EventLoadingException +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers +com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil$ReflectException +com.amazonaws.services.lambda.runtime.serialization.PojoSerializer +java.util.AbstractMap$SimpleEntry +com.amazonaws.services.lambda.runtime.serialization.events.serializers.OrgJsonSerializer +com.amazonaws.services.lambda.runtime.serialization.events.serializers.S3EventSerializer +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$536/0x00007faab40ed4a8 +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$537/0x00007faab40ed6e8 +java.util.stream.Collectors$$Lambda$538/0x00007faab40f0000 +java.util.stream.Collectors$$Lambda$539/0x00007faab40f0220 +java.util.stream.Collectors$$Lambda$540/0x00007faab40f0458 +com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudFormationCustomResourceEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudFrontEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudWatchLogsEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.CodeCommitEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.CodeCommitEventMixin$RecordMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.ConnectEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.ConnectEventMixin$DetailsMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.ConnectEventMixin$ContactDataMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.ConnectEventMixin$CustomerEndpointMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.ConnectEventMixin$QueueMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.ConnectEventMixin$SystemEndpointMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.DynamodbEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.DynamodbEventMixin$DynamodbStreamRecordMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.DynamodbEventMixin$StreamRecordMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.DynamodbEventMixin$AttributeValueMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.DynamodbTimeWindowEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.KinesisEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.KinesisEventMixin$RecordMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.KinesisTimeWindowEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.ScheduledEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.SecretsManagerRotationEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.SNSEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.SNSEventMixin$SNSRecordMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.SQSEventMixin +com.amazonaws.services.lambda.runtime.serialization.events.mixins.SQSEventMixin$SQSMessageMixin +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$541/0x00007faab4102a30 +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$542/0x00007faab4102c70 +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$NestedClass +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$AlternateNestedClass +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$543/0x00007faab41036d0 +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$544/0x00007faab4103910 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy$PropertyNamingStrategyBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy$UpperCamelCaseStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy$PascalCaseStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy$SnakeCaseStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy$LowerCaseStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy$KebabCaseStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyNamingStrategy$LowerDotCaseStrategy +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$545/0x00007faab4104cc8 +com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers$$Lambda$546/0x00007faab4104f08 +com.amazonaws.services.lambda.runtime.serialization.factories.PojoSerializerFactory +com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Versioned +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.Module +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleModule +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8Module +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TokenStreamFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.DataOutputAsStream +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.SerializableString +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TSFBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonFactoryBuilder +java.io.CharArrayReader +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.base.ParserMinimalBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.base.ParserBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.ReaderBasedJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.UTF8DataInputJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.async.NonBlockingInputFeeder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.async.ByteArrayFeeder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingJsonParserBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingUtf8JsonParserBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.async.ByteBufferFeeder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.async.NonBlockingByteBufferJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.base.GeneratorBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonGeneratorImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.UTF8JsonGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.UTF8Writer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.WriterBasedJsonGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.JacksonFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonFactory$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParser$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonGenerator$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.PrettyPrinter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.Instantiatable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter$Indenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.SerializedString +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.JsonStringEncoder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.CharTypes +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.FormatFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonReadFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonWriteFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$TableInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$Bucket +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer$TableInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TreeCodec +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.ObjectCodec +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.json.JsonMapper +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.MappingJsonFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.SubtypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DatabindContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.SerializerProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.DefaultSerializerProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.DefaultSerializerProvider$Impl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DeserializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BasicDeserializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DeserializationContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DefaultDeserializationContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DefaultDeserializationContext$Impl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.SerializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BasicSerializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializerFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AnnotationIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy$Provider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$Provider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator +java.text.Format +java.text.DateFormat +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.StdDateFormat +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JacksonException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonProcessingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DatabindException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonMappingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.TreeNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonSerializable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonSerializable$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BaseJsonNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ValueNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.NullNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.Module$SetupContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.TokenBuffer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.ClassIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.BasicClassIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.VisibilityChecker +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.TreeTraversingParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.SegmentedStringWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.ByteArrayBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.type.ResolvedType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JavaType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ArrayType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.CollectionLikeType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.CollectionType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.MapLikeType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.MapType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.MismatchedInputException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.Annotated +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.TypeResolutionContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedClass +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMember +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.VirtualAnnotatedMember +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.Named +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.BeanProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.PropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanPropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedWithParams +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethod +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonSerialize +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonView +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonTypeInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonRawValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonUnwrapped +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonBackReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonManagedReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonDeserialize +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonMerge +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7Support +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ClassUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ClassUtil$Ctor +java.beans.Transient +java.beans.ConstructorProperties +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.LookupCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.LRUMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Builder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap +java.io.ObjectStreamException +java.io.InvalidObjectException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.Linked +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.LinkedDeque +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$2 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$3 +java.util.concurrent.atomic.AtomicLongArray +java.lang.invoke.VarHandleLongs$Array +java.util.concurrent.atomic.AtomicReferenceArray +java.lang.invoke.VarHandleReferences$Array +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.BaseSettings +java.util.TimeZone +sun.util.calendar.ZoneInfo +sun.util.calendar.ZoneInfoFile +sun.util.calendar.ZoneInfoFile$1 +java.io.DataInputStream +sun.util.calendar.ZoneInfoFile$ZoneOffsetTransitionRule +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.SimpleType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ResolvedRecursiveType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.PlaceholderForType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ReferenceType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBindings +java.text.SimpleDateFormat +java.util.Calendar +java.util.GregorianCalendar +java.text.ParseException +java.text.AttributedCharacterIterator$Attribute +java.text.Format$Field +java.text.DateFormat$Field +sun.util.calendar.ZoneInfoFile$Checksum +java.util.spi.LocaleServiceProvider +sun.util.spi.CalendarProvider +sun.util.locale.provider.LocaleProviderAdapter +sun.util.locale.provider.LocaleProviderAdapter$Type +sun.util.locale.provider.LocaleProviderAdapter$1 +sun.util.locale.provider.ResourceBundleBasedAdapter +sun.util.locale.provider.JRELocaleProviderAdapter +sun.util.cldr.CLDRLocaleProviderAdapter +sun.util.locale.provider.LocaleDataMetaInfo +sun.util.cldr.CLDRBaseLocaleDataMetaInfo +sun.util.locale.LanguageTag +sun.util.locale.ParseStatus +sun.util.locale.StringTokenIterator +sun.util.locale.InternalLocaleBuilder +sun.util.locale.InternalLocaleBuilder$CaseInsensitiveChar +sun.util.locale.BaseLocale$Key +sun.util.locale.LocaleObjectCache +sun.util.locale.BaseLocale$Cache +sun.util.locale.LocaleObjectCache$CacheEntry +java.util.Locale$Cache +sun.util.cldr.CLDRLocaleProviderAdapter$$Lambda$57/0x80000005e +jdk.internal.module.ModulePatcher$PatchedModuleReader +sun.net.www.protocol.jrt.Handler +sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$59/0x800000060 +sun.util.locale.provider.AvailableLanguageTags +sun.util.locale.provider.CalendarProviderImpl +java.util.Calendar$Builder +sun.util.calendar.CalendarSystem +sun.util.calendar.CalendarSystem$GregorianHolder +sun.util.calendar.AbstractCalendar +sun.util.calendar.BaseCalendar +sun.util.calendar.Gregorian +sun.util.locale.provider.CalendarDataUtility +java.util.Locale$Builder +java.util.spi.CalendarDataProvider +sun.util.locale.provider.LocaleServiceProviderPool +java.text.spi.BreakIteratorProvider +java.text.spi.CollatorProvider +java.text.spi.DateFormatProvider +java.text.spi.DateFormatSymbolsProvider +java.text.spi.DecimalFormatSymbolsProvider +java.text.spi.NumberFormatProvider +java.util.spi.CurrencyNameProvider +java.util.spi.LocaleNameProvider +java.util.spi.TimeZoneNameProvider +sun.util.locale.provider.LocaleServiceProviderPool$LocalizedObjectGetter +sun.util.locale.provider.CalendarDataUtility$CalendarWeekParameterGetter +java.util.ResourceBundle$Control +java.util.ResourceBundle +java.util.ResourceBundle$Control$CandidateListCache +java.util.ResourceBundle$SingleFormatControl +java.util.ResourceBundle$NoFallbackControl +sun.util.cldr.CLDRLocaleProviderAdapter$$Lambda$58/0x80000005f +sun.util.locale.provider.CalendarDataProviderImpl +sun.util.cldr.CLDRCalendarDataProviderImpl +sun.util.locale.provider.LocaleResources +sun.util.resources.LocaleData +sun.util.resources.LocaleData$1 +sun.util.resources.Bundles$Strategy +sun.util.resources.LocaleData$LocaleDataStrategy +sun.util.resources.Bundles +sun.util.resources.Bundles$1 +jdk.internal.access.JavaUtilResourceBundleAccess +java.util.ResourceBundle$1 +java.util.ResourceBundle$2 +sun.util.resources.Bundles$CacheKey +java.util.ListResourceBundle +sun.util.resources.cldr.CalendarData +java.util.ResourceBundle$ResourceBundleProviderHelper +java.util.ResourceBundle$ResourceBundleProviderHelper$$Lambda$11/0x80000000f +sun.util.resources.Bundles$CacheKeyReference +sun.util.resources.Bundles$BundleReference +sun.util.locale.provider.LocaleResources$ResourceReference +sun.util.calendar.CalendarDate +sun.util.calendar.BaseCalendar$Date +sun.util.calendar.Gregorian$Date +sun.util.calendar.CalendarUtils +java.text.DateFormatSymbols +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$61/0x800000062 +sun.util.locale.provider.DateFormatSymbolsProviderImpl +sun.text.resources.cldr.FormatData +sun.text.resources.cldr.FormatData_en +java.text.NumberFormat +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$63/0x800000064 +sun.util.locale.provider.NumberFormatProviderImpl +java.text.DecimalFormatSymbols +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$62/0x800000063 +sun.util.locale.provider.DecimalFormatSymbolsProviderImpl +java.lang.StringLatin1$CharsSpliterator +java.util.stream.IntStream +java.util.stream.IntPipeline +java.util.stream.IntPipeline$Head +java.util.function.IntPredicate +java.text.DecimalFormatSymbols$$Lambda$7/0x80000000b +java.util.stream.IntPipeline$StatelessOp +java.util.stream.IntPipeline$10 +java.util.function.IntConsumer +java.util.stream.Sink$OfInt +java.util.stream.FindOps$FindSink$OfInt +java.util.OptionalInt +java.util.stream.FindOps$FindSink$OfInt$$Lambda$36/0x800000047 +java.util.stream.FindOps$FindSink$OfInt$$Lambda$34/0x800000045 +java.util.stream.FindOps$FindSink$OfInt$$Lambda$35/0x800000046 +java.util.stream.FindOps$FindSink$OfInt$$Lambda$33/0x800000044 +java.util.stream.Sink$ChainedInt +java.util.stream.IntPipeline$10$1 +java.lang.StringUTF16$CharsSpliterator +java.lang.CharacterData00 +java.text.DecimalFormat +java.text.FieldPosition +java.text.DigitList +java.math.RoundingMode +java.util.Date +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Base64Variants +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Base64Variant +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Base64Variant$PaddingReadBehaviour +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$RecordNaming +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MapperBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.json.JsonMapper$Builder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.RootNameLookup +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.ClassIntrospector$MixInResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.SimpleMixInResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MapperConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MapperConfigBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DeserializationConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.BeanDescription +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.BasicBeanDescription +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.SerializationConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.Annotations +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector$EmptyCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector$NoAnnotations +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedClass$Creators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedConstructor +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedParameter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigOverrides +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JacksonAnnotationValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonInclude$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonInclude$Include +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonSetter$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.Nulls +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.VisibilityChecker$Std +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionConfigs +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.LogicalType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionAction +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.MutableCoercionConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.CoercionInputShape +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Shape +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Features +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigOverride +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigOverride$Empty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConfigFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.MapperFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter$NopIndenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultPrettyPrinter$FixedSpaceIndenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.DefaultIndenter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.Separators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.SerializationFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DatatypeFeatures +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DatatypeFeatures$DefaultHolder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DatatypeFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.EnumFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.JsonNodeFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ContextAttributes +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ContextAttributes$Impl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.DeserializationFeature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.JsonNodeCreator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.JsonNodeFactory +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.MissingNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BooleanNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.NumericNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.DecimalNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ShortNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.IntNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.LongNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.DoubleNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.FloatNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BigIntegerNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.TextNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.BinaryNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.POJONode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsonschema.SchemaAware +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NullSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.FailingSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ToEmptyObjectSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnknownSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.ContextualSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.InvalidDefinitionException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.InvalidTypeIdException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ContainerNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ObjectNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.ResolvableSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.SerializerCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.NullValueProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.PropertyBindingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.InvalidFormatException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.ValueInstantiationException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.UnresolvedForwardReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ContextualDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiator$Gettable +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.EnumSetDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.AbstractDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ArrayBlockingQueueDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ResolvableDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.EnumMapDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.MapDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StringDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.MapEntryDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.TokenBufferDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.EnumDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicReferenceDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.SettableBeanProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.CreatorProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ErrorThrowingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.ObjectIdGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.ObjectIdGenerators$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.ObjectIdGenerators$PropertyGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.MethodProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.FieldProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.SetterlessProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyName +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AbstractTypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.KeyDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.KeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$DelegatingKD +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$EnumKD +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringCtorKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringFactoryKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.DeserializerCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.JsonValueSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ToStringSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.EnumSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.ContainerSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.IteratorSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.IterableSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.MapSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.IndexedStringListSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.StringCollectionSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.CollectionSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ArraySerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.StringArraySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.EnumSetSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ReferenceTypeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.SerializableSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.DateTimeSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.CalendarSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.DateSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ByteBufferSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.InetAddressSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.InetSocketAddressSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.TimeZoneSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.BeanSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedField +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StringSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntegerSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParser$NumberType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$LongSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntLikeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$ShortSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$DoubleSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$FloatSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.BooleanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.BooleanSerializer$AsNumber +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializer$BigDecimalAsStringSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers +java.util.Currency +java.util.UUID +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.UUIDSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicBooleanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicIntegerSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicLongSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.FileSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.ClassSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.TokenBufferSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializerModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.JSR310DateTimeDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.DurationDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.OffsetTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.YearDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.JSR310SerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.DurationSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.MonthDaySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.YearSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.YearMonthSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZoneIdSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.key.ZonedDateTimeKeySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.Jsr310KeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.DurationKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.InstantKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.LocalDateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.LocalDateKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.LocalTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.MonthDayKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.OffsetDateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.OffsetTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.PeriodKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.YearKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.YearMonthKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.ZonedDateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.ZoneIdKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.key.ZoneOffsetKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.PackageVersion +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.VersionUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.Version +java.time.temporal.TemporalAccessor +java.time.temporal.Temporal +java.time.temporal.TemporalAdjuster +java.time.Instant +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.exc.StreamReadException +java.time.DateTimeException +java.util.regex.Pattern$BnM +java.util.regex.Pattern$Pos +java.time.format.DateTimeFormatter +java.time.format.DateTimeFormatterBuilder +java.time.temporal.TemporalQuery +java.time.ZoneId +java.time.format.DateTimeFormatterBuilder$$Lambda$10/0x80000000e +java.time.temporal.TemporalField +java.time.temporal.ChronoField +java.time.temporal.ValueRange +java.time.temporal.IsoFields +java.time.temporal.IsoFields$Field +java.time.temporal.IsoFields$Field$1 +java.time.temporal.IsoFields$Field$2 +java.time.temporal.IsoFields$Field$3 +java.time.temporal.IsoFields$Field$4 +java.time.temporal.IsoFields$Unit +java.time.temporal.JulianFields +java.time.temporal.JulianFields$Field +java.time.format.SignStyle +java.time.format.DateTimeFormatterBuilder$DateTimePrinterParser +java.time.format.DateTimeFormatterBuilder$NumberPrinterParser +java.time.format.DateTimeFormatterBuilder$CharLiteralPrinterParser +java.time.format.ResolverStyle +java.time.chrono.Chronology +java.time.chrono.AbstractChronology +java.time.chrono.IsoChronology +java.time.format.DateTimeFormatterBuilder$CompositePrinterParser +java.time.format.DecimalStyle +java.time.format.DateTimeFormatterBuilder$SettingsParser +java.time.format.DateTimeFormatterBuilder$OffsetIdPrinterParser +java.time.format.DateTimeFormatterBuilder$FractionPrinterParser +java.time.format.DateTimeFormatterBuilder$ZoneIdPrinterParser +java.time.format.DateTimeFormatterBuilder$StringLiteralPrinterParser +java.time.format.DateTimeFormatterBuilder$InstantPrinterParser +java.time.format.TextStyle +java.time.format.DateTimeTextProvider$LocaleStore +java.util.AbstractMap$SimpleImmutableEntry +java.time.format.DateTimeTextProvider +java.time.format.DateTimeTextProvider$1 +java.time.format.DateTimeFormatterBuilder$1 +java.time.format.DateTimeFormatterBuilder$TextPrinterParser +java.time.chrono.ChronoPeriod +java.time.Period +java.time.format.DateTimeFormatter$$Lambda$8/0x80000000c +java.time.format.DateTimeFormatter$$Lambda$9/0x80000000d +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$562/0x00007faab41634e8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$FromIntegerArguments +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$563/0x00007faab4163938 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$FromDecimalArguments +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$564/0x00007faab4163d88 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$565/0x00007faab4163fc8 +java.time.OffsetDateTime +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$566/0x00007faab41641f8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$567/0x00007faab4164438 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$568/0x00007faab4164678 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$569/0x00007faab41648b8 +java.time.chrono.ChronoZonedDateTime +java.time.ZonedDateTime +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$570/0x00007faab4164ae8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$571/0x00007faab4164d28 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$572/0x00007faab4164f68 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer$$Lambda$573/0x00007faab41651a8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ClassKey +java.time.chrono.ChronoLocalDateTime +java.time.LocalDateTime +java.time.chrono.ChronoLocalDate +java.time.LocalDate +java.time.LocalTime +java.time.MonthDay +java.time.OffsetTime +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.deser.JSR310StringParsableDeserializer +java.time.ZoneOffset +java.time.Year +java.time.YearMonth +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleSerializers +java.util.function.ToLongFunction +java.lang.invoke.LambdaForm$DMH/0x00007faab4168000 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer$$Lambda$574/0x00007faab41665f8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer$$Lambda$575/0x00007faab4166818 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer$$Lambda$576/0x00007faab4166a38 +java.lang.invoke.LambdaForm$DMH/0x00007faab4168400 +java.lang.invoke.LambdaForm$DMH/0x00007faab4168800 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer$$Lambda$577/0x00007faab4166c58 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer$$Lambda$578/0x00007faab4166e78 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer$$Lambda$579/0x00007faab4167098 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer$$Lambda$580/0x00007faab41672b8 +java.lang.invoke.LambdaForm$DMH/0x00007faab4168c00 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer$$Lambda$581/0x00007faab41674d8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer$$Lambda$582/0x00007faab41676f8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.module.SimpleKeyDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ArrayBuilders +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiators$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jsr310.JavaTimeModule$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8TypeModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8BeanSerializerModifier +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.PackageVersion +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8Serializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalIntSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalLongSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalDoubleSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.LongStreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.IntStreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.DoubleStreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.StreamSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.Jdk8Deserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.BaseScalarOptionalDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalIntDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalLongDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.jdk8.OptionalDoubleDeserializer +com.amazonaws.services.lambda.runtime.serialization.util.SerializeUtil +com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil +java.lang.ExceptionInInitializerError +java.lang.InstantiationException +com.amazonaws.services.lambda.runtime.serialization.util.Functions$R0 +com.amazonaws.services.lambda.runtime.serialization.util.Functions$R1 +com.amazonaws.services.lambda.runtime.serialization.util.Functions$R9 +com.amazonaws.services.lambda.runtime.serialization.util.Functions$R4 +com.amazonaws.services.lambda.runtime.serialization.util.Functions$R3 +com.amazonaws.services.lambda.runtime.serialization.util.Functions$R5 +com.amazonaws.services.lambda.runtime.serialization.util.Functions$R2 +java.lang.ClassFormatError +com.amazonaws.services.lambda.runtime.serialization.util.Functions$V2 +com.amazonaws.services.lambda.runtime.serialization.util.Functions$V1 +com.amazonaws.services.lambda.runtime.events.models.kinesis.Record +com.amazonaws.services.lambda.runtime.events.KinesisEvent$Record +com.amazonaws.services.lambda.runtime.serialization.events.modules.DateModule +com.amazonaws.services.lambda.runtime.serialization.events.modules.DateModule$Serializer +com.amazonaws.services.lambda.runtime.serialization.events.modules.DateModule$Deserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.PackageVersion +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.JodaModule +com.amazonaws.services.lambda.runtime.serialization.events.modules.DateTimeModule +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.JodaDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.DateTimeZoneDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.DurationDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.JodaDateDeserializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.InstantDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.LocalDateTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.LocalTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.PeriodDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.IntervalDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.MonthDayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.YearMonthDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.JodaSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.JodaDateSerializerBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.DateTimeZoneSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.DurationSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.InstantSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.LocalDateTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.LocalDateSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.LocalTimeSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.PeriodSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.IntervalSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.MonthDaySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.YearMonthSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.key.JodaKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.key.DateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.key.LocalTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.key.LocalDateKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.key.LocalDateTimeKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.key.DurationKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.key.PeriodKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.DateMidnightDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.ser.DateMidnightSerializer +com.amazonaws.services.lambda.runtime.serialization.events.modules.DateTimeModule$1 +com.amazonaws.services.lambda.runtime.serialization.events.modules.DateTimeModule$2 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.PackageVersion +com.amazonaws.lambda.thirdparty.org.joda.time.ReadableInstant +com.amazonaws.lambda.thirdparty.org.joda.time.ReadableDateTime +com.amazonaws.lambda.thirdparty.org.joda.time.base.AbstractInstant +com.amazonaws.lambda.thirdparty.org.joda.time.base.AbstractDateTime +com.amazonaws.lambda.thirdparty.org.joda.time.base.BaseDateTime +com.amazonaws.lambda.thirdparty.org.joda.time.DateTime +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.cfg.FormatConfig +com.amazonaws.lambda.thirdparty.org.joda.time.DateTimeZone +com.amazonaws.lambda.thirdparty.org.joda.time.IllegalInstantException +com.amazonaws.lambda.thirdparty.org.joda.time.JodaTimePermission +com.amazonaws.lambda.thirdparty.org.joda.time.tz.NameProvider +com.amazonaws.lambda.thirdparty.org.joda.time.tz.FixedDateTimeZone +com.amazonaws.lambda.thirdparty.org.joda.time.tz.Provider +com.amazonaws.lambda.thirdparty.org.joda.time.UTCDateTimeZone +java.util.SimpleTimeZone +com.amazonaws.lambda.thirdparty.org.joda.time.tz.ZoneInfoProvider +java.lang.ArrayIndexOutOfBoundsException +com.amazonaws.lambda.thirdparty.org.joda.time.tz.ZoneInfoProvider$1 +java.util.Collections$UnmodifiableSortedSet +com.amazonaws.lambda.thirdparty.org.joda.time.tz.DateTimeZoneBuilder +com.amazonaws.lambda.thirdparty.org.joda.time.tz.DateTimeZoneBuilder$PrecalculatedZone +com.amazonaws.lambda.thirdparty.org.joda.time.tz.CachedDateTimeZone +com.amazonaws.lambda.thirdparty.org.joda.time.tz.DateTimeZoneBuilder$DSTZone +com.amazonaws.lambda.thirdparty.org.joda.time.Chronology +com.amazonaws.lambda.thirdparty.org.joda.time.chrono.BaseChronology +com.amazonaws.lambda.thirdparty.org.joda.time.chrono.AssembledChronology +com.amazonaws.lambda.thirdparty.org.joda.time.chrono.ISOChronology +com.amazonaws.lambda.thirdparty.org.joda.time.tz.CachedDateTimeZone$Info +com.amazonaws.lambda.thirdparty.org.joda.time.format.ISODateTimeFormat +com.amazonaws.lambda.thirdparty.org.joda.time.format.ISODateTimeFormat$Constants +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder +com.amazonaws.lambda.thirdparty.org.joda.time.format.InternalPrinter +com.amazonaws.lambda.thirdparty.org.joda.time.format.InternalParser +com.amazonaws.lambda.thirdparty.org.joda.time.DateTimeFieldType +com.amazonaws.lambda.thirdparty.org.joda.time.DateTimeFieldType$StandardDateTimeFieldType +com.amazonaws.lambda.thirdparty.org.joda.time.DurationFieldType +com.amazonaws.lambda.thirdparty.org.joda.time.DurationFieldType$StandardDurationFieldType +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$NumberFormatter +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$PaddedNumber +com.amazonaws.lambda.thirdparty.org.joda.time.DateTimeField +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$Composite +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatter +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$CharacterLiteral +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$StringLiteral +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$UnpaddedNumber +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$Fraction +com.amazonaws.lambda.thirdparty.org.joda.time.field.BaseDateTimeField +com.amazonaws.lambda.thirdparty.org.joda.time.field.PreciseDurationDateTimeField +com.amazonaws.lambda.thirdparty.org.joda.time.field.PreciseDateTimeField +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$TimeZoneOffset +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$FixedNumber +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeParser +com.amazonaws.lambda.thirdparty.org.joda.time.format.InternalParserDateTimeParser +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeParserInternalParser +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimeFormatterBuilder$MatchingParser +com.amazonaws.lambda.thirdparty.org.joda.time.format.DateTimePrinterInternalPrinter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaFormatBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaPeriodFormat +com.amazonaws.lambda.thirdparty.org.joda.time.format.ISOPeriodFormat +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodPrinter +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodParser +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder$PeriodFieldAffix +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder$FieldFormatter +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder$Literal +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder$IgnorableAffix +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder$SimpleAffix +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder$Separator +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatterBuilder$Composite +com.amazonaws.lambda.thirdparty.org.joda.time.format.PeriodFormatter +com.amazonaws.lambda.thirdparty.org.joda.time.ReadablePeriod +com.amazonaws.lambda.thirdparty.org.joda.time.ReadWritablePeriod +com.amazonaws.lambda.thirdparty.org.joda.time.ReadableDuration +com.amazonaws.lambda.thirdparty.org.joda.time.base.AbstractDuration +com.amazonaws.lambda.thirdparty.org.joda.time.base.BaseDuration +com.amazonaws.lambda.thirdparty.org.joda.time.Duration +com.amazonaws.lambda.thirdparty.org.joda.time.Instant +com.amazonaws.lambda.thirdparty.org.joda.time.ReadablePartial +com.amazonaws.lambda.thirdparty.org.joda.time.base.AbstractPartial +com.amazonaws.lambda.thirdparty.org.joda.time.base.BaseLocal +com.amazonaws.lambda.thirdparty.org.joda.time.LocalDateTime +com.amazonaws.lambda.thirdparty.org.joda.time.LocalDate +com.amazonaws.lambda.thirdparty.org.joda.time.LocalTime +com.amazonaws.lambda.thirdparty.org.joda.time.base.AbstractPeriod +com.amazonaws.lambda.thirdparty.org.joda.time.base.BasePeriod +com.amazonaws.lambda.thirdparty.org.joda.time.Period +com.amazonaws.lambda.thirdparty.org.joda.time.ReadableInterval +com.amazonaws.lambda.thirdparty.org.joda.time.base.AbstractInterval +com.amazonaws.lambda.thirdparty.org.joda.time.base.BaseInterval +com.amazonaws.lambda.thirdparty.org.joda.time.Interval +com.amazonaws.lambda.thirdparty.org.joda.time.base.BasePartial +com.amazonaws.lambda.thirdparty.org.joda.time.MonthDay +com.amazonaws.lambda.thirdparty.org.joda.time.YearMonth +com.amazonaws.lambda.thirdparty.org.joda.time.DateMidnight +org.joda.time.ReadableInstant +org.joda.time.ReadableDateTime +org.joda.time.base.AbstractInstant +org.joda.time.base.AbstractDateTime +org.joda.time.base.BaseDateTime +org.joda.time.DateTime +org.joda.time.Chronology +org.joda.time.chrono.BaseChronology +org.joda.time.chrono.AssembledChronology +org.joda.time.chrono.ISOChronology +com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory$InternalSerializer +com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory$ClassSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.ClassStack +sun.reflect.generics.repository.AbstractRepository +sun.reflect.generics.repository.GenericDeclRepository +sun.reflect.generics.repository.ClassRepository +java.lang.reflect.TypeVariable +sun.reflect.generics.tree.FormalTypeParameter +sun.reflect.generics.tree.Signature +sun.reflect.generics.tree.ClassSignature +java.util.OptionalLong +java.util.OptionalDouble +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WeightedValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Node +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$AddTask +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectReader +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.filter.TokenFilter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.filter.JsonPointerBasedFilter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ArrayNode +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.JsonParserDelegate +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.filter.FilteringParserDelegate +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonParseException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector$OneCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAutoDetect +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIdentityInfo +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ArrayIterator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.OptionalHandlerFactory +org.w3c.dom.Node +org.w3c.dom.Document +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7Handlers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.NioPathSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.NioPathDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.JdkDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.UUIDDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicBooleanDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicIntegerDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.AtomicLongDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.ByteBufferDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBuilderDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBufferDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$Std +java.net.InetAddress +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.BeanUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ValueInstantiator$Base +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.JsonLocationInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ArrayListInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ConstantValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$LinkedHashMapInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$HashMapInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonLocation +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConstructorDetector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.cfg.ConstructorDetector$SingleArgConstructor +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.CreatorCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdValueInstantiator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.CollectorBase +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.TypeResolutionContext$Basic +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector$FieldBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonValue +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAnyGetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAnySetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.InternCache +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonSetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAutoDetect$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.PropertyAccessor +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnore +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$WithMember +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty$Type +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$Linked +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.MemberKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonProperty$Access +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JacksonAnnotation +jdk.proxy2.$Proxy16 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector$MethodBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotationCollector$NCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JacksonAnnotationsInside +jdk.proxy2.$Proxy17 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedMethodMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonGetter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$5 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$6 +java.util.LinkedList$ListItr +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JacksonInject +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonNaming +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonPropertyOrder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonPropertyDescription +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.PropertyMetadata +sun.reflect.generics.scope.MethodScope +sun.reflect.generics.repository.ConstructorRepository +sun.reflect.generics.repository.MethodRepository +sun.reflect.generics.tree.VoidDescriptor +sun.reflect.generics.tree.MethodTypeSignature +com.amazonaws.services.lambda.runtime.events.KinesisEvent$KinesisEventRecord +java.lang.reflect.ParameterizedType +sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl +sun.reflect.generics.tree.TypeVariableSignature +sun.reflect.generics.reflectiveObjects.LazyReflectiveObjectGenerator +sun.reflect.generics.reflectiveObjects.TypeVariableImpl +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBindings$TypeParamStash +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.type.TypeBindings$AsKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BasicDeserializerFactory$CreatorCollectionState +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnoreProperties +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnoreProperties$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIncludeProperties +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIncludeProperties$Value +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.IgnorePropertiesUtil +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonIgnoreType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonTypeResolver +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.FailingDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.AccessPattern +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$2 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$4 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonAlias +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFormat$Feature +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.IgnoredPropertyException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.SettableBeanProperty$Delegating +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ManagedReferenceProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ObjectIdReferenceProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.MergingSettableBeanProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.InnerClassProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.ReadableObjectId$Referring +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BeanDeserializer$BeanReferring +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.BasicDeserializerFactory$ContainerDefaultMappings +java.util.concurrent.ConcurrentNavigableMap +java.util.concurrent.ConcurrentSkipListMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.LinkedNode +com.amazonaws.services.lambda.runtime.events.models.kinesis.EncryptionType +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$PrimitiveOrWrapperDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BooleanDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$LongDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$DoubleDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$CharacterDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ByteDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ShortDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$FloatDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$NumberDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigDecimalDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigIntegerDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.DateDeserializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateBasedDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.DateDeserializers$CalendarDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.ByteBufferBackedOutputStream +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.MinimalPrettyPrinter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectWriter$GeneratorSettings +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectWriter$Prefetch +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.RuntimeJsonMappingException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.ContentReference +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.IOContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.BufferRecyclers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.BufferRecycler +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.UTF8StreamJsonParser +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.MergedStream +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.UTF32Reader +java.io.CharConversionException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonEncoding +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.exc.InputCoercionException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.JsonEOFException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonStreamContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonReadContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.StreamReadCapability +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.JacksonFeatureSet +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.util.TextBuffer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonToken +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.NumberInput +org.junit.jupiter.params.provider.Arguments$$Lambda$583/0x00007faab41aefd8 +org.junit.jupiter.params.ParameterizedInvocationContext +org.junit.jupiter.params.ParameterizedTestInvocationContext +java.util.function.IntUnaryOperator +java.lang.invoke.LambdaForm$DMH/0x00007faab41b0000 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$584/0x00007faab41af670 +org.junit.jupiter.params.EvaluatedArgumentSet +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$585/0x00007faab41afb00 +java.lang.invoke.LambdaForm$DMH/0x00007faab41b0400 +org.junit.jupiter.params.provider.Arguments$ArgumentSet +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor$$Lambda$586/0x00007faab41b45b0 +org.junit.jupiter.api.Named +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$587/0x00007faab41b49d0 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$588/0x00007faab41b4c10 +java.util.stream.ReferencePipeline$$Lambda$589/0x00007faab40fc2b8 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$MessageFormatPartialFormatter +java.util.stream.Streams$RangeIntSpliterator +java.lang.invoke.LambdaForm$DMH/0x00007faab41b0800 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$590/0x00007faab41b5088 +java.util.stream.IntPipeline$1 +java.util.stream.IntPipeline$1$1 +org.junit.jupiter.params.ResolverFacade$$Lambda$591/0x00007faab41b52b0 +org.junit.jupiter.params.ParameterizedInvocationNameFormatter$$Lambda$592/0x00007faab41b54f0 +java.text.MessageFormat +sun.util.locale.provider.TimeZoneNameUtility +sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter +sun.util.cldr.CLDRLocaleProviderAdapter$$Lambda$593/0x00007faab40fded0 +sun.util.locale.provider.TimeZoneNameProviderImpl +sun.util.cldr.CLDRTimeZoneNameProviderImpl +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$594/0x00007faab40fe578 +sun.util.locale.provider.BaseLocaleDataMetaInfo +sun.util.locale.provider.JRELocaleProviderAdapter$$Lambda$595/0x00007faab40fe9f8 +sun.util.resources.provider.NonBaseLocaleDataMetaInfo +sun.util.resources.OpenListResourceBundle +sun.util.resources.TimeZoneNamesBundle +sun.util.resources.cldr.TimeZoneNames +sun.util.resources.cldr.TimeZoneNames_en +sun.util.cldr.CLDRBaseLocaleDataMetaInfo$TZCanonicalIDMapHolder +java.time.ZoneRegion +java.time.zone.ZoneRulesProvider +java.time.zone.ZoneRulesProvider$1 +java.time.zone.TzdbZoneRulesProvider +java.time.zone.Ser +java.time.zone.ZoneRules +java.time.zone.ZoneOffsetTransitionRule +java.time.zone.ZoneOffsetTransition +sun.util.resources.TimeZoneNames +sun.util.resources.TimeZoneNames_en +java.text.MessageFormat$Field +org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda$596/0x00007faab41b5730 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$597/0x00007faab41b5968 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$598/0x00007faab41b5ba0 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$599/0x00007faab41b5dc8 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$600/0x00007faab41b6000 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor$$Lambda$601/0x00007faab41b6228 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DynamicTaskState +org.junit.platform.engine.support.hierarchical.NodeTestTask$DynamicTaskState$$Lambda$602/0x00007faab41b6650 +org.junit.jupiter.params.ParameterizedInvocationParameterResolver +org.junit.jupiter.params.ParameterizedTestMethodParameterResolver +org.junit.jupiter.params.ResolutionCache +org.junit.jupiter.params.ResolutionCache$$Lambda$603/0x00007faab41b6f10 +org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor$$Lambda$604/0x00007faab41b7130 +org.junit.jupiter.engine.descriptor.MethodExtensionContext +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$605/0x00007faab41b7848 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$606/0x00007faab41b7a70 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$607/0x00007faab41b7c98 +org.junit.jupiter.engine.execution.ExtensionContextSupplier$ScopeBasedExtensionContextSupplier +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$608/0x00007faab41b2428 +org.junit.jupiter.engine.descriptor.DefaultTestInstanceFactoryContext +org.junit.jupiter.api.extension.TestInstancePreConstructCallback +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$609/0x00007faab41b2aa8 +java.lang.invoke.LambdaForm$DMH/0x00007faab41b0c00 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$610/0x00007faab41b2ce0 +org.junit.jupiter.engine.execution.ConstructorInvocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptorCall +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$$Lambda$611/0x00007faab41b3398 +org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation +org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation +org.aspectj.lang.NoAspectBoundException +software.amazon.lambda.powertools.validation.ValidationException +org.slf4j.LoggerFactory +org.slf4j.spi.SLF4JServiceProvider +java.util.ServiceConfigurationError +org.slf4j.event.LoggingEvent +org.slf4j.helpers.SubstituteServiceProvider +org.slf4j.IMarkerFactory +org.slf4j.spi.MDCAdapter +org.slf4j.ILoggerFactory +org.slf4j.helpers.SubstituteLoggerFactory +org.slf4j.Logger +java.util.concurrent.LinkedBlockingQueue +java.util.concurrent.LinkedBlockingQueue$Node +org.slf4j.helpers.BasicMarkerFactory +org.slf4j.Marker +org.slf4j.helpers.BasicMDCAdapter +java.lang.InheritableThreadLocal +org.slf4j.helpers.BasicMDCAdapter$1 +org.slf4j.helpers.ThreadLocalMapOfStacks +org.slf4j.helpers.NOP_FallbackServiceProvider +org.slf4j.helpers.NOPLoggerFactory +org.slf4j.helpers.NOPMDCAdapter +org.slf4j.helpers.Util +org.slf4j.simple.SimpleServiceProvider +org.slf4j.MDC +org.slf4j.simple.SimpleLoggerFactory +org.slf4j.helpers.AbstractLogger +org.slf4j.helpers.LegacyAbstractLogger +org.slf4j.simple.SimpleLogger +org.slf4j.spi.LoggingEventBuilder +org.slf4j.simple.SimpleLoggerConfiguration +org.slf4j.simple.SimpleLoggerConfiguration$$Lambda$612/0x00007faab41bb830 +org.slf4j.simple.OutputChoice +org.slf4j.simple.OutputChoice$OutputChoiceType +org.slf4j.helpers.Reporter +org.slf4j.helpers.Reporter$TargetChoice +org.slf4j.helpers.Reporter$Level +org.slf4j.simple.SimpleLoggerFactory$$Lambda$613/0x00007faab41bcb30 +org.junit.jupiter.engine.execution.DefaultTestInstances +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$614/0x00007faab41bcfd8 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$615/0x00007faab41bd220 +org.junit.jupiter.api.extension.TestInstancePostProcessor +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$616/0x00007faab41bd648 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$617/0x00007faab41bd880 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$618/0x00007faab41bdac0 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$619/0x00007faab41bdd18 +org.junit.jupiter.api.extension.ExtensionContext$Store$CloseableResource +org.junit.jupiter.params.ParameterizedInvocationContext$CloseableArgument +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$620/0x00007faab41be3a8 +org.junit.jupiter.params.ParameterizedInvocationContext$$Lambda$621/0x00007faab41be5e8 +org.junit.jupiter.params.ArgumentCountValidator +org.junit.jupiter.params.ArgumentCountValidator$$Lambda$622/0x00007faab41bea38 +org.junit.jupiter.params.ArgumentCountValidator$1 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$623/0x00007faab41bee88 +org.junit.jupiter.params.aggregator.DefaultArgumentsAccessor +org.junit.jupiter.params.aggregator.ArgumentAccessException +org.junit.jupiter.params.aggregator.DefaultArgumentsAccessor$$Lambda$624/0x00007faab41bf638 +org.junit.jupiter.params.aggregator.DefaultArgumentsAccessor$$Lambda$625/0x00007faab41bf870 +org.junit.jupiter.params.support.ParameterInfo +org.junit.jupiter.params.DefaultParameterInfo +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$626/0x00007faab4200000 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$627/0x00007faab4200248 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$628/0x00007faab4200498 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$629/0x00007faab42006e0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$630/0x00007faab4200928 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$631/0x00007faab4200b68 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$632/0x00007faab4200db8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$633/0x00007faab4200ff8 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$634/0x00007faab4201250 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$635/0x00007faab42014a0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$636/0x00007faab42016e0 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$637/0x00007faab4201930 +org.junit.jupiter.engine.extension.TempDirectory$FailureTracker +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$638/0x00007faab4201d98 +org.junit.jupiter.engine.extension.TempDirectory$$Lambda$639/0x00007faab4201fd0 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$640/0x00007faab4202220 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$641/0x00007faab4202448 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$642/0x00007faab4202668 +org.junit.jupiter.engine.execution.MethodInvocation +org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$$Lambda$643/0x00007faab4202b28 +org.junit.jupiter.engine.extension.TimeoutExtension$TimeoutProvider +org.junit.jupiter.engine.extension.TimeoutConfiguration +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$644/0x00007faab42031a0 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$645/0x00007faab42033f8 +org.junit.jupiter.engine.extension.TimeoutDurationParser +java.time.format.DateTimeParseException +java.util.regex.Pattern$$Lambda$646/0x00007faab41c2be0 +java.util.regex.Pattern$$Lambda$647/0x00007faab41c2e40 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$648/0x00007faab4203850 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$649/0x00007faab4203a78 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$650/0x00007faab4203cc0 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$651/0x00007faab4203f08 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$652/0x00007faab4204130 +org.mockito.MockitoAnnotations +org.mockito.configuration.IMockitoConfiguration +org.mockito.internal.configuration.GlobalConfiguration +org.mockito.configuration.DefaultMockitoConfiguration +org.mockito.internal.configuration.ClassPathLoader +org.mockito.exceptions.misusing.MockitoConfigurationException +org.mockito.internal.configuration.plugins.Plugins +org.mockito.plugins.MockitoPlugins +org.mockito.internal.configuration.plugins.PluginRegistry +org.mockito.plugins.PluginSwitch +org.mockito.internal.configuration.plugins.PluginLoader +org.mockito.internal.configuration.plugins.DefaultPluginSwitch +org.mockito.internal.configuration.plugins.DefaultMockitoPlugins +org.mockito.plugins.MockMaker +org.mockito.plugins.StackTraceCleanerProvider +org.mockito.plugins.InstantiatorProvider2 +org.mockito.plugins.AnnotationEngine +org.mockito.plugins.MockitoLogger +org.mockito.plugins.MemberAccessor +org.mockito.plugins.DoNotMockEnforcerWithType +org.mockito.internal.configuration.plugins.PluginInitializer +java.lang.invoke.LambdaForm$MH/0x00007faab4208000 +org.mockito.internal.configuration.plugins.PluginFinder +org.mockito.internal.util.collections.Iterables +org.mockito.internal.creation.bytebuddy.ClassCreatingMockMaker +org.mockito.plugins.InlineMockMaker +org.mockito.creation.instance.Instantiator +org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker +org.mockito.exceptions.base.MockitoInitializationException +org.mockito.internal.creation.bytebuddy.BytecodeGenerator +org.mockito.creation.instance.InstantiationException +org.mockito.plugins.MockMaker$TypeMockability +org.mockito.plugins.MockMaker$StaticMockControl +org.mockito.plugins.MockMaker$ConstructionMockControl +org.mockito.internal.PremainAttachAccess +org.mockito.internal.PremainAttach +java.lang.instrument.Instrumentation +net.bytebuddy.agent.Installer +java.io.Console +java.lang.Deprecated +jdk.proxy1.$Proxy18 +net.bytebuddy.ClassFileVersion +net.bytebuddy.ClassFileVersion$VersionLocator$Resolver +net.bytebuddy.ClassFileVersion$VersionLocator +net.bytebuddy.ClassFileVersion$VersionLocator$Resolved +java.lang.Process +net.bytebuddy.agent.ByteBuddyAgent +net.bytebuddy.agent.ByteBuddyAgent$AgentProvider +net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider +net.bytebuddy.agent.ByteBuddyAgent$AttachmentTypeEvaluator$InstallationAction +net.bytebuddy.agent.ByteBuddyAgent$AttachmentTypeEvaluator +java.security.PrivilegedActionException +com.sun.proxy.jdk.proxy1.$Proxy19 +java.security.DomainCombiner +net.bytebuddy.agent.ByteBuddyAgent$AttachmentTypeEvaluator$ForJava9CapableVm +java.lang.ProcessHandle +java.lang.ProcessHandle$Info +java.util.concurrent.CompletionStage +java.util.concurrent.CompletableFuture +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Compound +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForModularizedVm +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForJ9Vm +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForStandardToolsJarVm +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForUserDefinedToolsJar +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$ForEmulatedAttachment +net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm +net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm$ForJava9CapableVm +java.lang.reflect.AnnotatedType +java.lang.ProcessHandleImpl +java.lang.ProcessHandleImpl$$Lambda$653/0x00007faab41c5a00 +java.util.concurrent.ThreadLocalRandom +jdk.internal.util.random.RandomSupport +java.lang.invoke.LambdaForm$DMH/0x00007faab4208400 +java.lang.invoke.LambdaForm$DMH/0x00007faab4208800 +java.lang.ProcessHandleImpl$$Lambda$654/0x00007faab41c5c20 +java.lang.invoke.LambdaForm$DMH/0x00007faab4208c00 +java.lang.invoke.LambdaForm$MH/0x00007faab4209000 +java.util.concurrent.SynchronousQueue +java.util.concurrent.SynchronousQueue$Transferer +java.util.concurrent.SynchronousQueue$TransferStack +java.util.concurrent.SynchronousQueue$TransferStack$SNode +net.bytebuddy.agent.ByteBuddyAgent$AgentProvider$ForByteBuddyAgent +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor$Simple +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor$Simple$WithExternalAttachment +com.sun.tools.attach.VirtualMachine +net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Accessor$ExternalAttachment +java.util.zip.ZipInputStream +java.util.jar.JarInputStream +java.io.PushbackInputStream +sun.security.util.ManifestEntryVerifier +net.bytebuddy.agent.Attacher +java.lang.ProcessBuilder +java.lang.ProcessImpl +java.lang.ProcessImpl$Platform +java.lang.ProcessImpl$LaunchMechanism +java.lang.ProcessImpl$Platform$$Lambda$655/0x00007faab41c83f0 +java.lang.ProcessImpl$$Lambda$656/0x00007faab41c8618 +java.lang.invoke.LambdaForm$DMH/0x00007faab4210000 +java.lang.invoke.LambdaForm$DMH/0x00007faab4210400 +java.lang.invoke.LambdaForm$MH/0x00007faab4210800 +java.lang.ProcessImpl$1 +java.lang.ProcessImpl$ProcessPipeOutputStream +java.lang.ProcessImpl$ProcessPipeInputStream +java.lang.Process$PipeInputStream +java.lang.ProcessHandleImpl$ExitCompletion +java.util.concurrent.CompletableFuture$AltResult +java.util.concurrent.ForkJoinPool +java.lang.invoke.VarHandleLongs$FieldInstanceReadOnly +java.lang.invoke.VarHandleLongs$FieldInstanceReadWrite +java.lang.invoke.VarHandleInts$FieldStaticReadOnly +java.lang.invoke.VarHandleInts$FieldStaticReadWrite +java.util.concurrent.ForkJoinPool$ForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$1 +java.util.concurrent.ForkJoinPool$DefaultCommonPoolForkJoinWorkerThreadFactory +java.util.concurrent.ForkJoinPool$WorkQueue +java.util.concurrent.CompletableFuture$AsynchronousCompletionTask +java.util.concurrent.ForkJoinTask +java.util.concurrent.CompletableFuture$Completion +java.lang.ProcessHandleImpl$1 +java.lang.ProcessImpl$$Lambda$657/0x00007faab41ca448 +java.util.concurrent.CompletableFuture$UniCompletion +java.util.concurrent.CompletableFuture$UniHandle +java.util.concurrent.ForkJoinTask$Aux +jdk.internal.event.Event +jdk.internal.event.ProcessStartEvent +sun.instrument.InstrumentationImpl +sun.instrument.TransformerManager +sun.instrument.TransformerManager$TransformerInfo +java.lang.ProcessBuilder$NullInputStream +java.io.FileOutputStream$1 +java.lang.ProcessBuilder$NullOutputStream +java.io.File$TempDirectory +java.security.SecureRandom +sun.security.jca.Providers +sun.security.jca.ProviderList +sun.security.jca.ProviderConfig +java.security.Provider +sun.security.jca.ProviderList$3 +sun.security.jca.ProviderList$1 +java.security.Provider$ServiceKey +java.security.Provider$EngineDescription +java.security.SecureRandomParameters +java.security.cert.CertStoreParameters +java.security.Policy$Parameters +javax.security.auth.login.Configuration$Parameters +sun.security.jca.ProviderList$2 +sun.security.provider.Sun +sun.security.util.SecurityConstants +java.net.NetPermission +java.security.SecurityPermission +java.net.SocketPermission +sun.security.provider.SunEntries +sun.security.provider.SunEntries$1 +java.security.SecureRandomSpi +sun.security.provider.NativePRNG +sun.security.provider.NativePRNG$Variant +sun.security.provider.NativePRNG$1 +sun.security.provider.NativePRNG$2 +sun.security.provider.NativePRNG$RandomIO +sun.security.provider.FileInputStreamPool +sun.security.provider.FileInputStreamPool$UnclosableInputStream +sun.security.provider.FileInputStreamPool$StreamRef +java.security.Provider$Service +java.security.Provider$UString +sun.security.provider.NativePRNG$Blocking +sun.security.provider.NativePRNG$NonBlocking +sun.security.util.SecurityProviderConstants +sun.security.util.KnownOIDs +sun.security.util.KnownOIDs$1 +sun.security.util.KnownOIDs$2 +sun.security.util.KnownOIDs$3 +sun.security.util.KnownOIDs$4 +sun.security.util.KnownOIDs$5 +sun.security.util.KnownOIDs$6 +sun.security.util.KnownOIDs$7 +sun.security.util.KnownOIDs$8 +sun.security.util.KnownOIDs$9 +sun.security.util.KnownOIDs$10 +jdk.internal.event.SecurityProviderServiceEvent +sun.security.provider.SecureRandom +java.security.MessageDigestSpi +java.security.MessageDigest +sun.security.jca.GetInstance +sun.security.provider.DigestBase +sun.security.provider.SHA +sun.security.jca.GetInstance$Instance +sun.security.util.MessageDigestSpi2 +java.security.MessageDigest$Delegate +java.security.MessageDigest$Delegate$CloneableDelegate +sun.security.provider.ByteArrayAccess +sun.security.provider.ByteArrayAccess$BE +java.lang.invoke.VarHandleByteArrayAsInts$ByteArrayViewVarHandle +java.lang.invoke.VarHandleByteArrayAsInts$ArrayHandle +java.lang.invoke.VarHandleByteArrayBase +java.lang.invoke.VarHandleByteArrayAsInts +java.lang.invoke.VarHandleByteArrayAsInts$ArrayHandle$$Lambda$658/0x00007faab41d7338 +java.lang.invoke.VarHandleByteArrayAsLongs$ByteArrayViewVarHandle +java.lang.invoke.VarHandleByteArrayAsLongs$ArrayHandle +java.lang.invoke.VarHandleByteArrayAsLongs +java.lang.invoke.VarHandleByteArrayAsLongs$ArrayHandle$$Lambda$659/0x00007faab41d7ca0 +java.lang.invoke.VarHandle$TypesAndInvokers +java.lang.invoke.VarHandle$2 +java.lang.invoke.VarHandle$VarHandleDesc$Kind +java.lang.constant.ConstantDescs +java.lang.constant.ClassDesc +java.lang.constant.ConstantUtils +java.lang.constant.ReferenceClassDescImpl +java.lang.constant.DirectMethodHandleDesc$Kind +java.lang.constant.MethodTypeDesc +java.lang.constant.MethodTypeDescImpl +java.lang.constant.MethodHandleDesc +java.lang.constant.MethodHandleDesc$1 +java.lang.constant.DirectMethodHandleDesc +java.lang.constant.DirectMethodHandleDescImpl +java.lang.constant.DirectMethodHandleDescImpl$1 +java.lang.constant.DirectMethodHandleDesc$1 +java.lang.constant.DynamicConstantDesc +java.lang.constant.PrimitiveClassDescImpl +java.lang.constant.DynamicConstantDesc$AnonymousDynamicConstantDesc +java.io.DeleteOnExitHook +java.io.DeleteOnExitHook$1 +java.util.zip.DeflaterOutputStream +java.util.zip.ZipOutputStream +java.util.jar.JarOutputStream +java.util.zip.Deflater +java.util.zip.Deflater$DeflaterZStreamRef +java.util.zip.ZipOutputStream$XEntry +java.util.Vector$Itr +opened:/tmp/mockitoboot5452617940860437443.jar +org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher +org.mockito.internal.util.concurrent.WeakConcurrentMap +org.mockito.internal.util.concurrent.DetachedThreadLocal +org.mockito.internal.util.concurrent.DetachedThreadLocal$1 +org.mockito.internal.util.concurrent.WeakConcurrentMap$WithInlinedExpunction +org.mockito.internal.util.concurrent.DetachedThreadLocal$2 +org.mockito.internal.util.concurrent.DetachedThreadLocal$Cleaner +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$660/0x00007faab4215060 +java.lang.ThreadLocal$SuppliedThreadLocal +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$661/0x00007faab4215280 +org.mockito.internal.creation.bytebuddy.StackWalkerChecker +java.lang.StackWalker$Option +java.lang.invoke.LambdaForm$DMH/0x00007faab4210c00 +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$662/0x00007faab4215708 +org.mockito.internal.creation.bytebuddy.ConstructionCallback +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$663/0x00007faab4215b60 +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator +net.bytebuddy.matcher.ElementMatcher +net.bytebuddy.TypeCache +net.bytebuddy.TypeCache$WithInlineExpunction +java.lang.instrument.ClassFileTransformer +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator +net.bytebuddy.implementation.Implementation$Context$Factory +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler +net.bytebuddy.dynamic.scaffold.InstrumentedType$Prepareable +net.bytebuddy.implementation.Implementation +net.bytebuddy.asm.AsmVisitorWrapper +org.mockito.internal.creation.bytebuddy.MockMethodAdvice +java.lang.instrument.UnmodifiableClassException +net.bytebuddy.ByteBuddy +net.bytebuddy.NamingStrategy +net.bytebuddy.implementation.auxiliary.AuxiliaryType$NamingStrategy +net.bytebuddy.matcher.LatentMatcher +net.bytebuddy.utility.AsmClassWriter$Factory +net.bytebuddy.utility.AsmClassReader$Factory +net.bytebuddy.dynamic.VisibilityBridgeStrategy +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Factory +net.bytebuddy.NamingStrategy$Suffixing$BaseNameResolver +net.bytebuddy.dynamic.DynamicType$Builder +net.bytebuddy.description.NamedElement +net.bytebuddy.description.ModifierReviewable +net.bytebuddy.description.ModifierReviewable$OfByteCodeElement +net.bytebuddy.description.ModifierReviewable$OfAbstraction +net.bytebuddy.description.ModifierReviewable$OfEnumeration +net.bytebuddy.description.ModifierReviewable$ForTypeDefinition +net.bytebuddy.description.type.TypeDefinition +net.bytebuddy.matcher.FilterableList +net.bytebuddy.description.type.TypeList$Generic +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy +net.bytebuddy.description.NamedElement$WithRuntimeName +net.bytebuddy.description.annotation.AnnotationSource +net.bytebuddy.description.type.PackageDescription +net.bytebuddy.description.NamedElement$WithDescriptor +net.bytebuddy.description.DeclaredByType +net.bytebuddy.description.ByteCodeElement +net.bytebuddy.description.TypeVariableSource +net.bytebuddy.description.type.TypeDescription +net.bytebuddy.utility.privilege.GetSystemPropertyAction +net.bytebuddy.dynamic.scaffold.TypeValidation +net.bytebuddy.utility.GraalImageCode +net.bytebuddy.NamingStrategy$AbstractBase +net.bytebuddy.NamingStrategy$Suffixing +net.bytebuddy.NamingStrategy$SuffixingRandom +net.bytebuddy.NamingStrategy$Suffixing$BaseNameResolver$ForUnnamedType +net.bytebuddy.utility.RandomString +net.bytebuddy.implementation.auxiliary.AuxiliaryType$NamingStrategy$SuffixingRandom +net.bytebuddy.implementation.attribute.AnnotationValueFilter +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Default +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Default$1 +net.bytebuddy.implementation.attribute.AnnotationValueFilter$Default$2 +net.bytebuddy.implementation.attribute.AnnotationRetention +net.bytebuddy.implementation.Implementation$Context$Default$Factory +net.bytebuddy.implementation.MethodAccessorFactory +net.bytebuddy.implementation.Implementation$Context +net.bytebuddy.implementation.Implementation$Context$ExtractableView +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$AbstractBase +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default +net.bytebuddy.dynamic.scaffold.MethodGraph +net.bytebuddy.dynamic.scaffold.MethodGraph$Linked +net.bytebuddy.description.type.TypeDescription$Generic$Visitor +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Merger +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Harmonizer +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Harmonizer$ForJavaMethod +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Merger$Directional +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying$1 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying$2 +net.bytebuddy.description.type.TypeDescription$Generic +net.bytebuddy.matcher.ElementMatchers +net.bytebuddy.matcher.ElementMatcher$Junction +net.bytebuddy.description.ModifierReviewable$ForFieldDescription +net.bytebuddy.description.DeclaredByType$WithMandatoryDeclaration +net.bytebuddy.description.NamedElement$WithGenericName +net.bytebuddy.description.ByteCodeElement$Member +net.bytebuddy.description.ByteCodeElement$TypeDependant +net.bytebuddy.description.field.FieldDescription +net.bytebuddy.description.field.FieldDescription$InDefinedShape +net.bytebuddy.description.ModifierReviewable$ForMethodDescription +net.bytebuddy.description.method.MethodDescription +net.bytebuddy.description.method.MethodDescription$InDefinedShape +net.bytebuddy.matcher.ElementMatcher$Junction$AbstractBase +net.bytebuddy.matcher.BooleanMatcher +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default$1 +net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default$2 +net.bytebuddy.implementation.LoadedTypeInitializer +net.bytebuddy.implementation.bytecode.ByteCodeAppender +net.bytebuddy.dynamic.scaffold.TypeInitializer +net.bytebuddy.dynamic.scaffold.InstrumentedType +net.bytebuddy.dynamic.scaffold.InstrumentedType$WithFlexibleName +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default$1 +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default$2 +net.bytebuddy.dynamic.VisibilityBridgeStrategy$Default$3 +net.bytebuddy.utility.AsmClassReader$Factory$Default +net.bytebuddy.utility.AsmClassReader$Factory$Default$1 +net.bytebuddy.utility.AsmClassReader$Factory$Default$2 +net.bytebuddy.utility.AsmClassReader$Factory$Default$3 +net.bytebuddy.utility.AsmClassReader$Factory$Default$4 +net.bytebuddy.utility.AsmClassReader$Factory$Default$5 +net.bytebuddy.utility.AsmClassReader +net.bytebuddy.utility.AsmClassWriter$Factory$Default +net.bytebuddy.utility.AsmClassWriter$Factory$Default$1 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$2 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$3 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$4 +net.bytebuddy.utility.AsmClassWriter$Factory$Default$5 +net.bytebuddy.pool.TypePool +net.bytebuddy.jar.asm.ClassVisitor +net.bytebuddy.jar.asm.ClassWriter +net.bytebuddy.utility.AsmClassWriter$FrameComputingClassWriter +net.bytebuddy.utility.AsmClassWriter +net.bytebuddy.matcher.LatentMatcher$Resolved +net.bytebuddy.matcher.ModifierMatcher$Mode +net.bytebuddy.matcher.ElementMatcher$Junction$ForNonNullValues +net.bytebuddy.matcher.ModifierMatcher +net.bytebuddy.matcher.NameMatcher +net.bytebuddy.matcher.StringMatcher +net.bytebuddy.matcher.StringMatcher$Mode +net.bytebuddy.matcher.StringMatcher$Mode$1 +net.bytebuddy.matcher.StringMatcher$Mode$2 +net.bytebuddy.matcher.StringMatcher$Mode$3 +net.bytebuddy.matcher.StringMatcher$Mode$4 +net.bytebuddy.matcher.StringMatcher$Mode$5 +net.bytebuddy.matcher.StringMatcher$Mode$6 +net.bytebuddy.matcher.StringMatcher$Mode$7 +net.bytebuddy.matcher.StringMatcher$Mode$8 +net.bytebuddy.matcher.StringMatcher$Mode$9 +net.bytebuddy.matcher.MethodParametersMatcher +net.bytebuddy.matcher.CollectionSizeMatcher +net.bytebuddy.matcher.ElementMatcher$Junction$Conjunction +net.bytebuddy.description.ModifierReviewable$ForParameterDescription +net.bytebuddy.description.ModifierReviewable$AbstractBase +net.bytebuddy.description.TypeVariableSource$AbstractBase +net.bytebuddy.description.type.TypeDescription$AbstractBase +net.bytebuddy.description.type.TypeDescription$ForLoadedType +java.lang.reflect.GenericSignatureFormatError +net.bytebuddy.description.annotation.AnnotationList +net.bytebuddy.description.field.FieldList +net.bytebuddy.description.type.RecordComponentList +net.bytebuddy.matcher.FilterableList$Empty +net.bytebuddy.description.type.RecordComponentList$Empty +net.bytebuddy.matcher.FilterableList$AbstractBase +net.bytebuddy.description.type.RecordComponentList$AbstractBase +net.bytebuddy.description.type.RecordComponentList$ForLoadedRecordComponents +net.bytebuddy.description.method.MethodList +net.bytebuddy.description.type.TypeList +net.bytebuddy.description.type.TypeList$Empty +net.bytebuddy.description.type.TypeList$AbstractBase +net.bytebuddy.description.type.TypeList$ForLoadedTypes +net.bytebuddy.description.type.TypeDescription$ForLoadedType$Dispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader$Resolver$CreationAction +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader$Resolver +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader$Resolver$ForModuleSystem +net.bytebuddy.utility.dispatcher.JavaDispatcher$InvokerCreationAction +net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader +net.bytebuddy.utility.Invoker +net.bytebuddy.jar.asm.ClassTooLargeException +net.bytebuddy.jar.asm.FieldVisitor +net.bytebuddy.jar.asm.FieldWriter +net.bytebuddy.jar.asm.AnnotationVisitor +net.bytebuddy.jar.asm.AnnotationWriter +net.bytebuddy.jar.asm.MethodVisitor +net.bytebuddy.jar.asm.MethodWriter +net.bytebuddy.jar.asm.ModuleVisitor +net.bytebuddy.jar.asm.ModuleWriter +net.bytebuddy.jar.asm.RecordComponentVisitor +net.bytebuddy.jar.asm.RecordComponentWriter +java.lang.TypeNotPresentException +net.bytebuddy.jar.asm.SymbolTable +net.bytebuddy.jar.asm.Symbol +net.bytebuddy.jar.asm.SymbolTable$Entry +net.bytebuddy.jar.asm.ByteVector +net.bytebuddy.jar.asm.Type +net.bytebuddy.utility.MethodComparator +net.bytebuddy.jar.asm.Frame +net.bytebuddy.jar.asm.CurrentFrame +net.bytebuddy.jar.asm.MethodTooLargeException +net.bytebuddy.jar.asm.Handler +net.bytebuddy.jar.asm.Attribute +net.bytebuddy.utility.Invoker$Dispatcher +net.bytebuddy.utility.dispatcher.JavaDispatcher$Proxied +net.bytebuddy.utility.dispatcher.JavaDispatcher$Defaults +jdk.proxy2.$Proxy20 +jdk.proxy2.$Proxy21 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Instance +net.bytebuddy.utility.dispatcher.JavaDispatcher$Container +net.bytebuddy.utility.dispatcher.JavaDispatcher$IsStatic +net.bytebuddy.utility.dispatcher.JavaDispatcher$IsConstructor +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod +net.bytebuddy.utility.nullability.MaybeNull +jdk.proxy2.$Proxy22 +net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler +net.bytebuddy.description.type.$Proxy23 +net.bytebuddy.dynamic.TargetType +net.bytebuddy.matcher.EqualityMatcher +net.bytebuddy.matcher.ErasureMatcher +net.bytebuddy.matcher.MethodReturnTypeMatcher +net.bytebuddy.matcher.DeclaringTypeMatcher +net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction +net.bytebuddy.implementation.Implementation$Context$Disabled$Factory +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$ForDeclaredMethods +net.bytebuddy.matcher.MethodSortMatcher$Sort +net.bytebuddy.matcher.MethodSortMatcher$Sort$1 +net.bytebuddy.matcher.MethodSortMatcher$Sort$2 +net.bytebuddy.matcher.MethodSortMatcher$Sort$3 +net.bytebuddy.matcher.MethodSortMatcher$Sort$4 +net.bytebuddy.matcher.MethodSortMatcher$Sort$5 +net.bytebuddy.matcher.MethodSortMatcher +net.bytebuddy.matcher.NegatingMatcher +org.mockito.internal.util.concurrent.WeakConcurrentSet +org.mockito.internal.util.concurrent.WeakConcurrentSet$Cleaner +org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator +net.bytebuddy.implementation.attribute.MethodAttributeAppender$Factory +net.bytebuddy.dynamic.loading.ClassLoadingStrategy +org.mockito.internal.creation.bytebuddy.ModuleHandler +org.mockito.internal.creation.bytebuddy.ModuleHandler$ModuleSystemFound +org.mockito.internal.creation.bytebuddy.ModuleHandler$1 +org.mockito.internal.creation.bytebuddy.ModuleHandler$NoModuleSystemFound +org.mockito.internal.creation.bytebuddy.ModuleHandler$2 +org.mockito.internal.creation.bytebuddy.ModuleHandler$3 +java.lang.instrument.ClassDefinition +org.mockito.internal.creation.bytebuddy.ModuleHandler$MockitoMockClassLoader +jdk.internal.vm.annotation.ForceInline +com.sun.proxy.jdk.proxy1.$Proxy24 +net.bytebuddy.implementation.Implementation$Composable +net.bytebuddy.implementation.MethodDelegation +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler +net.bytebuddy.implementation.bind.MethodDelegationBinder$Record +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver +net.bytebuddy.implementation.MethodDelegation$WithCustomProperties +net.bytebuddy.implementation.bind.MethodDelegationBinder$BindingResolver +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate +net.bytebuddy.dynamic.scaffold.FieldLocator$Factory +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver$Compound +net.bytebuddy.implementation.bind.annotation.BindingPriority$Resolver +net.bytebuddy.implementation.bind.annotation.BindingPriority +net.bytebuddy.description.method.MethodList$AbstractBase +net.bytebuddy.description.method.MethodList$ForLoadedMethods +net.bytebuddy.description.method.MethodDescription$AbstractBase +net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase +net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$ParameterAnnotationSource +net.bytebuddy.description.method.MethodDescription$ForLoadedConstructor +net.bytebuddy.description.method.MethodDescription$ForLoadedMethod +net.bytebuddy.utility.ConstructorComparator +net.bytebuddy.description.ByteCodeElement$Token +net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$Executable +net.bytebuddy.description.method.$Proxy25 +net.bytebuddy.implementation.bind.DeclaringTypeResolver +net.bytebuddy.implementation.bind.ArgumentTypeResolver +net.bytebuddy.implementation.bind.MethodNameEqualityResolver +net.bytebuddy.implementation.bind.ParameterLengthResolver +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver$NoOp +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder +net.bytebuddy.implementation.bind.annotation.Argument$Binder +net.bytebuddy.implementation.bytecode.StackManipulation +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding +net.bytebuddy.implementation.bind.annotation.Argument +net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic +net.bytebuddy.description.method.MethodList$Explicit +net.bytebuddy.implementation.bind.annotation.AllArguments$Binder +net.bytebuddy.implementation.bind.annotation.AllArguments +net.bytebuddy.implementation.bind.annotation.AllArguments$Assignment +net.bytebuddy.implementation.bind.annotation.Origin$Binder +net.bytebuddy.implementation.bind.annotation.Origin +net.bytebuddy.implementation.bind.annotation.This$Binder +net.bytebuddy.implementation.bind.annotation.This +net.bytebuddy.implementation.bind.annotation.Super$Binder +net.bytebuddy.implementation.bind.annotation.Super +net.bytebuddy.implementation.bind.annotation.Super$Instantiation +net.bytebuddy.implementation.bind.annotation.Default$Binder +net.bytebuddy.implementation.bind.annotation.Default +net.bytebuddy.implementation.bind.annotation.SuperCall$Binder +net.bytebuddy.implementation.bind.annotation.SuperCall +net.bytebuddy.implementation.bind.annotation.SuperCallHandle$Binder +net.bytebuddy.implementation.bind.annotation.SuperCallHandle +net.bytebuddy.implementation.bind.annotation.DefaultCall$Binder +net.bytebuddy.implementation.bind.annotation.DefaultCall$Binder$DefaultMethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultCall +net.bytebuddy.implementation.bind.annotation.DefaultCallHandle$Binder +net.bytebuddy.implementation.bind.annotation.DefaultCallHandle$Binder$DefaultMethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultCallHandle +net.bytebuddy.implementation.bind.annotation.SuperMethod$Binder +net.bytebuddy.implementation.bind.annotation.SuperMethod +net.bytebuddy.implementation.bind.annotation.SuperMethodHandle$Binder +net.bytebuddy.implementation.bind.annotation.SuperMethodHandle +net.bytebuddy.implementation.bind.annotation.Handle$Binder +net.bytebuddy.utility.ConstantValue +net.bytebuddy.utility.JavaConstant +net.bytebuddy.implementation.bind.annotation.Handle +net.bytebuddy.utility.JavaConstant$MethodHandle$HandleType +net.bytebuddy.implementation.bind.annotation.DynamicConstant$Binder +net.bytebuddy.implementation.bind.annotation.DynamicConstant +net.bytebuddy.implementation.bind.annotation.DefaultMethod$Binder +net.bytebuddy.implementation.bind.annotation.DefaultMethod$Binder$MethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultMethod +net.bytebuddy.implementation.bind.annotation.DefaultMethodHandle$Binder +net.bytebuddy.implementation.bind.annotation.DefaultMethodHandle$Binder$MethodLocator +net.bytebuddy.implementation.bind.annotation.DefaultMethodHandle +net.bytebuddy.implementation.bind.annotation.FieldValue$Binder +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder$ForFieldBinding +net.bytebuddy.implementation.bind.annotation.FieldValue$Binder$Delegate +net.bytebuddy.dynamic.scaffold.FieldLocator +net.bytebuddy.dynamic.scaffold.FieldLocator$AbstractBase +net.bytebuddy.dynamic.scaffold.FieldLocator$ForClassHierarchy +net.bytebuddy.dynamic.scaffold.FieldLocator$ForExactType +net.bytebuddy.implementation.bind.annotation.FieldValue +net.bytebuddy.implementation.bind.annotation.FieldGetterHandle$Binder +net.bytebuddy.implementation.bind.annotation.FieldGetterHandle$Binder$Delegate +net.bytebuddy.implementation.bind.annotation.FieldGetterHandle +net.bytebuddy.implementation.bind.annotation.FieldSetterHandle$Binder +net.bytebuddy.implementation.bind.annotation.FieldSetterHandle$Binder$Delegate +net.bytebuddy.implementation.bind.annotation.FieldSetterHandle +net.bytebuddy.implementation.bind.annotation.StubValue$Binder +net.bytebuddy.implementation.bind.annotation.Empty$Binder +net.bytebuddy.implementation.bind.MethodDelegationBinder$BindingResolver$Default +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$Identifier +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder$ForFixedValue +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$ParameterBinder$ForFixedValue$OfConstant +net.bytebuddy.utility.CompoundList +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForReadObject +org.mockito.internal.creation.bytebuddy.access.MockAccess +net.bytebuddy.implementation.bind.MethodDelegationBinder +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor$Handler +net.bytebuddy.implementation.bind.annotation.StubValue +net.bytebuddy.implementation.bind.annotation.Empty +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate$ForStaticMethod +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate$Compiled +net.bytebuddy.implementation.bind.annotation.IgnoreForBinding$Verifier +net.bytebuddy.description.annotation.AnnotationList$AbstractBase +net.bytebuddy.description.annotation.AnnotationList$ForLoadedAnnotations +net.bytebuddy.description.annotation.AnnotationDescription +net.bytebuddy.implementation.bind.annotation.IgnoreForBinding +net.bytebuddy.description.method.ParameterList +net.bytebuddy.description.method.ParameterList$AbstractBase +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfMethod +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfLegacyVmMethod +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfConstructor +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$OfLegacyVmConstructor +net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$Executable +jdk.proxy2.$Proxy26 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForInstanceCheck +jdk.internal.reflect.GeneratedConstructorAccessor7 +net.bytebuddy.description.method.$Proxy27 +net.bytebuddy.description.NamedElement$WithOptionalName +net.bytebuddy.description.method.ParameterDescription +net.bytebuddy.description.method.ParameterDescription$InDefinedShape +net.bytebuddy.description.method.ParameterDescription$AbstractBase +net.bytebuddy.description.method.ParameterDescription$InDefinedShape$AbstractBase +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$OfMethod +net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$Parameter +net.bytebuddy.description.method.$Proxy28 +net.bytebuddy.implementation.bind.annotation.RuntimeType$Verifier +org.mockito.internal.creation.bytebuddy.$Proxy29 +jdk.proxy2.$Proxy30 +net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 +net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 +jdk.proxy2.$Proxy31 +net.bytebuddy.implementation.bind.annotation.RuntimeType +net.bytebuddy.description.annotation.AnnotationDescription$Loadable +net.bytebuddy.description.annotation.AnnotationDescription$AbstractBase +net.bytebuddy.description.annotation.AnnotationDescription$ForLoadedAnnotation +net.bytebuddy.description.annotation.AnnotationValue +net.bytebuddy.description.enumeration.EnumerationDescription +net.bytebuddy.description.type.TypeDefinition$Sort +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader +net.bytebuddy.description.type.TypeDefinition$Sort$AnnotatedType +net.bytebuddy.description.type.$Proxy32 +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$NoOp +net.bytebuddy.description.type.TypeDescription$Generic$AbstractBase +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType$ForLoadedType +net.bytebuddy.implementation.bytecode.assign.Assigner$Typing +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor$Handler$Unbound +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$DelegationProcessor$Handler$Bound +net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder$Record +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler$Default +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler$Default$1 +net.bytebuddy.implementation.bind.MethodDelegationBinder$TerminationHandler$Default$2 +net.bytebuddy.implementation.bytecode.assign.Assigner +net.bytebuddy.implementation.bytecode.assign.primitive.VoidAwareAssigner +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveTypeAwareAssigner +net.bytebuddy.implementation.bytecode.assign.reference.ReferenceTypeAwareAssigner +net.bytebuddy.implementation.bytecode.StackManipulation$Trivial +net.bytebuddy.implementation.bytecode.StackManipulation$Illegal +net.bytebuddy.implementation.bytecode.assign.reference.GenericTypeAwareAssigner +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$DispatcherDefaultingToRealMethod +org.mockito.internal.invocation.RealMethod +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor +jdk.proxy2.$Proxy33 +jdk.proxy2.$Proxy34 +jdk.proxy2.$Proxy35 +jdk.proxy2.$Proxy36 +jdk.proxy2.$Proxy37 +jdk.proxy2.$Proxy38 +jdk.proxy2.$Proxy39 +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$ForHashCode +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$ForEquals +org.mockito.internal.creation.bytebuddy.access.MockMethodInterceptor$ForWriteReplace +net.bytebuddy.TypeCache$Sort +net.bytebuddy.TypeCache$Sort$1 +net.bytebuddy.TypeCache$Sort$2 +net.bytebuddy.TypeCache$Sort$3 +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$TypeCachingLock +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$DispatchingVisitor +net.bytebuddy.matcher.CollectionOneToOneMatcher +net.bytebuddy.matcher.CollectionErasureMatcher +net.bytebuddy.matcher.MethodParameterTypesMatcher +net.bytebuddy.matcher.AnnotationTypeMatcher +net.bytebuddy.matcher.DeclaringAnnotationMatcher +net.bytebuddy.matcher.CollectionItemMatcher +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$MethodVisitorWrapper +net.bytebuddy.asm.Advice +net.bytebuddy.asm.Advice$ExceptionHandler +net.bytebuddy.asm.Advice$Dispatcher +net.bytebuddy.asm.Advice$Dispatcher$Unresolved +net.bytebuddy.dynamic.ClassFileLocator +net.bytebuddy.asm.Advice$Delegator$Factory +net.bytebuddy.asm.Advice$PostProcessor$Factory +net.bytebuddy.utility.visitor.ExceptionTableSensitiveMethodVisitor +net.bytebuddy.utility.visitor.LineNumberPrependingMethodVisitor +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$Relocation +net.bytebuddy.asm.Advice$AdviceVisitor +net.bytebuddy.asm.Advice$AdviceVisitor$WithoutExitAdvice +net.bytebuddy.asm.Advice$AdviceVisitor$WithExitAdvice +net.bytebuddy.asm.Advice$AdviceVisitor$WithExitAdvice$WithoutExceptionHandling +net.bytebuddy.asm.Advice$AdviceVisitor$WithExitAdvice$WithExceptionHandling +net.bytebuddy.asm.Advice$OnMethodEnter +net.bytebuddy.asm.Advice$OnMethodExit +net.bytebuddy.asm.Advice$WithCustomMapping +net.bytebuddy.asm.Advice$OffsetMapping$Factory +net.bytebuddy.asm.Advice$BootstrapArgumentResolver$Factory +net.bytebuddy.asm.Advice$PostProcessor +net.bytebuddy.asm.Advice$PostProcessor$NoOp +net.bytebuddy.asm.Advice$Delegator$ForRegularInvocation$Factory +net.bytebuddy.asm.Advice$Delegator +net.bytebuddy.asm.Advice$OffsetMapping$ForStackManipulation$Factory +net.bytebuddy.asm.Advice$OffsetMapping +net.bytebuddy.utility.ConstantValue$Simple +net.bytebuddy.utility.JavaConstant$Simple +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher +jdk.proxy2.$Proxy40 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForContainerCreation +net.bytebuddy.utility.$Proxy41 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfClassDesc +jdk.proxy2.$Proxy42 +net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForStaticMethod +jdk.proxy2.$Proxy43 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfMethodTypeDesc +jdk.proxy2.$Proxy44 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfMethodHandleDesc +jdk.proxy2.$Proxy45 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDirectMethodHandleDesc +jdk.proxy2.$Proxy46 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDirectMethodHandleDesc$ForKind +jdk.proxy2.$Proxy47 +net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDynamicConstantDesc +jdk.proxy2.$Proxy48 +net.bytebuddy.utility.JavaConstant$Simple$OfTrivialValue +net.bytebuddy.utility.JavaConstant$Simple$OfTrivialValue$ForString +net.bytebuddy.implementation.bytecode.StackManipulation$AbstractBase +net.bytebuddy.implementation.bytecode.constant.TextConstant +net.bytebuddy.dynamic.ClassFileLocator$ForClassLoader +net.bytebuddy.dynamic.ClassFileLocator$Resolution +net.bytebuddy.dynamic.ClassFileLocator$ForClassLoader$BootLoaderProxyCreationAction +net.bytebuddy.asm.Advice$Dispatcher$Resolved +net.bytebuddy.asm.Advice$Dispatcher$Resolved$ForMethodEnter +net.bytebuddy.asm.Advice$Dispatcher$Resolved$ForMethodExit +net.bytebuddy.asm.Advice$Dispatcher$Bound +net.bytebuddy.asm.Advice$Dispatcher$Inactive +net.bytebuddy.asm.Advice$NoExceptionHandler +jdk.proxy2.$Proxy49 +net.bytebuddy.description.annotation.AnnotationValue$AbstractBase +net.bytebuddy.description.annotation.AnnotationValue$ForConstant +net.bytebuddy.description.annotation.AnnotationValue$Loaded +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$1 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$2 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$3 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$4 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$5 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$6 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$7 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$8 +net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$9 +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithEagerNavigation +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithEagerNavigation$OfAnnotatedElement +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$ForLoadedReturnType +net.bytebuddy.asm.Advice$Dispatcher$Inlining +net.bytebuddy.asm.Advice$Return +jdk.proxy2.$Proxy50 +net.bytebuddy.asm.Advice$Enter +jdk.proxy2.$Proxy51 +net.bytebuddy.asm.Advice$Local +net.bytebuddy.asm.Advice$OnNonDefaultValue +jdk.proxy2.$Proxy52 +net.bytebuddy.asm.Advice$This +jdk.proxy2.$Proxy53 +net.bytebuddy.asm.Advice$Origin +jdk.proxy2.$Proxy54 +net.bytebuddy.asm.Advice$AllArguments +jdk.proxy2.$Proxy55 +net.bytebuddy.dynamic.ClassFileLocator$Resolution$Explicit +net.bytebuddy.utility.StreamDrainer +net.bytebuddy.utility.OpenedClassReader +net.bytebuddy.utility.AsmClassReader$ForAsm +net.bytebuddy.jar.asm.ClassReader +net.bytebuddy.asm.Advice$Dispatcher$Resolved$AbstractBase +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter$WithRetainedEnterType +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter$WithDiscardedEnterType +net.bytebuddy.asm.Advice$ArgumentHandler +net.bytebuddy.asm.Advice$Dispatcher$Inlining$CodeTranslationVisitor +net.bytebuddy.asm.Advice$OffsetMapping$ForArgument$Unresolved$Factory +net.bytebuddy.asm.Advice$Argument +net.bytebuddy.asm.Advice$OffsetMapping$ForAllArguments$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForThisReference$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForField +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved$WithImplicitType +net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved$WithExplicitType +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$ReaderFactory +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$WithImplicitType +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$WithExplicitType +net.bytebuddy.asm.Advice$FieldGetterHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForFieldHandle$Unresolved$WriterFactory +net.bytebuddy.asm.Advice$FieldSetterHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForOrigin$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForSelfCallHandle$Factory +net.bytebuddy.asm.Advice$SelfCallHandle +net.bytebuddy.asm.Advice$OffsetMapping$ForHandle$Factory +net.bytebuddy.asm.Advice$Handle +net.bytebuddy.asm.Advice$OffsetMapping$ForDynamicConstant$Factory +net.bytebuddy.asm.Advice$DynamicConstant +net.bytebuddy.asm.Advice$OffsetMapping$ForUnusedValue$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForStubValue +net.bytebuddy.asm.Advice$OffsetMapping$Target +net.bytebuddy.asm.Advice$OffsetMapping$ForThrowable$Factory +net.bytebuddy.asm.Advice$Thrown +net.bytebuddy.asm.Advice$OffsetMapping$ForExitValue$Factory +net.bytebuddy.asm.Advice$Exit +net.bytebuddy.asm.Advice$OffsetMapping$Factory$Illegal +net.bytebuddy.asm.Advice$OffsetMapping$ForLocalValue$Factory +net.bytebuddy.description.annotation.AnnotationValue$ForTypeDescription +net.bytebuddy.description.annotation.AnnotationValue$ForMismatchedType +net.bytebuddy.asm.Advice$OffsetMapping$Factory$AdviceType +net.bytebuddy.asm.Advice$FieldValue +net.bytebuddy.asm.Advice$Unused +net.bytebuddy.asm.Advice$StubValue +net.bytebuddy.asm.Advice$OffsetMapping$ForStackManipulation +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$OfMethodParameter +net.bytebuddy.description.type.TypeList$Generic$AbstractBase +net.bytebuddy.description.type.TypeList$Generic$Explicit +net.bytebuddy.description.type.TypeList$Explicit +net.bytebuddy.implementation.bytecode.StackSize +net.bytebuddy.asm.Advice$OffsetMapping$ForThisReference +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForDefaultValue +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForDefaultValue$ReadOnly +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForDefaultValue$ReadWrite +net.bytebuddy.description.enumeration.EnumerationDescription$AbstractBase +net.bytebuddy.description.enumeration.EnumerationDescription$ForLoadedEnumeration +net.bytebuddy.description.annotation.AnnotationValue$ForEnumerationDescription +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$1 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$2 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$3 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$4 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedMethod$5 +sun.reflect.generics.tree.ArrayTypeSignature +sun.reflect.generics.tree.BottomSignature +sun.reflect.generics.tree.Wildcard +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableParameterType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableParameterType$Dispatcher +jdk.internal.reflect.GeneratedMethodAccessor1 +net.bytebuddy.description.type.$Proxy56 +net.bytebuddy.asm.Advice$OffsetMapping$ForAllArguments +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$Chained +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForComponentType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForComponentType$AnnotatedParameterizedType +java.lang.reflect.AnnotatedArrayType +net.bytebuddy.description.type.$Proxy57 +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler$Suppressing +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler$Bound +net.bytebuddy.asm.Advice$Dispatcher$SuppressionHandler$NoOp +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForType +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$Bound +net.bytebuddy.asm.Advice$OnDefaultValue +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$1 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$2 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$3 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$4 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$5 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$6 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$7 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$8 +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$9 +java.lang.reflect.WildcardType +sun.reflect.generics.reflectiveObjects.WildcardTypeImpl +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedMethodReturnType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedMethodReturnType$Dispatcher +net.bytebuddy.description.type.$Proxy58 +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$ForLoadedType +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$OfNonDefault +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit$WithoutExceptionHandler +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit$WithExceptionHandler +net.bytebuddy.asm.Advice$OffsetMapping$ForEnterValue$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForReturnValue$Factory +net.bytebuddy.asm.Advice$OffsetMapping$ForReturnValue +net.bytebuddy.asm.Advice$OffsetMapping$ForEnterValue +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$Disabled +net.bytebuddy.asm.Advice$ExceptionHandler$Default +net.bytebuddy.asm.Advice$ExceptionHandler$Default$1 +net.bytebuddy.asm.Advice$ExceptionHandler$Default$2 +net.bytebuddy.asm.Advice$ExceptionHandler$Default$3 +net.bytebuddy.implementation.SuperMethodCall +net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$Entry +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForStatic +jdk.internal.reflect.GeneratedMethodAccessor2 +net.bytebuddy.asm.Advice$OffsetMapping$ForInstrumentedType +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ConstructorShortcut +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ConstructorShortcut$1 +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForHashCode +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$ForEquals +jdk.proxy2.$Proxy59 +net.bytebuddy.asm.Advice$OffsetMapping$ForArgument +net.bytebuddy.asm.Advice$OffsetMapping$ForArgument$Unresolved +org.mockito.internal.creation.bytebuddy.MockMethodAdvice$SelfCallInfo +org.mockito.internal.util.reflection.ModuleMemberAccessor +org.mockito.internal.util.reflection.InstrumentationMemberAccessor +net.bytebuddy.dynamic.loading.InjectionClassLoader +net.bytebuddy.dynamic.loading.ByteArrayClassLoader +net.bytebuddy.implementation.MethodCall +net.bytebuddy.implementation.MethodCall$WithoutSpecifiedTarget +net.bytebuddy.dynamic.loading.ClassFilePostProcessor +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PackageLookupStrategy$CreationAction +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PackageLookupStrategy +net.bytebuddy.utility.JavaModule +net.bytebuddy.utility.JavaModule$Resolver +net.bytebuddy.utility.$Proxy60 +net.bytebuddy.utility.JavaModule$Module +net.bytebuddy.utility.$Proxy61 +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PackageLookupStrategy$ForJava9CapableVm +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy$CreationAction +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy$Initializable +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$SynchronizationStrategy$ForJava8CapableVm +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PersistenceHandler +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PersistenceHandler$1 +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PersistenceHandler$2 +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy$Trivial +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy$Definition +net.bytebuddy.dynamic.loading.ClassFilePostProcessor$NoOp +org.mockito.internal.util.reflection.InstrumentationMemberAccessor$Dispatcher +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$1 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$2 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$3 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$4 +net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$5 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter +net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder +net.bytebuddy.dynamic.TypeResolutionStrategy +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ImplementationDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$TypeVariableDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ExceptionDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Valuable +net.bytebuddy.implementation.attribute.TypeAttributeAppender +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator +net.bytebuddy.dynamic.DynamicType$Builder$InnerTypeDefinition +net.bytebuddy.dynamic.DynamicType$Builder$InnerTypeDefinition$ForType +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$InnerTypeDefinitionForTypeAdapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$InnerTypeDefinitionForMethodAdapter +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ImplementationDefinition$Optional +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Initial +net.bytebuddy.dynamic.DynamicType$Builder$TypeVariableDefinition +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$Valuable +net.bytebuddy.dynamic.DynamicType$Builder$RecordComponentDefinition +net.bytebuddy.dynamic.DynamicType$Builder$RecordComponentDefinition$Optional +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry +net.bytebuddy.dynamic.scaffold.MethodRegistry +net.bytebuddy.dynamic.scaffold.FieldRegistry +net.bytebuddy.implementation.Implementation$Target$Factory +net.bytebuddy.dynamic.scaffold.TypeWriter$RecordComponentPool +net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool +net.bytebuddy.description.modifier.ModifierContributor +net.bytebuddy.description.modifier.ModifierContributor$ForType +net.bytebuddy.description.modifier.ModifierContributor$ForMethod +net.bytebuddy.description.modifier.ModifierContributor$ForField +net.bytebuddy.description.modifier.Visibility +net.bytebuddy.description.modifier.TypeManifestation +net.bytebuddy.description.modifier.ModifierContributor$Resolver +net.bytebuddy.description.type.TypeDescription$AbstractBase$OfSimpleType +net.bytebuddy.dynamic.scaffold.InstrumentedType$Default +net.bytebuddy.dynamic.scaffold.TypeInitializer$None +net.bytebuddy.implementation.LoadedTypeInitializer$NoOp +net.bytebuddy.description.type.TypeDescription$LazyProxy +net.bytebuddy.description.modifier.Ownership +net.bytebuddy.description.modifier.ModifierContributor$ForParameter +net.bytebuddy.description.modifier.SyntheticState +net.bytebuddy.description.modifier.EnumerationState +net.bytebuddy.description.TypeVariableSource$Visitor +jdk.proxy2.$Proxy62 +net.bytebuddy.description.type.TypeList$Generic$ForLoadedTypes +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$ForDetachment +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default +net.bytebuddy.dynamic.scaffold.FieldRegistry$Compiled +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default +net.bytebuddy.dynamic.scaffold.MethodRegistry$Prepared +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry$Default +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry$Compiled +net.bytebuddy.implementation.attribute.TypeAttributeAppender$ForInstrumentedType +net.bytebuddy.implementation.attribute.AnnotationAppender$Target +net.bytebuddy.implementation.attribute.AnnotationAppender +net.bytebuddy.asm.AsmVisitorWrapper$NoOp +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ImplementationDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodMatchAdapter +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ReceiverTypeDefinition +net.bytebuddy.implementation.MethodCall$MethodLocator$Factory +net.bytebuddy.implementation.MethodCall$TerminationHandler$Factory +net.bytebuddy.implementation.MethodCall$MethodInvoker$Factory +net.bytebuddy.implementation.MethodCall$TargetHandler$Factory +net.bytebuddy.implementation.MethodCall$ArgumentLoader$Factory +net.bytebuddy.implementation.MethodCall$MethodLocator +net.bytebuddy.implementation.MethodCall$MethodLocator$ForExplicitMethod +net.bytebuddy.implementation.MethodCall$TargetHandler$ForField$Location +net.bytebuddy.implementation.MethodCall$TargetHandler$ForSelfOrStaticInvocation$Factory +net.bytebuddy.implementation.MethodCall$TargetHandler +net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation$Factory +net.bytebuddy.implementation.MethodCall$MethodInvoker +net.bytebuddy.implementation.MethodCall$TerminationHandler +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple$1 +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple$2 +net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple$3 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler$ForImplementation +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler$Compiled +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ReceiverTypeDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$AbstractBase$Adapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodMatchAdapter$AnnotationAdapter +net.bytebuddy.dynamic.Transformer +net.bytebuddy.implementation.attribute.MethodAttributeAppender +net.bytebuddy.implementation.attribute.MethodAttributeAppender$NoOp +net.bytebuddy.dynamic.Transformer$NoOp +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Entry +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodCall$Factory +net.bytebuddy.implementation.MethodCall$MethodInvoker$ForVirtualInvocation$WithImplicitType +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodParameter +net.bytebuddy.implementation.MethodCall$TargetHandler$Resolved +net.bytebuddy.implementation.MethodCall$ArgumentLoader$ArgumentProvider +net.bytebuddy.implementation.MethodCall$ArgumentLoader$ForMethodParameter$Factory +net.bytebuddy.dynamic.TypeResolutionStrategy$Resolved +net.bytebuddy.dynamic.TypeResolutionStrategy$Passive +net.bytebuddy.pool.TypePool$AbstractBase +net.bytebuddy.pool.TypePool$AbstractBase$Hierarchical +net.bytebuddy.pool.TypePool$ClassLoading +net.bytebuddy.pool.TypePool$Resolution +net.bytebuddy.pool.TypePool$CacheProvider +net.bytebuddy.pool.TypePool$Empty +net.bytebuddy.pool.TypePool$CacheProvider$Simple +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithResolvedErasure +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$ForAttachment +net.bytebuddy.description.method.MethodList$TypeSubstituting +net.bytebuddy.description.method.MethodDescription$InGenericShape +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$ForRawType +net.bytebuddy.matcher.VisibilityMatcher +net.bytebuddy.description.method.MethodDescription$TypeSubstituting +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$ForGenerifiedErasure +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType$ForErasure +net.bytebuddy.description.type.TypeList$Generic$ForLoadedTypes$OfTypeVariables +net.bytebuddy.description.method.MethodDescription$Token +net.bytebuddy.matcher.TypeSortMatcher +net.bytebuddy.description.ByteCodeElement$Token$TokenList +net.bytebuddy.description.method.ParameterList$TypeSubstituting +net.bytebuddy.description.method.ParameterDescription$InGenericShape +net.bytebuddy.description.type.TypeList$Generic$ForDetachedTypes +net.bytebuddy.description.type.TypeList$Generic$OfConstructorExceptionTypes +jdk.internal.vm.annotation.IntrinsicCandidate +com.sun.proxy.jdk.proxy1.$Proxy63 +net.bytebuddy.description.annotation.AnnotationList$Explicit +net.bytebuddy.description.type.TypeDescription$Generic$LazyProxy +jdk.proxy2.$Proxy64 +net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder$InstrumentableMatcher +net.bytebuddy.description.method.MethodList$ForTokens +net.bytebuddy.description.method.MethodDescription$Latent +net.bytebuddy.description.method.ParameterList$ForTokens +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithLazyNavigation +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithLazyNavigation$OfAnnotatedElement +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$ForLoadedSuperClass +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry +net.bytebuddy.description.type.TypeList$Generic$ForDetachedTypes$WithResolvedErasure +net.bytebuddy.description.type.TypeList$Generic$OfLoadedInterfaceTypes +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Harmonized +net.bytebuddy.description.method.MethodDescription$TypeToken +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Harmonizer$ForJavaMethod$Token +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry$Initial +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry$Resolved +net.bytebuddy.dynamic.scaffold.MethodGraph$Node +net.bytebuddy.description.method.ParameterDescription$TypeSubstituting +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Entry$Resolved$Node +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Detached +net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default$Key$Store$Graph +net.bytebuddy.dynamic.scaffold.MethodGraph$Linked$Delegation +net.bytebuddy.matcher.MethodParameterTypeMatcher +net.bytebuddy.matcher.FailSafeMatcher +net.bytebuddy.dynamic.scaffold.MethodGraph$NodeList +net.bytebuddy.dynamic.scaffold.MethodGraph$Node$Sort +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Prepared$Entry +net.bytebuddy.description.method.MethodDescription$Latent$TypeInitializer +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Prepared +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool +net.bytebuddy.dynamic.scaffold.MethodRegistry$Compiled +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$1 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$2 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$3 +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator$ForTypeAnnotations +net.bytebuddy.description.annotation.AnnotationList$Empty +net.bytebuddy.description.type.TypeList$Generic$ForDetachedTypes$OfTypeVariables +net.bytebuddy.description.type.PackageDescription$AbstractBase +net.bytebuddy.description.type.PackageDescription$Simple +net.bytebuddy.description.field.FieldList$AbstractBase +net.bytebuddy.description.field.FieldList$ForTokens +net.bytebuddy.description.method.MethodDescription$SignatureToken +net.bytebuddy.description.annotation.AnnotationValue$ForDescriptionArray +net.bytebuddy.description.annotation.AnnotationValue$Sort +net.bytebuddy.description.annotation.AnnotationValue$State +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$Factory +net.bytebuddy.implementation.Implementation$Target +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$OriginTypeResolver +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$OriginTypeResolver$1 +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget$OriginTypeResolver$2 +net.bytebuddy.implementation.Implementation$Target$AbstractBase +net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation +net.bytebuddy.implementation.Implementation$Target$AbstractBase$DefaultMethodInvocation +net.bytebuddy.implementation.Implementation$Target$AbstractBase$DefaultMethodInvocation$1 +net.bytebuddy.implementation.Implementation$Target$AbstractBase$DefaultMethodInvocation$2 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Handler$ForImplementation$Compiled +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record +net.bytebuddy.implementation.MethodCall$Appender +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodCall +net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation +net.bytebuddy.implementation.MethodCall$TargetHandler$ForSelfOrStaticInvocation +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Compiled$Entry +net.bytebuddy.implementation.SuperMethodCall$Appender +net.bytebuddy.implementation.SuperMethodCall$Appender$TerminationHandler +net.bytebuddy.implementation.SuperMethodCall$Appender$TerminationHandler$1 +net.bytebuddy.implementation.SuperMethodCall$Appender$TerminationHandler$2 +net.bytebuddy.dynamic.scaffold.MethodRegistry$Default$Compiled +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default$Compiled +net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool$Record +net.bytebuddy.dynamic.scaffold.RecordComponentRegistry$Default$Compiled +net.bytebuddy.dynamic.scaffold.TypeWriter$RecordComponentPool$Record +net.bytebuddy.pool.TypePool$Explicit +net.bytebuddy.pool.TypePool$CacheProvider$NoOp +net.bytebuddy.dynamic.scaffold.TypeWriter +net.bytebuddy.dynamic.scaffold.TypeWriter$Default +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ClassDumpAction$Dispatcher +net.bytebuddy.dynamic.scaffold.inline.MethodRebaseResolver +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation +net.bytebuddy.utility.visitor.MetadataAwareClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation$CreationClassVisitor +net.bytebuddy.utility.visitor.ContextClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation$ImplementationContextClassVisitor +net.bytebuddy.dynamic.scaffold.TypeInitializer$Drain +net.bytebuddy.description.type.RecordComponentList$ForTokens +net.bytebuddy.description.type.RecordComponentDescription +net.bytebuddy.description.type.RecordComponentDescription$InDefinedShape +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ClassDumpAction$Dispatcher$Disabled +net.bytebuddy.utility.AsmClassWriter$Factory$Default$EmptyAsmClassReader +net.bytebuddy.utility.AsmClassWriter$ForAsm +net.bytebuddy.implementation.Implementation$Context$FrameGeneration +net.bytebuddy.implementation.Implementation$Context$FrameGeneration$1 +net.bytebuddy.implementation.Implementation$Context$FrameGeneration$2 +net.bytebuddy.implementation.Implementation$Context$FrameGeneration$3 +net.bytebuddy.implementation.Implementation$Context$ExtractableView$AbstractBase +net.bytebuddy.implementation.Implementation$Context$Default +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod +net.bytebuddy.implementation.Implementation$Context$Default$DelegationRecord +net.bytebuddy.implementation.Implementation$Context$Default$AccessorMethodDelegation +net.bytebuddy.implementation.Implementation$Context$Default$FieldGetterDelegation +net.bytebuddy.implementation.Implementation$Context$Default$FieldSetterDelegation +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$ValidatingFieldVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$ValidatingMethodVisitor +net.bytebuddy.jar.asm.signature.SignatureVisitor +net.bytebuddy.jar.asm.signature.SignatureWriter +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$ForSignatureVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint$ForClassFileVersion +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint$ForClass +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor$Constraint$Compound +net.bytebuddy.implementation.attribute.AnnotationAppender$Default +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnType +net.bytebuddy.implementation.attribute.AnnotationAppender$ForTypeAnnotations +java.util.AbstractList$SubList +net.bytebuddy.jar.asm.TypeReference +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$AccessBridgeWrapper +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$Sort +net.bytebuddy.description.modifier.Visibility$1 +net.bytebuddy.description.type.TypeList$Generic$OfMethodExceptionTypes +net.bytebuddy.implementation.MethodCall$TargetHandler$ForSelfOrStaticInvocation$Resolved +net.bytebuddy.implementation.bytecode.Duplication +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodCall$Resolved +net.bytebuddy.implementation.bytecode.ByteCodeAppender$Size +net.bytebuddy.implementation.bytecode.StackManipulation$Compound +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$MethodLoading$TypeCastingHandler +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$OffsetLoading +net.bytebuddy.implementation.bytecode.member.MethodInvocation +net.bytebuddy.implementation.bytecode.member.MethodInvocation$WithImplicitInvocationTargetType +net.bytebuddy.implementation.bytecode.member.MethodInvocation$Invocation +net.bytebuddy.implementation.bytecode.member.MethodReturn +net.bytebuddy.implementation.bytecode.StackManipulation$Size +net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodParameter$Resolved +net.bytebuddy.implementation.MethodCall$ArgumentLoader +net.bytebuddy.implementation.MethodCall$ArgumentLoader$ForMethodParameter +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveWideningDelegate +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveWideningDelegate$WideningStackManipulation +net.bytebuddy.description.type.TypeList$Generic$OfMethodExceptionTypes$TypeProjection +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableExceptionType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableExceptionType$Dispatcher +net.bytebuddy.description.type.$Proxy65 +net.bytebuddy.matcher.SignatureTokenMatcher +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation$AbstractBase +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation$Simple +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$MethodLoading +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$MethodLoading$TypeCastingHandler$NoOp +net.bytebuddy.dynamic.scaffold.TypeInitializer$Drain$Default +net.bytebuddy.description.method.ParameterList$Empty +net.bytebuddy.description.type.TypeList$Generic$Empty +net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForNonImplementedMethod +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$UnresolvedType +net.bytebuddy.dynamic.DynamicType +net.bytebuddy.dynamic.DynamicType$Unloaded +net.bytebuddy.dynamic.DynamicType$AbstractBase +net.bytebuddy.dynamic.DynamicType$Default +net.bytebuddy.dynamic.DynamicType$Default$Unloaded +net.bytebuddy.dynamic.DynamicType$Loaded +net.bytebuddy.dynamic.loading.InjectionClassLoader$Strategy +net.bytebuddy.dynamic.DynamicType$Default$Loaded +java.lang.invoke.LambdaForm$MH/0x00007faab42dc000 +java.lang.invoke.MethodHandleImpl$WrappedMember +java.lang.invoke.MethodHandleImpl$CasesHolder +java.lang.invoke.MethodHandleImpl$LoopClauses +java.lang.invoke.MethodHandleImpl$ArrayAccess +java.lang.invoke.MethodHandleImpl$2 +java.lang.invoke.MethodHandleImpl$ArrayAccessor +java.lang.invoke.MethodHandleImpl$ArrayAccessor$1 +java.lang.invoke.LambdaForm$DMH/0x00007faab42dc400 +java.lang.invoke.LambdaForm$DMH/0x00007faab42dc800 +java.lang.invoke.LambdaForm$MH/0x00007faab42dcc00 +java.lang.invoke.LambdaForm$MH/0x00007faab42dd000 +java.lang.invoke.BoundMethodHandle$Species_LLL +java.lang.invoke.LambdaForm$MH/0x00007faab42dd400 +java.lang.invoke.LambdaForm$MH/0x00007faab42dd800 +net.bytebuddy.dynamic.loading.ByteArrayClassLoader$ClassDefinitionAction +net.bytebuddy.dynamic.loading.PackageDefinitionStrategy$Definition$Trivial +org.mockito.internal.util.reflection.InstrumentationMemberAccessor$Dispatcher$ByteBuddy$csVRoV61 +java.lang.invoke.LambdaForm$DMH/0x00007faab42de000 +org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleanerProvider +org.mockito.internal.configuration.InjectingAnnotationEngine +org.mockito.internal.configuration.IndependentAnnotationEngine +org.mockito.internal.configuration.FieldAnnotationProcessor +org.mockito.internal.configuration.MockAnnotationProcessor +org.mockito.Captor +org.mockito.internal.configuration.CaptorAnnotationProcessor +org.mockito.internal.configuration.SpyAnnotationEngine +org.mockito.internal.util.ConsoleMockitoLogger +org.mockito.plugins.MockResolver +org.mockito.plugins.DoNotMockEnforcer +org.mockito.internal.configuration.DefaultDoNotMockEnforcer +org.mockito.internal.creation.instance.DefaultInstantiatorProvider +org.mockito.internal.creation.instance.ObjenesisInstantiator +org.objenesis.Objenesis +org.objenesis.ObjenesisBase +org.objenesis.ObjenesisStd +org.objenesis.strategy.InstantiatorStrategy +org.objenesis.strategy.BaseInstantiatorStrategy +org.objenesis.strategy.StdInstantiatorStrategy +org.objenesis.instantiator.ObjectInstantiator +org.mockito.internal.util.Supplier +org.mockito.internal.configuration.MockAnnotationProcessor$$Lambda$664/0x00007faab42e0650 +org.mockito.ArgumentMatchers +org.mockito.Mockito +org.mockito.ArgumentMatcher +org.mockito.verification.VerificationMode +org.mockito.verification.VerificationAfterDelay +org.mockito.verification.VerificationWithTimeout +org.mockito.MockitoFramework +org.mockito.session.MockitoSessionBuilder +org.mockito.internal.MockitoCore +org.mockito.stubbing.BaseStubber +org.mockito.stubbing.LenientStubber +org.mockito.ScopedMock +org.mockito.MockedStatic +org.mockito.MockedConstruction +org.mockito.MockingDetails +org.mockito.exceptions.misusing.NotAMockException +org.mockito.internal.verification.api.VerificationData +org.mockito.stubbing.Stubber +org.mockito.InOrder +org.mockito.exceptions.misusing.DoNotMockException +org.mockito.internal.verification.api.VerificationDataInOrder +org.mockito.MockSettings +org.mockito.mock.MockCreationSettings +org.mockito.internal.creation.settings.CreationSettings +org.mockito.internal.creation.MockSettingsImpl +org.mockito.mock.MockName +org.mockito.mock.SerializableMode +org.mockito.internal.util.MockCreationValidator +org.mockito.internal.util.MockUtil +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$1 +org.mockito.mock.MockType +org.mockito.internal.util.MockNameImpl +org.mockito.plugins.DoNotMockEnforcer$Cache +org.mockito.internal.handler.MockHandlerFactory +org.mockito.invocation.MockHandler +org.mockito.internal.handler.MockHandlerImpl +org.mockito.invocation.MatchableInvocation +org.mockito.stubbing.OngoingStubbing +org.mockito.stubbing.Stubbing +org.mockito.invocation.InvocationContainer +org.mockito.internal.invocation.MatchersBinder +org.mockito.internal.stubbing.InvocationContainerImpl +org.mockito.invocation.StubInfo +org.mockito.internal.verification.RegisteredInvocations +org.mockito.internal.verification.DefaultRegisteredInvocations +org.mockito.internal.stubbing.DoAnswerStyleStubbing +org.mockito.internal.handler.NullResultGuardian +org.mockito.internal.handler.InvocationNotifierHandler +org.mockito.listeners.MethodInvocationReport +org.mockito.internal.creation.bytebuddy.MockFeatures +net.bytebuddy.TypeCache$SimpleKey +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$MockitoMockKey +org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$$Lambda$665/0x00007faab42e8000 +net.bytebuddy.TypeCache$LookupKey +org.mockito.internal.creation.bytebuddy.TypeSupport +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$$Lambda$666/0x00007faab42e8650 +org.mockito.internal.util.concurrent.WeakConcurrentMap$WeakKey +org.mockito.internal.util.concurrent.WeakConcurrentMap$LatentKey +sun.instrument.InstrumentationImpl$$Lambda$667/0x00007faab41e0010 +sun.instrument.InstrumentationImpl$$Lambda$668/0x00007faab41e0248 +net.bytebuddy.dynamic.ClassFileLocator$Simple +net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder +net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder +net.bytebuddy.description.type.TypeList$Generic$OfLoadedInterfaceTypes$TypeProjection +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedInterface +net.bytebuddy.description.field.FieldList$ForLoadedFields +net.bytebuddy.utility.FieldComparator +com.networknt.schema.SpecVersion$VersionFlag +sun.reflect.annotation.TypeAnnotation$TypeAnnotationTarget +sun.reflect.annotation.TypeAnnotationParser +sun.reflect.annotation.TypeAnnotation +sun.reflect.annotation.TypeAnnotation$LocationInfo +sun.reflect.annotation.TypeAnnotation$LocationInfo$Location +sun.reflect.annotation.AnnotatedTypeFactory +sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$Simple +net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType$Latent +java.lang.Class$EnclosingMethodInfo +net.bytebuddy.description.type.RecordComponentDescription$Token +net.bytebuddy.implementation.attribute.TypeAttributeAppender$ForInstrumentedType$Differentiating +net.bytebuddy.asm.AsmVisitorWrapper$AbstractBase +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$ParameterWritingVisitorWrapper +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$ParameterWritingVisitorWrapper$ParameterAddingClassVisitor +net.bytebuddy.asm.AsmVisitorWrapper$Compound +net.bytebuddy.pool.TypePool$Default +net.bytebuddy.pool.TypePool$Default$TypeExtractor +net.bytebuddy.pool.TypePool$Default$ReaderMode +net.bytebuddy.dynamic.scaffold.inline.InliningImplementationMatcher +net.bytebuddy.dynamic.scaffold.MethodGraph$Node$Simple +net.bytebuddy.dynamic.scaffold.MethodGraph$Simple +net.bytebuddy.dynamic.scaffold.MethodGraph$Empty +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RegistryContextClassVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor +net.bytebuddy.jar.asm.commons.Remapper +net.bytebuddy.jar.asm.commons.SimpleRemapper +net.bytebuddy.jar.asm.commons.ClassRemapper +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$OpenedClassRemapper +net.bytebuddy.dynamic.scaffold.inline.MethodRebaseResolver$Disabled +net.bytebuddy.dynamic.scaffold.inline.MethodRebaseResolver$Resolution +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$ContextRegistry +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$InitializationHandler +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$AttributeObtainingRecordComponentVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$AttributeObtainingFieldVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$AttributeObtainingMethodVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$CodePreservingMethodVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$DeduplicatingClassVisitor +net.bytebuddy.jar.asm.Context +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$InitializationHandler$Creating +net.bytebuddy.implementation.Implementation$Context$Disabled +org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$ParameterWritingVisitorWrapper$MethodParameterStrippingMethodVisitor +net.bytebuddy.dynamic.scaffold.TypeWriter$Default$SignatureKey +net.bytebuddy.matcher.DescriptorMatcher +software.amazon.lambda.powertools.validation.Validation +net.bytebuddy.description.method.ParameterDescription$Token +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$ForLoadedType$ParameterArgumentTypeList +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForTypeArgument +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForTypeArgument$AnnotatedParameterizedType +java.lang.reflect.AnnotatedParameterizedType +net.bytebuddy.description.type.$Proxy66 +net.bytebuddy.description.type.TypeDescription$Generic$OfWildcardType +net.bytebuddy.description.type.TypeDescription$Generic$OfWildcardType$ForLoadedType +net.bytebuddy.description.type.TypeDescription$Generic$OfWildcardType$Latent +net.bytebuddy.description.type.TypeDescription$Generic$OfWildcardType$ForLoadedType$WildcardUpperBoundTypeList +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForWildcardUpperBoundType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForWildcardUpperBoundType$AnnotatedWildcardType +java.lang.reflect.AnnotatedWildcardType +net.bytebuddy.description.type.$Proxy67 +net.bytebuddy.description.type.TypeDescription$Generic$OfWildcardType$ForLoadedType$WildcardLowerBoundTypeList +net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$Latent +net.bytebuddy.description.method.ParameterDescription$Latent +java.lang.annotation.Annotation +net.bytebuddy.dynamic.loading.MultipleParentClassLoader$Builder +net.bytebuddy.dynamic.loading.MultipleParentClassLoader +jdk.internal.reflect.GeneratedMethodAccessor3 +net.bytebuddy.matcher.LatentMatcher$Disjunction +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$OptionalMethodMatchAdapter +net.bytebuddy.description.modifier.SynchronizationState +net.bytebuddy.dynamic.Transformer$ForMethod +net.bytebuddy.dynamic.Transformer$ForMethod$MethodModifierTransformer +net.bytebuddy.dynamic.Transformer$Compound +net.bytebuddy.implementation.attribute.MethodAttributeAppender$ForInstrumentedMethod +net.bytebuddy.implementation.attribute.MethodAttributeAppender$ForInstrumentedMethod$1 +net.bytebuddy.implementation.attribute.MethodAttributeAppender$ForInstrumentedMethod$2 +net.bytebuddy.implementation.attribute.MethodAttributeAppender$Factory$Compound +net.bytebuddy.description.modifier.FieldManifestation +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$Valuable$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$FieldDefinition$Optional$Valuable$AbstractBase$Adapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$FieldDefinitionAdapter +net.bytebuddy.implementation.attribute.FieldAttributeAppender$Factory +net.bytebuddy.description.field.FieldDescription$Token +net.bytebuddy.implementation.attribute.FieldAttributeAppender +net.bytebuddy.implementation.attribute.FieldAttributeAppender$ForInstrumentedField +net.bytebuddy.matcher.LatentMatcher$ForFieldToken +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default$Entry +net.bytebuddy.implementation.FieldAccessor +net.bytebuddy.implementation.FieldAccessor$FieldLocation +net.bytebuddy.implementation.FieldAccessor$PropertyConfigurable +net.bytebuddy.implementation.FieldAccessor$AssignerConfigurable +net.bytebuddy.implementation.FieldAccessor$OwnerTypeLocatable +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor$ForBeanProperty +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor$ForBeanProperty$1 +net.bytebuddy.implementation.FieldAccessor$FieldNameExtractor$ForBeanProperty$2 +net.bytebuddy.implementation.FieldAccessor$ForImplicitProperty +net.bytebuddy.implementation.FieldAccessor$FieldLocation$Relative +net.bytebuddy.implementation.FieldAccessor$FieldLocation$Prepared +net.bytebuddy.dynamic.scaffold.FieldLocator$ForClassHierarchy$Factory +net.bytebuddy.matcher.SuperTypeMatcher +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$TypeVariableDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ExceptionDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Initial$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodDefinitionAdapter +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$TypeVariableDefinition$Annotatable +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Annotatable +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$Annotatable +net.bytebuddy.description.method.ParameterDescription$Token$TypeList +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$Annotatable$AbstractBase +net.bytebuddy.dynamic.DynamicType$Builder$MethodDefinition$ParameterDefinition$Simple$Annotatable$AbstractBase$Adapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodDefinitionAdapter$SimpleParameterAnnotationAdapter +net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Adapter$MethodDefinitionAdapter$AnnotationAdapter +net.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup +net.bytebuddy.dynamic.loading.ClassInjector +net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase +net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup +net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles +net.bytebuddy.dynamic.loading.$Proxy68 +net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup +jdk.proxy2.$Proxy69 +net.bytebuddy.utility.JavaType +net.bytebuddy.description.type.TypeDescription$Latent +net.bytebuddy.utility.JavaType$LatentTypeWithSimpleName +net.bytebuddy.matcher.LatentMatcher$ForMethodToken +net.bytebuddy.matcher.LatentMatcher$ForMethodToken$ResolvedMatcher +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reducing +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod +jdk.internal.reflect.GeneratedMethodAccessor4 +net.bytebuddy.implementation.MethodDelegation$ImplementationDelegate$Compiled$ForStaticCall +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodInvoker +net.bytebuddy.implementation.MethodDelegation$Appender +net.bytebuddy.implementation.bind.MethodDelegationBinder$Processor +net.bytebuddy.implementation.attribute.MethodAttributeAppender$Compound +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$WithoutTypeSubstitution +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod$AttachmentVisitor +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod$TransformedParameterList +net.bytebuddy.implementation.FieldAccessor$ForImplicitProperty$Appender +net.bytebuddy.implementation.FieldAccessor$FieldLocation$Relative$Prepared +net.bytebuddy.dynamic.scaffold.FieldLocator$Resolution +net.bytebuddy.dynamic.scaffold.FieldRegistry$Default$Compiled$Entry +net.bytebuddy.matcher.LatentMatcher$ForFieldToken$ResolvedMatcher +net.bytebuddy.description.field.FieldDescription$SignatureToken +net.bytebuddy.description.type.TypeVariableToken +net.bytebuddy.implementation.attribute.AnnotationAppender$1 +net.bytebuddy.description.annotation.AnnotationValue$Loaded$AbstractBase +net.bytebuddy.description.annotation.AnnotationValue$ForEnumerationDescription$Loaded +net.bytebuddy.description.field.FieldDescription$AbstractBase +net.bytebuddy.description.field.FieldDescription$InDefinedShape$AbstractBase +net.bytebuddy.description.field.FieldDescription$Latent +net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool$Record$ForExplicitField +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnField +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnMethod +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodInvoker$Simple +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Builder +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding$Anonymous +net.bytebuddy.description.type.TypeDefinition$SuperClassIterator +net.bytebuddy.description.field.FieldList$Explicit +net.bytebuddy.dynamic.scaffold.FieldLocator$Resolution$Simple +net.bytebuddy.implementation.bytecode.member.FieldAccess +net.bytebuddy.implementation.bytecode.member.FieldAccess$Defined +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher$AbstractFieldInstruction +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher$FieldGetInstruction +net.bytebuddy.implementation.bytecode.constant.MethodConstant +net.bytebuddy.implementation.bytecode.constant.MethodConstant$CanCache +net.bytebuddy.implementation.bytecode.constant.MethodConstant$ForMethod +net.bytebuddy.implementation.bytecode.constant.MethodConstant$CachedMethod +net.bytebuddy.implementation.bytecode.collection.CollectionFactory +net.bytebuddy.implementation.bytecode.collection.ArrayFactory +net.bytebuddy.implementation.bytecode.collection.ArrayFactory$ArrayCreator +net.bytebuddy.implementation.bytecode.collection.ArrayFactory$ArrayCreator$ForReferenceType +net.bytebuddy.implementation.bytecode.collection.ArrayFactory$ArrayStackManipulation +net.bytebuddy.implementation.auxiliary.MethodCallProxy$AssignableSignatureCall +net.bytebuddy.implementation.auxiliary.AuxiliaryType +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Builder$Build +net.bytebuddy.implementation.bytecode.constant.DefaultValue +net.bytebuddy.implementation.bytecode.constant.IntegerConstant +net.bytebuddy.implementation.bytecode.constant.LongConstant +net.bytebuddy.implementation.bytecode.constant.FloatConstant +net.bytebuddy.implementation.bytecode.constant.DoubleConstant +net.bytebuddy.implementation.bytecode.constant.NullConstant +net.bytebuddy.implementation.bind.MethodDelegationBinder$1 +net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver$Resolution +net.bytebuddy.implementation.Implementation$Context$Default$FieldCacheEntry +net.bytebuddy.implementation.Implementation$Context$Default$CacheValueField +net.bytebuddy.implementation.auxiliary.MethodCallProxy +net.bytebuddy.implementation.MethodAccessorFactory$AccessType +net.bytebuddy.implementation.Implementation$Context$Default$AbstractPropertyAccessorMethod +net.bytebuddy.implementation.Implementation$Context$Default$AccessorMethod +net.bytebuddy.description.method.ParameterList$Explicit$ForTypes +net.bytebuddy.implementation.auxiliary.MethodCallProxy$PrecomputedMethodGraph +net.bytebuddy.implementation.auxiliary.MethodCallProxy$MethodCall +net.bytebuddy.implementation.auxiliary.MethodCallProxy$ConstructorCall +net.bytebuddy.implementation.auxiliary.MethodCallProxy$MethodCall$Appender +net.bytebuddy.implementation.auxiliary.MethodCallProxy$ConstructorCall$Appender +net.bytebuddy.implementation.bytecode.Removal +net.bytebuddy.implementation.bytecode.Removal$1 +net.bytebuddy.implementation.bytecode.Removal$2 +net.bytebuddy.implementation.attribute.AnnotationAppender$Target$OnMethodParameter +net.bytebuddy.implementation.bytecode.member.FieldAccess$AccessDispatcher$FieldPutInstruction +net.bytebuddy.implementation.bytecode.TypeCreation +net.bytebuddy.implementation.bytecode.Duplication$1 +net.bytebuddy.implementation.bytecode.Duplication$2 +net.bytebuddy.implementation.bytecode.Duplication$3 +net.bytebuddy.implementation.bind.ArgumentTypeResolver$ParameterIndexToken +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding$Unique +net.bytebuddy.implementation.bytecode.assign.TypeCasting +net.bytebuddy.description.type.TypeDescription$Generic$Visitor$ForSignatureVisitor$OfTypeArgument +sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedParameterizedTypeImpl +sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedWildcardTypeImpl +net.bytebuddy.dynamic.scaffold.MethodGraph$Node$Unresolved +net.bytebuddy.implementation.Implementation$SpecialMethodInvocation$Illegal +net.bytebuddy.implementation.bind.MethodDelegationBinder$ParameterBinding$Illegal +net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Illegal +com.networknt.schema.SpecVersion +jdk.internal.reflect.GeneratedMethodAccessor5 +jdk.internal.reflect.GeneratedMethodAccessor6 +jdk.internal.reflect.GeneratedMethodAccessor7 +jdk.internal.reflect.GeneratedMethodAccessor8 +jdk.internal.reflect.GeneratedMethodAccessor9 +jdk.internal.reflect.GeneratedMethodAccessor10 +net.bytebuddy.dynamic.scaffold.FieldLocator$Resolution$Illegal +net.bytebuddy.implementation.bytecode.ByteCodeAppender$Simple +net.bytebuddy.dynamic.scaffold.TypeInitializer$Simple +net.bytebuddy.implementation.bytecode.ByteCodeAppender$Compound +net.bytebuddy.implementation.bytecode.constant.ClassConstant +net.bytebuddy.implementation.bytecode.constant.ClassConstant$ForReferenceType +net.bytebuddy.description.type.PackageDescription$ForLoadedPackage +software.amazon.lambda.powertools.validation.Validation$MockitoMock$r3AWkvyb +software.amazon.lambda.powertools.validation.Validation$MockitoMock$r3AWkvyb$auxiliary$a0EM2a2n +software.amazon.lambda.powertools.validation.Validation$MockitoMock$r3AWkvyb$auxiliary$RFyMI1P9 +net.bytebuddy.TypeCache$StorageKey +org.mockito.plugins.MemberAccessor$OnConstruction +org.mockito.plugins.MemberAccessor$ConstructionDispatcher +org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker$$Lambda$669/0x00007faab431f9a8 +java.lang.invoke.LambdaForm$MH/0x00007faab431d800 +java.lang.invoke.LambdaForm$MH/0x00007faab431dc00 +java.lang.invoke.LambdaForm$MH/0x00007faab4320000 +java.lang.invoke.LambdaForm$MH/0x00007faab4320400 +sun.invoke.util.ValueConversions$WrapperCache +java.lang.invoke.LambdaForm$MH/0x00007faab4320800 +java.lang.invoke.LambdaForm$MH/0x00007faab4320c00 +java.lang.invoke.BoundMethodHandle$Species_LLLL +java.lang.invoke.LambdaForm$MH/0x00007faab4321000 +java.lang.invoke.LambdaForm$MH/0x00007faab4321400 +java.lang.invoke.LambdaForm$MH/0x00007faab4321800 +org.mockito.internal.util.reflection.InstrumentationMemberAccessor$$Lambda$670/0x00007faab431fbd0 +org.mockito.invocation.Invocation +org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport +org.mockito.exceptions.base.MockitoSerializationIssue +org.mockito.internal.progress.ThreadSafeMockingProgress +org.mockito.internal.progress.ThreadSafeMockingProgress$1 +org.mockito.internal.progress.MockingProgress +org.mockito.internal.progress.MockingProgressImpl +org.mockito.internal.progress.ArgumentMatcherStorage +org.mockito.verification.VerificationStrategy +org.mockito.internal.progress.ArgumentMatcherStorageImpl +java.util.Stack +org.mockito.internal.progress.MockingProgressImpl$1 +java.lang.invoke.LambdaForm$MH/0x00007faab4321c00 +java.lang.invoke.LambdaForm$MH/0x00007faab4322000 +java.lang.invoke.LambdaForm$MH/0x00007faab4322400 +jdk.internal.reflect.GeneratedMethodAccessor11 +org.aspectj.lang.Signature +jdk.internal.reflect.GeneratedMethodAccessor12 +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate$UnboxingResponsible +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate$ImplicitlyTypedUnboxingResponsible +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveBoxingDelegate +net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveBoxingDelegate$BoxingStackManipulation +jdk.internal.reflect.GeneratedMethodAccessor13 +jdk.internal.reflect.GeneratedMethodAccessor14 +org.aspectj.lang.Signature$MockitoMock$F8wJJwod +org.aspectj.lang.Signature$MockitoMock$F8wJJwod$auxiliary$TN3rx6QT +org.aspectj.lang.Signature$MockitoMock$F8wJJwod$auxiliary$MUj6Pxo6 +com.amazonaws.services.lambda.runtime.LambdaLogger +com.amazonaws.services.lambda.runtime.CognitoIdentity +com.amazonaws.services.lambda.runtime.ClientContext +net.bytebuddy.jar.asm.Label +net.bytebuddy.utility.visitor.StackAwareMethodVisitor +net.bytebuddy.asm.Advice$ArgumentHandler$Factory +net.bytebuddy.asm.Advice$ArgumentHandler$Factory$1 +net.bytebuddy.asm.Advice$ArgumentHandler$Factory$2 +net.bytebuddy.asm.Advice$ArgumentHandler$ForInstrumentedMethod +net.bytebuddy.asm.Advice$ArgumentHandler$ForInstrumentedMethod$Default +net.bytebuddy.asm.Advice$ArgumentHandler$ForInstrumentedMethod$Default$Copying +net.bytebuddy.asm.Advice$ArgumentHandler$ForAdvice +net.bytebuddy.asm.Advice$MethodSizeHandler +net.bytebuddy.asm.Advice$MethodSizeHandler$ForInstrumentedMethod +net.bytebuddy.asm.Advice$MethodSizeHandler$Default +net.bytebuddy.asm.Advice$MethodSizeHandler$ForAdvice +net.bytebuddy.asm.Advice$MethodSizeHandler$Default$WithCopiedArguments +net.bytebuddy.asm.Advice$StackMapFrameHandler +net.bytebuddy.asm.Advice$StackMapFrameHandler$ForInstrumentedMethod +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default +net.bytebuddy.asm.Advice$StackMapFrameHandler$ForPostProcessor +net.bytebuddy.asm.Advice$StackMapFrameHandler$ForAdvice +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$WithPreservedArguments +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$WithPreservedArguments$WithArgumentCopy +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner$ExceptionTableExtractor +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner$ExceptionTableSubstitutor +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForValue$Bound +net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$Relocation$ForLabel +net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner$ExceptionTableCollector +java.util.TreeMap$EntrySet +java.util.TreeMap$EntryIterator +net.bytebuddy.asm.Advice$ArgumentHandler$ForAdvice$Default +net.bytebuddy.asm.Advice$ArgumentHandler$ForAdvice$Default$ForMethodEnter +net.bytebuddy.asm.Advice$MethodSizeHandler$Default$ForAdvice +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$ForAdvice +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$TranslationMode +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$TranslationMode$1 +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$TranslationMode$2 +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$TranslationMode$3 +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$Initialization +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$Initialization$1 +net.bytebuddy.asm.Advice$StackMapFrameHandler$Default$Initialization$2 +net.bytebuddy.asm.Advice$OffsetMapping$Sort +net.bytebuddy.asm.Advice$OffsetMapping$Sort$1 +net.bytebuddy.asm.Advice$OffsetMapping$Sort$2 +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForStackManipulation +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForVariable +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForVariable$ReadOnly +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForArray +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForArray$ReadOnly +net.bytebuddy.jar.asm.Opcodes +net.bytebuddy.asm.Advice$ArgumentHandler$ForAdvice$Default$ForMethodExit +net.bytebuddy.asm.Advice$OffsetMapping$Target$ForVariable$ReadWrite +net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$OffsetWriting +com.amazonaws.services.lambda.runtime.Context +jdk.internal.reflect.GeneratedMethodAccessor15 +com.amazonaws.services.lambda.runtime.Context$MockitoMock$MawJ5Xou +com.amazonaws.services.lambda.runtime.Context$MockitoMock$MawJ5Xou$auxiliary$QIVtpjYT +com.amazonaws.services.lambda.runtime.Context$MockitoMock$MawJ5Xou$auxiliary$xgUr4uvo +org.aspectj.runtime.internal.AroundClosure +net.bytebuddy.description.type.TypeDescription$Generic$OfGenericArray +net.bytebuddy.description.type.TypeDescription$Generic$OfGenericArray$Latent +jdk.internal.reflect.GeneratedMethodAccessor16 +jdk.internal.reflect.GeneratedMethodAccessor17 +net.bytebuddy.description.type.TypeDescription$ArrayProjection +net.bytebuddy.implementation.bytecode.StackSize$1 +org.aspectj.lang.ProceedingJoinPoint +net.bytebuddy.description.field.FieldDescription$ForLoadedField +net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$ForLoadedFieldType +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedField +net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedField$Dispatcher +net.bytebuddy.description.type.$Proxy70 +org.aspectj.lang.reflect.SourceLocation +org.aspectj.lang.JoinPoint$StaticPart +org.aspectj.lang.JoinPoint$EnclosingStaticPart +net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool$Record$ForImplicitField +org.aspectj.lang.JoinPoint +jdk.internal.reflect.GeneratedMethodAccessor18 +net.bytebuddy.dynamic.Transformer$ForMethod$TransformedMethod$TransformedParameter +sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedArrayTypeImpl +org.aspectj.lang.ProceedingJoinPoint$MockitoMock$alXgCaDh +org.aspectj.lang.ProceedingJoinPoint$MockitoMock$alXgCaDh$auxiliary$XCx5KE2h +org.aspectj.lang.ProceedingJoinPoint$MockitoMock$alXgCaDh$auxiliary$GAQiDCJf +java.lang.invoke.LambdaForm$DMH/0x00007faab4331000 +org.mockito.internal.configuration.IndependentAnnotationEngine$$Lambda$671/0x00007faab4337dc8 +org.mockito.Spy +org.mockito.plugins.AnnotationEngine$NoAction +org.mockito.internal.util.collections.Sets +org.mockito.internal.util.collections.HashCodeAndEqualsSafeSet +org.mockito.internal.configuration.injection.scanner.InjectMocksScanner +org.mockito.InjectMocks +org.mockito.internal.configuration.injection.scanner.MockScanner +org.mockito.internal.util.reflection.FieldReader +org.mockito.internal.util.collections.HashCodeAndEqualsMockWrapper +java.lang.invoke.LambdaForm$MH/0x00007faab4331400 +org.mockito.internal.util.Checks +org.mockito.internal.util.collections.HashCodeAndEqualsSafeSet$1 +org.mockito.internal.configuration.DefaultInjectionEngine +org.mockito.internal.configuration.injection.MockInjection +org.mockito.internal.configuration.injection.MockInjection$OngoingMockInjection +org.mockito.internal.configuration.injection.MockInjectionStrategy +org.mockito.internal.configuration.injection.ConstructorInjection +org.mockito.internal.configuration.injection.PropertyAndSetterInjection +org.mockito.internal.configuration.injection.SpyOnInjectedFieldsHandler +org.mockito.internal.configuration.injection.MockInjectionStrategy$1 +org.mockito.internal.util.reflection.FieldInitializer$ConstructorArgumentResolver +org.mockito.internal.configuration.injection.filter.MockCandidateFilter +org.mockito.internal.configuration.injection.filter.TypeBasedCandidateFilter +org.mockito.internal.configuration.injection.filter.NameBasedCandidateFilter +org.mockito.internal.configuration.injection.filter.TerminalMockCandidateFilter +org.mockito.internal.configuration.InjectingAnnotationEngine$$Lambda$672/0x00007faab4339998 +org.mockito.internal.configuration.InjectingAnnotationEngine$$Lambda$673/0x00007faab4339bc0 +org.junit.jupiter.api.extension.BeforeTestExecutionCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$674/0x00007faab4339fe8 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$675/0x00007faab433a208 +org.junit.jupiter.engine.descriptor.MethodExtensionContext$$Lambda$676/0x00007faab433a430 +org.junit.jupiter.engine.execution.DefaultParameterContext +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$677/0x00007faab433a940 +org.junit.jupiter.params.ResolverFacade$$Lambda$678/0x00007faab433ab98 +org.junit.jupiter.params.ResolverFacade$$Lambda$679/0x00007faab433adb8 +java.lang.invoke.LambdaForm$DMH/0x00007faab433c000 +org.junit.jupiter.params.ResolverFacade$$Lambda$680/0x00007faab433afd8 +java.lang.invoke.LambdaForm$DMH/0x00007faab433c400 +java.lang.invoke.LambdaForm$DMH/0x00007faab433c800 +java.lang.invoke.LambdaForm$MH/0x00007faab433cc00 +org.junit.jupiter.params.ResolverFacade$$Lambda$681/0x00007faab433b200 +org.junit.jupiter.params.converter.ConvertWith +org.junit.jupiter.params.ResolverFacade$$Lambda$682/0x00007faab433b648 +org.junit.jupiter.params.converter.ArgumentConverter +org.junit.jupiter.params.ResolverFacade$$Lambda$683/0x00007faab433ba88 +org.junit.jupiter.params.ResolverFacade$$Lambda$684/0x00007faab433bcd0 +org.junit.jupiter.params.ResolverFacade$Converter +org.junit.jupiter.params.ResolverFacade$$Lambda$685/0x00007faab433e240 +org.junit.jupiter.params.ResolverFacade$$Lambda$686/0x00007faab433e480 +org.junit.jupiter.params.converter.DefaultArgumentConverter +org.junit.platform.commons.support.conversion.ConversionException +org.junit.jupiter.params.converter.ArgumentConversionException +org.junit.jupiter.params.converter.DefaultArgumentConverter$LocaleConversionFormat +org.junit.jupiter.params.converter.DefaultArgumentConverter$$Lambda$687/0x00007faab433efe8 +org.junit.jupiter.params.ResolverFacade$ExecutableParameterDeclaration$$Lambda$688/0x00007faab433f228 +org.junit.jupiter.params.ResolverFacade$ExecutableParameterDeclaration$$Lambda$689/0x00007faab433f480 +org.junit.jupiter.params.EvaluatedArgumentSet$$Lambda$690/0x00007faab433f6a8 +org.junit.jupiter.engine.execution.ParameterResolutionUtils$$Lambda$691/0x00007faab433f8e8 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$692/0x00007faab433fb10 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$693/0x00007faab433fd68 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$694/0x00007faab433d000 +software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7StringHandler +software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7StringHandler$AjcClosure1 +org.aspectj.runtime.reflect.Factory +org.aspectj.lang.reflect.MemberSignature +org.aspectj.lang.reflect.CodeSignature +org.aspectj.lang.reflect.MethodSignature +org.aspectj.lang.reflect.ConstructorSignature +org.aspectj.lang.reflect.FieldSignature +org.aspectj.lang.reflect.AdviceSignature +org.aspectj.lang.reflect.InitializerSignature +org.aspectj.lang.reflect.CatchClauseSignature +org.aspectj.lang.reflect.LockSignature +org.aspectj.lang.reflect.UnlockSignature +org.aspectj.runtime.reflect.SignatureImpl +org.aspectj.runtime.reflect.MemberSignatureImpl +org.aspectj.runtime.reflect.CodeSignatureImpl +org.aspectj.runtime.reflect.MethodSignatureImpl +org.aspectj.runtime.reflect.SignatureImpl$Cache +org.aspectj.runtime.reflect.JoinPointImpl$StaticPartImpl +org.aspectj.runtime.reflect.SourceLocationImpl +org.aspectj.runtime.reflect.JoinPointImpl +jdk.proxy2.$Proxy71 +software.amazon.lambda.powertools.validation.ValidationConfig +software.amazon.lambda.powertools.validation.ValidationConfig$ConfigHolder +com.networknt.schema.JsonSchemaFactory +com.networknt.schema.resource.SchemaLoader +com.networknt.schema.JsonSchemaException +com.networknt.schema.JsonSchemaVersion +com.networknt.schema.resource.SchemaLoaders +com.networknt.schema.resource.SchemaLoaders$Builder +com.networknt.schema.resource.SchemaMappers +com.networknt.schema.resource.SchemaMappers$Builder +com.networknt.schema.JsonSchemaFactory$1 +com.networknt.schema.Version7 +com.networknt.schema.Version7$Holder +com.networknt.schema.JsonMetaSchema +com.networknt.schema.JsonMetaSchema$Builder +com.networknt.schema.InvalidSchemaException +com.networknt.schema.Formats +com.networknt.schema.Format +com.networknt.schema.format.PatternFormat +java.util.regex.Pattern$Loop +java.util.regex.Pattern$Prolog +com.networknt.schema.format.IPv6Format +java.util.regex.CharPredicates$$Lambda$695/0x00007faab41e3c78 +java.util.regex.CharPredicates$$Lambda$696/0x00007faab41e3f00 +com.networknt.schema.format.DateFormat +com.networknt.schema.format.DateTimeFormat +com.networknt.schema.utils.Classes +com.ethlo.time.ITU +com.networknt.schema.format.DateTimeFormat$Ethlo +com.ethlo.time.LeapSecondException +com.networknt.schema.format.DateTimeFormat$$Lambda$697/0x00007faab4346870 +com.networknt.schema.format.EmailFormat +com.networknt.org.apache.commons.validator.routines.EmailValidator +com.networknt.schema.format.IPv6AwareEmailValidator +com.networknt.org.apache.commons.validator.routines.DomainValidator +com.networknt.org.apache.commons.validator.routines.DomainValidator$LazyHolder +com.networknt.org.apache.commons.validator.routines.RegexValidator +com.networknt.schema.format.IdnEmailFormat +com.networknt.schema.format.IdnHostnameFormat +com.networknt.schema.format.AbstractRFC3986Format +com.networknt.schema.format.IriFormat +com.networknt.schema.format.IriReferenceFormat +com.networknt.schema.format.RegexFormat +com.networknt.schema.format.TimeFormat +com.networknt.schema.format.UriFormat +com.networknt.schema.format.UriReferenceFormat +com.networknt.schema.format.DurationFormat +java.util.regex.Pattern$$Lambda$698/0x00007faab41e4380 +java.util.regex.Pattern$Bound +com.networknt.schema.Keyword +com.networknt.schema.ErrorMessageType +com.networknt.schema.ValidatorTypeCode +com.networknt.schema.ValidatorTypeCode$1 +com.networknt.schema.ValidatorFactory +com.networknt.schema.SchemaLocation +com.networknt.schema.JsonNodePath +com.networknt.schema.walk.JsonSchemaWalker +com.networknt.schema.JsonValidator +com.networknt.schema.ValidationMessageHandler +com.networknt.schema.BaseJsonValidator +com.networknt.schema.JsonSchema +com.networknt.schema.ValidationContext +com.networknt.schema.AdditionalPropertiesValidator +com.networknt.schema.FailFastAssertionException +com.networknt.schema.ValidatorTypeCode$$Lambda$699/0x00007faab434b900 +com.networknt.schema.VersionCode +com.networknt.schema.AllOfValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$700/0x00007faab434c288 +com.networknt.schema.AnyOfValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$701/0x00007faab434c7e0 +com.networknt.schema.ConstValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$702/0x00007faab434cd20 +com.networknt.schema.ContainsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$703/0x00007faab434d260 +com.networknt.schema.ContentEncodingValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$704/0x00007faab434d7a0 +com.networknt.schema.ContentMediaTypeValidator +com.fasterxml.jackson.core.JacksonException +com.fasterxml.jackson.core.JsonProcessingException +com.networknt.schema.ValidatorTypeCode$$Lambda$705/0x00007faab434e1e0 +com.networknt.schema.DependenciesValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$706/0x00007faab434e720 +com.networknt.schema.DependentRequired +com.networknt.schema.ValidatorTypeCode$$Lambda$707/0x00007faab434ec60 +com.networknt.schema.DependentSchemas +com.networknt.schema.ValidatorTypeCode$$Lambda$708/0x00007faab434f1a8 +com.networknt.schema.DiscriminatorValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$709/0x00007faab434f6f8 +com.networknt.schema.DynamicRefValidator +com.networknt.schema.InvalidSchemaRefException +com.networknt.schema.ValidatorTypeCode$$Lambda$710/0x00007faab4350000 +com.networknt.schema.EnumValidator +com.fasterxml.jackson.databind.node.JsonNodeCreator +com.fasterxml.jackson.databind.node.BaseJsonNode +com.fasterxml.jackson.databind.node.ContainerNode +com.fasterxml.jackson.databind.node.ArrayNode +com.fasterxml.jackson.databind.node.ValueNode +com.fasterxml.jackson.databind.node.NumericNode +com.fasterxml.jackson.databind.node.DecimalNode +com.networknt.schema.ValidatorTypeCode$$Lambda$711/0x00007faab4353210 +com.networknt.schema.ExclusiveMaximumValidator +com.networknt.schema.ThresholdMixin +com.networknt.schema.ValidatorTypeCode$$Lambda$712/0x00007faab4353950 +com.networknt.schema.ExclusiveMinimumValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$713/0x00007faab4353e90 +com.networknt.schema.FalseValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$714/0x00007faab43543d0 +com.networknt.schema.IfValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$715/0x00007faab4354910 +com.networknt.schema.ItemsValidator202012 +com.networknt.schema.ValidatorTypeCode$$Lambda$716/0x00007faab4354e60 +com.networknt.schema.ItemsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$717/0x00007faab43553b0 +com.networknt.schema.MinMaxContainsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$718/0x00007faab43558f0 +com.networknt.schema.MaxItemsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$719/0x00007faab4355e30 +com.networknt.schema.MaxLengthValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$720/0x00007faab4356370 +com.networknt.schema.MaxPropertiesValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$721/0x00007faab43568b0 +com.networknt.schema.MaximumValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$722/0x00007faab4356df0 +com.networknt.schema.ValidatorTypeCode$$Lambda$723/0x00007faab4357010 +com.networknt.schema.MinItemsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$724/0x00007faab4357550 +com.networknt.schema.MinLengthValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$725/0x00007faab4357a90 +com.networknt.schema.MinPropertiesValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$726/0x00007faab4358000 +com.networknt.schema.MinimumValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$727/0x00007faab4358540 +com.networknt.schema.MultipleOfValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$728/0x00007faab4358a90 +com.networknt.schema.NotAllowedValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$729/0x00007faab4358fd0 +com.networknt.schema.NotValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$730/0x00007faab4359518 +com.networknt.schema.OneOfValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$731/0x00007faab4359a70 +com.networknt.schema.PatternPropertiesValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$732/0x00007faab4359fb0 +com.networknt.schema.PatternValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$733/0x00007faab435a4f0 +com.networknt.schema.PrefixItemsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$734/0x00007faab435aa38 +com.networknt.schema.PropertiesValidator +com.fasterxml.jackson.databind.node.MissingNode +com.networknt.schema.ValidatorTypeCode$$Lambda$735/0x00007faab435b5c8 +com.networknt.schema.PropertyNamesValidator +com.fasterxml.jackson.databind.node.TextNode +com.networknt.schema.ValidatorTypeCode$$Lambda$736/0x00007faab435c158 +com.networknt.schema.ReadOnlyValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$737/0x00007faab435c698 +com.networknt.schema.RecursiveRefValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$738/0x00007faab435cbe0 +com.networknt.schema.RefValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$739/0x00007faab435d128 +com.networknt.schema.RequiredValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$740/0x00007faab435d668 +com.networknt.schema.TrueValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$741/0x00007faab435dba8 +com.networknt.schema.TypeValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$742/0x00007faab435e0f8 +com.networknt.schema.UnevaluatedItemsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$743/0x00007faab435e638 +com.networknt.schema.UnevaluatedPropertiesValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$744/0x00007faab435eb78 +com.networknt.schema.UnionTypeValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$745/0x00007faab435f0b8 +com.networknt.schema.UniqueItemsValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$746/0x00007faab435f5f8 +com.networknt.schema.WriteOnlyValidator +com.networknt.schema.ValidatorTypeCode$$Lambda$747/0x00007faab435fb38 +com.networknt.schema.AbstractKeyword +com.networknt.schema.NonValidationKeyword +com.networknt.schema.AnnotationKeyword +com.networknt.schema.FormatKeyword +com.networknt.schema.utils.StringUtils +com.networknt.schema.JsonSchemaFactory$Builder +com.networknt.schema.resource.DefaultSchemaLoader +com.networknt.schema.resource.SchemaMapper +com.networknt.schema.resource.MetaSchemaMapper +com.networknt.schema.resource.ClasspathSchemaLoader +com.networknt.schema.resource.ClasspathSchemaLoader$$Lambda$748/0x00007faab4361c00 +com.networknt.schema.resource.UriSchemaLoader +software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor +software.amazon.lambda.powertools.common.internal.SystemWrapper +software.amazon.lambda.powertools.validation.ValidationUtils +software.amazon.lambda.powertools.validation.ValidationUtils$$Lambda$749/0x00007faab4362678 +com.networknt.schema.SchemaValidatorsConfig +com.networknt.schema.regex.RegularExpressionFactory +com.networknt.schema.walk.WalkListenerRunner +com.networknt.schema.SchemaValidatorsConfig$Builder +com.networknt.schema.SchemaValidatorsConfig$ImmutableSchemaValidatorsConfig +com.networknt.schema.ApplyDefaultsStrategy +com.networknt.schema.PathType +com.networknt.schema.PathType$$Lambda$750/0x00007faab4363d08 +com.networknt.schema.PathType$$Lambda$751/0x00007faab4363f38 +com.networknt.schema.PathType$$Lambda$752/0x00007faab4364168 +com.networknt.schema.PathType$$Lambda$753/0x00007faab4364398 +com.networknt.schema.PathType$$Lambda$754/0x00007faab43645c8 +com.networknt.schema.PathType$$Lambda$755/0x00007faab43647f8 +com.networknt.schema.PathType$$Lambda$756/0x00007faab4364a28 +com.networknt.schema.PathType$$Lambda$757/0x00007faab4364c58 +com.networknt.schema.regex.JDKRegularExpressionFactory +com.networknt.schema.regex.RegularExpression +com.networknt.schema.JsonSchemaIdValidator +com.networknt.schema.JsonSchemaIdValidator$DefaultJsonSchemaIdValidator +com.networknt.schema.walk.AbstractWalkListenerRunner +com.networknt.schema.walk.DefaultItemWalkListenerRunner +com.networknt.schema.walk.DefaultKeywordWalkListenerRunner +com.networknt.schema.walk.DefaultPropertyWalkListenerRunner +com.networknt.schema.AbsoluteIri +com.networknt.schema.resource.InputStreamSource +com.networknt.schema.resource.ClasspathSchemaLoader$$Lambda$758/0x00007faab4366670 +com.networknt.schema.InputFormat +com.networknt.schema.serialization.JsonMapperFactory +com.networknt.schema.serialization.JsonMapperFactory$Holder +com.fasterxml.jackson.core.Versioned +com.fasterxml.jackson.core.TreeCodec +com.fasterxml.jackson.core.ObjectCodec +com.fasterxml.jackson.databind.ObjectMapper +com.fasterxml.jackson.databind.json.JsonMapper +com.fasterxml.jackson.core.TokenStreamFactory +com.fasterxml.jackson.core.JsonFactory +com.fasterxml.jackson.databind.MappingJsonFactory +com.fasterxml.jackson.databind.jsontype.SubtypeResolver +com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver +com.fasterxml.jackson.databind.DatabindContext +com.fasterxml.jackson.databind.SerializerProvider +com.fasterxml.jackson.databind.ser.DefaultSerializerProvider +com.fasterxml.jackson.databind.ser.DefaultSerializerProvider$Impl +com.fasterxml.jackson.databind.deser.DeserializerFactory +com.fasterxml.jackson.databind.deser.BasicDeserializerFactory +com.fasterxml.jackson.databind.deser.BeanDeserializerFactory +com.fasterxml.jackson.databind.DeserializationContext +com.fasterxml.jackson.databind.deser.DefaultDeserializationContext +com.fasterxml.jackson.databind.deser.DefaultDeserializationContext$Impl +com.fasterxml.jackson.databind.ser.SerializerFactory +com.fasterxml.jackson.databind.ser.BasicSerializerFactory +com.fasterxml.jackson.databind.ser.BeanSerializerFactory +com.fasterxml.jackson.databind.AnnotationIntrospector +com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector +com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy$Provider +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$Provider +com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator +com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator$Base +com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator +com.fasterxml.jackson.databind.util.StdDateFormat +com.fasterxml.jackson.databind.DatabindException +com.fasterxml.jackson.databind.JsonMappingException +com.fasterxml.jackson.databind.node.NullNode +com.fasterxml.jackson.databind.Module$SetupContext +com.fasterxml.jackson.core.JsonGenerator +com.fasterxml.jackson.databind.util.TokenBuffer +com.fasterxml.jackson.databind.introspect.ClassIntrospector +com.fasterxml.jackson.databind.introspect.BasicClassIntrospector +com.fasterxml.jackson.databind.introspect.VisibilityChecker +com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder +com.fasterxml.jackson.core.JsonParser +com.fasterxml.jackson.core.base.ParserMinimalBase +com.fasterxml.jackson.databind.node.TreeTraversingParser +com.fasterxml.jackson.core.util.BufferRecycler$Gettable +com.fasterxml.jackson.core.io.SegmentedStringWriter +com.fasterxml.jackson.core.util.ByteArrayBuilder +com.fasterxml.jackson.core.type.ResolvedType +com.fasterxml.jackson.databind.JavaType +com.fasterxml.jackson.databind.type.TypeBase +com.fasterxml.jackson.databind.type.ArrayType +com.fasterxml.jackson.databind.type.CollectionLikeType +com.fasterxml.jackson.databind.type.CollectionType +com.fasterxml.jackson.databind.type.MapLikeType +com.fasterxml.jackson.databind.type.MapType +com.fasterxml.jackson.databind.exc.MismatchedInputException +com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair +com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector +com.fasterxml.jackson.databind.introspect.Annotated +com.fasterxml.jackson.databind.introspect.TypeResolutionContext +com.fasterxml.jackson.databind.introspect.AnnotatedClass +com.fasterxml.jackson.databind.introspect.AnnotatedMember +com.fasterxml.jackson.databind.introspect.VirtualAnnotatedMember +com.fasterxml.jackson.databind.util.Named +com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition +com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition +com.fasterxml.jackson.databind.BeanProperty +com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase +com.fasterxml.jackson.databind.ser.PropertyWriter +com.fasterxml.jackson.databind.ser.BeanPropertyWriter +com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter +com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter +com.fasterxml.jackson.databind.introspect.AnnotatedWithParams +com.fasterxml.jackson.databind.introspect.AnnotatedMethod +com.fasterxml.jackson.databind.annotation.JsonSerialize +com.fasterxml.jackson.annotation.JsonView +com.fasterxml.jackson.annotation.JsonFormat +com.fasterxml.jackson.annotation.JsonTypeInfo +com.fasterxml.jackson.annotation.JsonRawValue +com.fasterxml.jackson.annotation.JsonUnwrapped +com.fasterxml.jackson.annotation.JsonBackReference +com.fasterxml.jackson.annotation.JsonManagedReference +com.fasterxml.jackson.databind.annotation.JsonDeserialize +com.fasterxml.jackson.annotation.JsonMerge +com.fasterxml.jackson.databind.ext.Java7Support +java.lang.IllegalAccessError +com.fasterxml.jackson.databind.ext.Java7SupportImpl +com.fasterxml.jackson.databind.util.ClassUtil +com.fasterxml.jackson.databind.util.ClassUtil$Ctor +com.fasterxml.jackson.databind.util.LookupCache +com.fasterxml.jackson.databind.util.LRUMap +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Builder +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap +com.fasterxml.jackson.databind.util.internal.Linked +com.fasterxml.jackson.databind.util.internal.LinkedDeque +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$1 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$2 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$DrainStatus$3 +com.fasterxml.jackson.databind.cfg.BaseSettings +com.fasterxml.jackson.databind.type.TypeFactory +com.fasterxml.jackson.databind.type.SimpleType +com.fasterxml.jackson.databind.type.IdentityEqualityType +com.fasterxml.jackson.databind.type.ResolvedRecursiveType +com.fasterxml.jackson.databind.type.PlaceholderForType +com.fasterxml.jackson.databind.type.ReferenceType +com.fasterxml.jackson.databind.type.IterationType +com.fasterxml.jackson.databind.type.TypeParser +com.fasterxml.jackson.databind.type.TypeBindings +com.fasterxml.jackson.core.Base64Variants +com.fasterxml.jackson.core.Base64Variant +com.fasterxml.jackson.core.Base64Variant$PaddingReadBehaviour +com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy +com.fasterxml.jackson.databind.introspect.DefaultAccessorNamingStrategy$RecordNaming +com.fasterxml.jackson.databind.cfg.CacheProvider +com.fasterxml.jackson.databind.cfg.DefaultCacheProvider +com.fasterxml.jackson.databind.cfg.MapperBuilder +com.fasterxml.jackson.databind.json.JsonMapper$Builder +com.fasterxml.jackson.core.io.DataOutputAsStream +com.fasterxml.jackson.core.SerializableString +com.fasterxml.jackson.core.TSFBuilder +com.fasterxml.jackson.core.JsonFactoryBuilder +com.fasterxml.jackson.core.base.ParserBase +com.fasterxml.jackson.core.json.JsonParserBase +com.fasterxml.jackson.core.json.UTF8DataInputJsonParser +com.fasterxml.jackson.core.json.ReaderBasedJsonParser +com.fasterxml.jackson.core.async.NonBlockingInputFeeder +com.fasterxml.jackson.core.async.ByteArrayFeeder +com.fasterxml.jackson.core.json.async.NonBlockingJsonParserBase +com.fasterxml.jackson.core.json.async.NonBlockingUtf8JsonParserBase +com.fasterxml.jackson.core.json.async.NonBlockingJsonParser +com.fasterxml.jackson.core.async.ByteBufferFeeder +com.fasterxml.jackson.core.json.async.NonBlockingByteBufferJsonParser +com.fasterxml.jackson.core.base.GeneratorBase +com.fasterxml.jackson.core.json.JsonGeneratorImpl +com.fasterxml.jackson.core.json.UTF8JsonGenerator +com.fasterxml.jackson.core.io.UTF8Writer +com.fasterxml.jackson.core.json.WriterBasedJsonGenerator +com.fasterxml.jackson.core.util.JacksonFeature +com.fasterxml.jackson.core.JsonFactory$Feature +com.fasterxml.jackson.core.JsonParser$Feature +com.fasterxml.jackson.core.JsonGenerator$Feature +com.fasterxml.jackson.core.io.SerializedString +com.fasterxml.jackson.core.io.JsonStringEncoder +com.fasterxml.jackson.core.io.CharTypes +com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer +com.fasterxml.jackson.core.exc.StreamConstraintsException +com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer$TableInfo +com.fasterxml.jackson.core.util.JsonRecyclerPools +com.fasterxml.jackson.core.util.RecyclerPool +com.fasterxml.jackson.core.util.RecyclerPool$ThreadLocalPoolBase +com.fasterxml.jackson.core.util.JsonRecyclerPools$ThreadLocalPool +com.fasterxml.jackson.core.util.RecyclerPool$WithPool +com.fasterxml.jackson.core.StreamReadConstraints +com.fasterxml.jackson.core.StreamWriteConstraints +com.fasterxml.jackson.core.ErrorReportConfiguration +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$TableInfo +com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$Bucket +com.fasterxml.jackson.databind.util.RootNameLookup +com.fasterxml.jackson.databind.introspect.ClassIntrospector$MixInResolver +com.fasterxml.jackson.databind.introspect.SimpleMixInResolver +com.fasterxml.jackson.databind.BeanDescription +com.fasterxml.jackson.databind.introspect.BasicBeanDescription +com.fasterxml.jackson.databind.cfg.MapperConfig +com.fasterxml.jackson.databind.cfg.MapperConfigBase +com.fasterxml.jackson.databind.DeserializationConfig +com.fasterxml.jackson.databind.SerializationConfig +com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver +com.fasterxml.jackson.databind.introspect.AnnotationCollector +com.fasterxml.jackson.databind.util.Annotations +com.fasterxml.jackson.databind.introspect.AnnotationCollector$EmptyCollector +com.fasterxml.jackson.databind.introspect.AnnotationCollector$NoAnnotations +com.fasterxml.jackson.databind.introspect.AnnotatedClass$Creators +com.fasterxml.jackson.databind.introspect.AnnotatedConstructor +com.fasterxml.jackson.databind.cfg.ConfigOverrides +com.fasterxml.jackson.annotation.JacksonAnnotationValue +com.fasterxml.jackson.annotation.JsonInclude$Value +com.fasterxml.jackson.annotation.JsonInclude$Include +com.fasterxml.jackson.annotation.JsonSetter$Value +com.fasterxml.jackson.annotation.Nulls +com.fasterxml.jackson.databind.introspect.VisibilityChecker$Std +com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility +com.fasterxml.jackson.databind.cfg.CoercionConfigs +com.fasterxml.jackson.databind.type.LogicalType +com.fasterxml.jackson.databind.cfg.CoercionAction +com.fasterxml.jackson.databind.cfg.CoercionConfig +com.fasterxml.jackson.databind.cfg.MutableCoercionConfig +com.fasterxml.jackson.databind.cfg.CoercionInputShape +com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator +com.fasterxml.jackson.core.PrettyPrinter +com.fasterxml.jackson.annotation.JsonFormat$Value +com.fasterxml.jackson.annotation.JsonFormat$Shape +com.fasterxml.jackson.annotation.JsonFormat$Features +com.fasterxml.jackson.databind.cfg.ConfigOverride +com.fasterxml.jackson.databind.cfg.ConfigOverride$Empty +com.fasterxml.jackson.databind.cfg.ConfigFeature +com.fasterxml.jackson.databind.MapperFeature +com.fasterxml.jackson.core.util.Instantiatable +com.fasterxml.jackson.core.util.DefaultPrettyPrinter +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$Indenter +com.fasterxml.jackson.core.util.Separators +com.fasterxml.jackson.core.util.Separators$Spacing +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$NopIndenter +com.fasterxml.jackson.core.util.DefaultPrettyPrinter$FixedSpaceIndenter +com.fasterxml.jackson.core.util.DefaultIndenter +com.fasterxml.jackson.databind.SerializationFeature +com.fasterxml.jackson.databind.cfg.DatatypeFeatures +com.fasterxml.jackson.databind.cfg.DatatypeFeatures$DefaultHolder +com.fasterxml.jackson.databind.cfg.DatatypeFeature +com.fasterxml.jackson.databind.cfg.EnumFeature +com.fasterxml.jackson.databind.cfg.JsonNodeFeature +com.fasterxml.jackson.databind.cfg.ContextAttributes +com.fasterxml.jackson.databind.cfg.ContextAttributes$Impl +com.fasterxml.jackson.databind.DeserializationFeature +com.fasterxml.jackson.databind.node.JsonNodeFactory +com.fasterxml.jackson.databind.node.BooleanNode +com.fasterxml.jackson.databind.node.DoubleNode +com.fasterxml.jackson.databind.node.ShortNode +com.fasterxml.jackson.databind.node.IntNode +com.fasterxml.jackson.databind.node.LongNode +com.fasterxml.jackson.databind.node.FloatNode +com.fasterxml.jackson.databind.node.BigIntegerNode +com.fasterxml.jackson.databind.node.BinaryNode +com.fasterxml.jackson.databind.node.POJONode +com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable +com.fasterxml.jackson.databind.JsonSerializer +com.fasterxml.jackson.databind.jsonschema.SchemaAware +com.fasterxml.jackson.databind.ser.std.StdSerializer +com.fasterxml.jackson.databind.ser.std.NullSerializer +com.fasterxml.jackson.databind.ser.impl.FailingSerializer +com.fasterxml.jackson.databind.ser.std.ToEmptyObjectSerializer +com.fasterxml.jackson.databind.ser.impl.UnknownSerializer +com.fasterxml.jackson.databind.ser.ContextualSerializer +com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer +com.fasterxml.jackson.databind.exc.InvalidDefinitionException +com.fasterxml.jackson.databind.exc.InvalidTypeIdException +com.fasterxml.jackson.databind.node.ObjectNode +com.fasterxml.jackson.databind.ser.ResolvableSerializer +com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer +com.fasterxml.jackson.databind.ser.SerializerCache +com.fasterxml.jackson.databind.deser.NullValueProvider +com.fasterxml.jackson.databind.JsonDeserializer +com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer +com.fasterxml.jackson.databind.exc.PropertyBindingException +com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException +com.fasterxml.jackson.databind.exc.InvalidFormatException +com.fasterxml.jackson.databind.exc.ValueInstantiationException +com.fasterxml.jackson.databind.deser.UnresolvedForwardReference +com.fasterxml.jackson.databind.deser.ContextualDeserializer +com.fasterxml.jackson.databind.deser.ValueInstantiator$Gettable +com.fasterxml.jackson.databind.deser.std.StdDeserializer +com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer +com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase +com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer +com.fasterxml.jackson.databind.deser.std.EnumSetDeserializer +com.fasterxml.jackson.databind.deser.std.CollectionDeserializer +com.fasterxml.jackson.databind.deser.std.ArrayBlockingQueueDeserializer +com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer +com.fasterxml.jackson.databind.deser.ResolvableDeserializer +com.fasterxml.jackson.databind.deser.std.EnumMapDeserializer +com.fasterxml.jackson.databind.deser.std.MapDeserializer +com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer +com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer +com.fasterxml.jackson.databind.deser.std.StringDeserializer +com.fasterxml.jackson.databind.deser.std.MapEntryDeserializer +com.fasterxml.jackson.databind.deser.std.TokenBufferDeserializer +com.fasterxml.jackson.databind.deser.AbstractDeserializer +com.fasterxml.jackson.databind.deser.std.EnumDeserializer +com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicReferenceDeserializer +com.fasterxml.jackson.databind.introspect.AnnotatedParameter +com.fasterxml.jackson.databind.deser.SettableBeanProperty +com.fasterxml.jackson.databind.deser.CreatorProperty +com.fasterxml.jackson.databind.deser.impl.ErrorThrowingDeserializer +com.fasterxml.jackson.annotation.ObjectIdGenerator +com.fasterxml.jackson.annotation.ObjectIdGenerators$Base +com.fasterxml.jackson.annotation.ObjectIdGenerators$PropertyGenerator +com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator +com.fasterxml.jackson.databind.deser.impl.MethodProperty +com.fasterxml.jackson.databind.deser.impl.FieldProperty +com.fasterxml.jackson.databind.deser.impl.SetterlessProperty +com.fasterxml.jackson.databind.deser.BeanDeserializerBase +com.fasterxml.jackson.databind.deser.BeanDeserializer +com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer +com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer +com.fasterxml.jackson.databind.deser.Deserializers +com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig +com.fasterxml.jackson.databind.deser.BeanDeserializerModifier +com.fasterxml.jackson.databind.AbstractTypeResolver +com.fasterxml.jackson.databind.deser.ValueInstantiators +com.fasterxml.jackson.databind.deser.KeyDeserializers +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers +com.fasterxml.jackson.databind.KeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$DelegatingKD +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$EnumKD +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringCtorKeyDeserializer +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringFactoryKeyDeserializer +com.fasterxml.jackson.databind.deser.DeserializerCache +com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer +com.fasterxml.jackson.databind.ser.std.JsonValueSerializer +com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase +com.fasterxml.jackson.databind.ser.std.ToStringSerializer +com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +com.fasterxml.jackson.databind.ser.std.EnumSerializer +com.fasterxml.jackson.databind.ser.ContainerSerializer +com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer +com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase +com.fasterxml.jackson.databind.ser.impl.IteratorSerializer +com.fasterxml.jackson.databind.ser.std.IterableSerializer +com.fasterxml.jackson.databind.ser.std.MapSerializer +com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase +com.fasterxml.jackson.databind.ser.impl.IndexedStringListSerializer +com.fasterxml.jackson.databind.ser.impl.StringCollectionSerializer +com.fasterxml.jackson.databind.ser.std.CollectionSerializer +com.fasterxml.jackson.databind.ser.std.ArraySerializerBase +com.fasterxml.jackson.databind.ser.impl.StringArraySerializer +com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer +com.fasterxml.jackson.databind.ser.std.EnumSetSerializer +com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer +com.fasterxml.jackson.databind.ser.std.ReferenceTypeSerializer +com.fasterxml.jackson.databind.ser.std.SerializableSerializer +com.fasterxml.jackson.databind.ser.std.DateTimeSerializerBase +com.fasterxml.jackson.databind.ser.std.CalendarSerializer +com.fasterxml.jackson.databind.ser.std.DateSerializer +com.fasterxml.jackson.databind.ser.std.ByteBufferSerializer +com.fasterxml.jackson.databind.ser.std.InetAddressSerializer +com.fasterxml.jackson.databind.ser.std.InetSocketAddressSerializer +com.fasterxml.jackson.databind.ser.std.TimeZoneSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializer +com.fasterxml.jackson.databind.ser.impl.MapEntryAsPOJOSerializer +com.fasterxml.jackson.databind.ser.std.BeanSerializerBase +com.fasterxml.jackson.databind.ser.BeanSerializer +com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer +com.fasterxml.jackson.databind.introspect.AnnotatedField +com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator +com.fasterxml.jackson.databind.ser.std.StringSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers +com.fasterxml.jackson.databind.ser.std.NumberSerializers$Base +com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntegerSerializer +com.fasterxml.jackson.core.JsonParser$NumberType +com.fasterxml.jackson.databind.ser.std.NumberSerializers$LongSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$IntLikeSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$ShortSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$DoubleSerializer +com.fasterxml.jackson.databind.ser.std.NumberSerializers$FloatSerializer +com.fasterxml.jackson.databind.ser.std.BooleanSerializer +com.fasterxml.jackson.databind.ser.std.BooleanSerializer$AsNumber +com.fasterxml.jackson.databind.ser.std.NumberSerializer$BigDecimalAsStringSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers +com.fasterxml.jackson.databind.ser.std.UUIDSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicBooleanSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicIntegerSerializer +com.fasterxml.jackson.databind.ser.std.StdJdkSerializers$AtomicLongSerializer +com.fasterxml.jackson.databind.ser.std.FileSerializer +com.fasterxml.jackson.databind.ser.std.ClassSerializer +com.fasterxml.jackson.databind.ser.std.TokenBufferSerializer +com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig +com.fasterxml.jackson.databind.ser.Serializers +com.fasterxml.jackson.databind.ser.BeanSerializerModifier +com.fasterxml.jackson.core.io.ContentReference +com.fasterxml.jackson.core.util.BufferRecyclers +com.fasterxml.jackson.core.util.BufferRecycler +com.fasterxml.jackson.core.io.IOContext +com.fasterxml.jackson.core.util.TextBuffer +com.fasterxml.jackson.core.util.ReadConstrainedTextBuffer +com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper +com.fasterxml.jackson.core.json.UTF8StreamJsonParser +com.fasterxml.jackson.core.io.MergedStream +com.fasterxml.jackson.core.io.UTF32Reader +com.fasterxml.jackson.core.JsonEncoding +com.fasterxml.jackson.core.util.InternCache +com.fasterxml.jackson.core.exc.StreamReadException +com.fasterxml.jackson.core.exc.InputCoercionException +com.fasterxml.jackson.core.JsonParseException +com.fasterxml.jackson.core.io.JsonEOFException +com.fasterxml.jackson.core.JsonStreamContext +com.fasterxml.jackson.core.json.JsonReadContext +com.fasterxml.jackson.core.StreamReadCapability +com.fasterxml.jackson.core.util.JacksonFeatureSet +com.fasterxml.jackson.core.JsonToken +com.fasterxml.jackson.databind.util.ArrayIterator +com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer$ObjectDeserializer +com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer$ArrayDeserializer +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WeightedValue +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$Node +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$AddTask +com.fasterxml.jackson.databind.util.LinkedNode +com.fasterxml.jackson.databind.annotation.JsonTypeResolver +com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer$ContainerStack +com.fasterxml.jackson.core.io.NumberInput +com.fasterxml.jackson.core.JsonParser$NumberTypeFP +com.fasterxml.jackson.core.StreamReadFeature +com.fasterxml.jackson.databind.node.JsonNodeType +com.networknt.schema.JsonSchemaFactory$$Lambda$759/0x00007faab43c1cc0 +com.networknt.schema.JsonSchemaFactory$$Lambda$760/0x00007faab43c1f08 +com.networknt.schema.OutputFormat +java.lang.invoke.LambdaForm$DMH/0x00007faab43c4000 +com.networknt.schema.JsonSchema$$Lambda$761/0x00007faab43c2350 +com.networknt.schema.i18n.DefaultMessageSource +com.networknt.schema.i18n.DefaultMessageSource$Holder +com.networknt.schema.i18n.MessageSource +com.networknt.schema.i18n.ResourceBundleMessageSource +java.util.MissingResourceException +com.networknt.schema.AbstractJsonValidator +com.networknt.schema.NonValidationKeyword$Validator +com.networknt.schema.TypeFactory +com.networknt.schema.JsonType +com.networknt.schema.AnnotationKeyword$Validator +com.networknt.schema.SchemaLocation$Fragment +com.fasterxml.jackson.core.io.NumberOutput +com.networknt.schema.ExclusiveMinimumValidator$2 +com.networknt.schema.JsonSchemaRef +com.networknt.schema.RefValidator$$Lambda$762/0x00007faab43c6660 +com.networknt.schema.CachedSupplier +com.networknt.schema.JsonSchema$$Lambda$763/0x00007faab43c6ab8 +com.networknt.schema.MinimumValidator$1 +com.networknt.schema.RefValidator$$Lambda$764/0x00007faab43c6f40 +com.fasterxml.jackson.databind.node.InternalNodeMapper +com.fasterxml.jackson.databind.ObjectWriter +com.fasterxml.jackson.core.util.MinimalPrettyPrinter +com.fasterxml.jackson.databind.ObjectWriter$GeneratorSettings +com.fasterxml.jackson.databind.ObjectWriter$Prefetch +com.fasterxml.jackson.databind.RuntimeJsonMappingException +com.fasterxml.jackson.databind.ObjectReader +com.fasterxml.jackson.core.filter.TokenFilter +com.fasterxml.jackson.core.filter.JsonPointerBasedFilter +com.fasterxml.jackson.core.util.JsonParserDelegate +com.fasterxml.jackson.core.filter.FilteringParserDelegate +com.fasterxml.jackson.databind.node.InternalNodeMapper$WrapperForSerializer +com.fasterxml.jackson.core.exc.StreamWriteException +com.fasterxml.jackson.core.JsonGenerationException +com.fasterxml.jackson.core.json.JsonWriteContext +com.fasterxml.jackson.core.StreamWriteCapability +com.fasterxml.jackson.core.FormatFeature +com.fasterxml.jackson.core.json.JsonWriteFeature +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$Bucket +com.fasterxml.jackson.databind.util.TypeKey +com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$$Lambda$765/0x00007faab43ca5c0 +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntrySet +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntryIterator +com.fasterxml.jackson.databind.type.ClassStack +com.fasterxml.jackson.databind.introspect.AnnotationCollector$OneCollector +com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector +com.fasterxml.jackson.annotation.JsonAutoDetect +com.fasterxml.jackson.annotation.JsonIdentityInfo +com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WriteThroughEntry +com.networknt.schema.format.BaseFormatJsonValidator +com.networknt.schema.FormatValidator +java.util.regex.PatternSyntaxException +com.networknt.schema.OutputFormat$Default +com.networknt.schema.OutputFormat$Boolean +com.networknt.schema.OutputFormat$Flag +com.networknt.schema.OutputFormat$List +com.networknt.schema.output.OutputUnitData +com.networknt.schema.ValidationMessage +com.networknt.schema.OutputFormat$List$$Lambda$766/0x00007faab43cd088 +com.networknt.schema.OutputFormat$Hierarchical +com.networknt.schema.OutputFormat$Hierarchical$$Lambda$767/0x00007faab43cd510 +com.networknt.schema.OutputFormat$Result +com.networknt.schema.ExecutionConfig +com.networknt.schema.ExecutionConfig$$Lambda$768/0x00007faab43cdc00 +com.networknt.schema.ExecutionContext +com.networknt.schema.BaseJsonValidator$JsonNodePathJsonPointer +com.networknt.schema.utils.JsonNodeUtil +com.networknt.schema.TypeFactory$1 +com.networknt.schema.ValidationMessage$BuilderSupport +com.networknt.schema.MessageSourceValidationMessage$BuilderSupport +com.networknt.schema.MessageSourceValidationMessage$Builder +com.networknt.schema.FormatValidator$$Lambda$769/0x00007faab43cf038 +com.networknt.schema.format.UriReferenceFormat$$Lambda$770/0x00007faab43cf260 +java.util.stream.MatchOps$$Lambda$771/0x00007faab41e4f78 +java.util.stream.MatchOps$2MatchSink +com.networknt.schema.format.UriFormat$$Lambda$772/0x00007faab43cf4b0 +com.networknt.schema.utils.SetView +com.networknt.schema.ValidationMessageHandler$$Lambda$773/0x00007faab43cfb18 +com.networknt.schema.MessageSourceValidationMessage +com.networknt.schema.i18n.MessageFormatter +com.networknt.schema.MessageSourceValidationMessage$BuilderSupport$$Lambda$774/0x00007faab43d0000 +com.networknt.schema.utils.CachingSupplier +com.networknt.schema.ValidationMessage$BuilderSupport$$Lambda$775/0x00007faab43d0458 +com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent +com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent +com.amazonaws.services.lambda.runtime.events.SNSEvent +com.amazonaws.services.lambda.runtime.events.ScheduledEvent +com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent +com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent +com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent +com.amazonaws.services.lambda.runtime.events.StreamsEventResponse +com.amazonaws.services.lambda.runtime.events.StreamsEventResponse$StreamsEventResponseBuilder +io.burt.jmespath.function.Function +io.burt.jmespath.function.BaseFunction +software.amazon.lambda.powertools.utilities.jmespath.Base64Function +io.burt.jmespath.JmesPathException +io.burt.jmespath.function.FunctionConfigurationException +sun.nio.cs.ThreadLocalCoders +sun.nio.cs.ThreadLocalCoders$Cache +sun.nio.cs.ThreadLocalCoders$1 +sun.nio.cs.ThreadLocalCoders$2 +software.amazon.lambda.powertools.utilities.JsonConfig +io.burt.jmespath.JmesPath +software.amazon.lambda.powertools.utilities.JsonConfig$$Lambda$776/0x00007faab43d2b30 +software.amazon.lambda.powertools.utilities.JsonConfig$ConfigHolder +io.burt.jmespath.function.FunctionRegistry +io.burt.jmespath.function.MathFunction +io.burt.jmespath.function.AbsFunction +io.burt.jmespath.JmesPathType +io.burt.jmespath.function.ArgumentConstraints +io.burt.jmespath.function.ArgumentConstraint +io.burt.jmespath.function.ArgumentConstraints$BaseArgumentConstraint +io.burt.jmespath.function.ArgumentConstraints$TypeCheck +io.burt.jmespath.function.ArgumentConstraints$TypeOf +io.burt.jmespath.function.ArrayMathFunction +io.burt.jmespath.function.AvgFunction +io.burt.jmespath.function.ArgumentConstraints$ArrayOf +io.burt.jmespath.function.ArgumentError +io.burt.jmespath.function.ArgumentError$ArgumentTypeError +io.burt.jmespath.function.ContainsFunction +io.burt.jmespath.function.ArgumentConstraints$TypeOfEither +io.burt.jmespath.function.ArgumentConstraints$AnyValue +io.burt.jmespath.function.ArgumentConstraints$HeterogeneousListOf +io.burt.jmespath.function.CeilFunction +io.burt.jmespath.function.EndsWithFunction +io.burt.jmespath.function.FloorFunction +io.burt.jmespath.function.JoinFunction +io.burt.jmespath.function.KeysFunction +io.burt.jmespath.function.LengthFunction +io.burt.jmespath.function.MapFunction +io.burt.jmespath.function.ArgumentConstraints$Expression +io.burt.jmespath.function.CompareFunction +io.burt.jmespath.function.MaxFunction +io.burt.jmespath.function.TransformByFunction +io.burt.jmespath.function.CompareByFunction +io.burt.jmespath.function.MaxByFunction +io.burt.jmespath.function.TransformByFunction$Aggregator +io.burt.jmespath.function.CompareByFunction$ComparingAggregator +io.burt.jmespath.function.MergeFunction +io.burt.jmespath.function.ArgumentConstraints$HomogeneousListOf +io.burt.jmespath.function.MinFunction +io.burt.jmespath.function.MinByFunction +io.burt.jmespath.function.NotNullFunction +io.burt.jmespath.function.ReverseFunction +io.burt.jmespath.function.SortFunction +io.burt.jmespath.function.SortByFunction +io.burt.jmespath.function.SortByFunction$SortingAggregator +io.burt.jmespath.function.StartsWithFunction +io.burt.jmespath.function.SumFunction +io.burt.jmespath.function.ToArrayFunction +io.burt.jmespath.function.ToStringFunction +io.burt.jmespath.function.ToNumberFunction +io.burt.jmespath.function.TypeFunction +io.burt.jmespath.function.ValuesFunction +software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction +java.util.zip.GZIPInputStream +software.amazon.lambda.powertools.utilities.jmespath.JsonFunction +io.burt.jmespath.RuntimeConfiguration$Builder +io.burt.jmespath.RuntimeConfiguration +io.burt.jmespath.Adapter +io.burt.jmespath.BaseRuntime +io.burt.jmespath.jackson.JacksonRuntime +io.burt.jmespath.node.NodeFactory +io.burt.jmespath.function.FunctionCallException +io.burt.jmespath.function.ArgumentTypeException +io.burt.jmespath.node.StandardNodeFactory +io.burt.jmespath.Expression +io.burt.jmespath.node.Node +io.burt.jmespath.node.StringNode +io.burt.jmespath.node.CurrentNode +io.burt.jmespath.node.PropertyNode +io.burt.jmespath.node.IndexNode +io.burt.jmespath.node.SliceNode +io.burt.jmespath.node.ProjectionNode +io.burt.jmespath.node.FlattenArrayNode +io.burt.jmespath.node.FlattenObjectNode +io.burt.jmespath.node.SelectionNode +io.burt.jmespath.node.OperatorNode +io.burt.jmespath.node.OrNode +io.burt.jmespath.node.AndNode +io.burt.jmespath.node.FunctionCallNode +io.burt.jmespath.node.ExpressionReferenceNode +io.burt.jmespath.node.NegateNode +io.burt.jmespath.node.CreateObjectNode +io.burt.jmespath.node.CreateArrayNode +io.burt.jmespath.node.JsonLiteralNode +io.burt.jmespath.node.SequenceNode +java.math.MathContext +com.amazonaws.services.lambda.runtime.events.SQSBatchResponse +org.assertj.core.api.InstanceOfAssertFactories +org.assertj.core.api.Assertions +org.assertj.core.api.NumberAssert +org.assertj.core.api.ComparableAssert +org.assertj.core.api.Descriptable +org.assertj.core.api.ExtensionPoints +org.assertj.core.api.Assert +org.assertj.core.api.AbstractAssert +org.assertj.core.api.AbstractObjectAssert +org.assertj.core.api.AbstractComparableAssert +org.assertj.core.api.AbstractBigIntegerAssert +org.assertj.core.api.BigIntegerAssert +org.assertj.core.data.TemporalOffset +org.assertj.core.data.TemporalUnitOffset +org.assertj.core.data.TemporalUnitWithinOffset +org.assertj.core.data.TemporalUnitLessThanOffset +org.assertj.core.configuration.ConfigurationProvider +org.assertj.core.api.AssertionsForClassTypes +org.assertj.core.api.AbstractTemporalAssert +org.assertj.core.api.AbstractInstantAssert +org.assertj.core.api.InstantAssert +org.assertj.core.api.AbstractYearMonthAssert +org.assertj.core.api.YearMonthAssert +org.assertj.core.api.AbstractLocalDateAssert +org.assertj.core.api.LocalDateAssert +org.assertj.core.api.AbstractLocalTimeAssert +org.assertj.core.api.LocalTimeAssert +org.assertj.core.api.ArraySortedAssert +org.assertj.core.api.EnumerableAssert +org.assertj.core.api.AbstractEnumerableAssert +org.assertj.core.api.AbstractArrayAssert +org.assertj.core.api.AbstractByteArrayAssert +org.assertj.core.api.ByteArrayAssert +org.assertj.core.api.AbstractThrowableAssert +org.assertj.core.api.ThrowableAssert +org.assertj.core.api.AbstractPeriodAssert +org.assertj.core.api.PeriodAssert +org.assertj.core.api.AbstractDurationAssert +org.assertj.core.api.DurationAssert +org.assertj.core.api.AbstractDateAssert +org.assertj.core.api.DateAssert +org.assertj.core.api.AbstractCharSequenceAssert +org.assertj.core.api.AbstractStringAssert +org.assertj.core.api.StringAssert +org.assertj.core.api.CharSequenceAssert +org.assertj.core.api.AbstractOffsetTimeAssert +org.assertj.core.api.OffsetTimeAssert +org.assertj.core.api.AbstractOffsetDateTimeAssert +org.assertj.core.api.OffsetDateTimeAssert +org.assertj.core.api.AbstractLocalDateTimeAssert +org.assertj.core.api.LocalDateTimeAssert +org.assertj.core.api.AbstractZonedDateTimeAssert +org.assertj.core.api.ZonedDateTimeAssert +org.assertj.core.api.AbstractBigDecimalAssert +org.assertj.core.api.BigDecimalAssert +org.assertj.core.api.AbstractUriAssert +org.assertj.core.api.UriAssert +org.assertj.core.api.AbstractBooleanArrayAssert +org.assertj.core.api.BooleanArrayAssert +org.assertj.core.api.AbstractByteAssert +org.assertj.core.api.ByteAssert +org.assertj.core.api.AbstractBooleanAssert +org.assertj.core.api.BooleanAssert +org.assertj.core.api.AbstractUrlAssert +org.assertj.core.api.UrlAssert +org.assertj.core.api.AbstractInputStreamAssert +org.assertj.core.api.InputStreamAssert +org.assertj.core.api.AbstractFileAssert +org.assertj.core.api.FileAssert +org.assertj.core.api.AbstractDoubleArrayAssert +org.assertj.core.api.DoubleArrayAssert +org.assertj.core.api.AbstractFloatArrayAssert +org.assertj.core.api.FloatArrayAssert +org.assertj.core.api.FloatingPointNumberAssert +org.assertj.core.api.AbstractFloatAssert +org.assertj.core.api.FloatAssert +org.assertj.core.api.AbstractCharacterAssert +org.assertj.core.api.CharacterAssert +org.assertj.core.api.AbstractCharArrayAssert +org.assertj.core.api.CharArrayAssert +org.assertj.core.api.AbstractDoubleAssert +org.assertj.core.api.DoubleAssert +org.assertj.core.api.AbstractLongArrayAssert +org.assertj.core.api.LongArrayAssert +org.assertj.core.api.AbstractLongAssert +org.assertj.core.api.LongAssert +org.assertj.core.api.AbstractShortAssert +org.assertj.core.api.ShortAssert +org.assertj.core.api.AbstractIntegerAssert +org.assertj.core.api.IntegerAssert +org.assertj.core.api.AbstractShortArrayAssert +org.assertj.core.api.ShortArrayAssert +org.assertj.core.api.AbstractIntArrayAssert +org.assertj.core.api.IntArrayAssert +org.assertj.core.description.Description +org.assertj.core.description.LazyTextDescription +org.assertj.core.description.TextDescription +org.assertj.core.api.AssertionInfo +org.assertj.core.internal.ComparisonStrategy +org.assertj.core.api.ObjectEnumerableAssert +org.assertj.core.api.IndexedObjectEnumerableAssert +org.assertj.core.api.AbstractIterableAssert +org.assertj.core.api.AbstractCollectionAssert +org.assertj.core.api.AbstractListAssert +org.assertj.core.api.FactoryBasedNavigableListAssert +org.assertj.core.api.ListAssert +org.assertj.core.internal.Objects +org.assertj.core.error.ErrorMessageFactory +org.assertj.core.util.introspection.IntrospectionError +org.assertj.core.internal.AbstractComparisonStrategy +org.assertj.core.internal.StandardComparisonStrategy +org.assertj.core.util.introspection.PropertySupport +org.assertj.core.internal.Failures +org.assertj.core.error.AssertionErrorCreator +org.assertj.core.util.Arrays +org.assertj.core.error.ConstructorInvoker +org.assertj.core.util.introspection.FieldSupport +org.assertj.core.error.GroupTypeDescription +org.assertj.core.internal.Conditions +org.assertj.core.api.WritableAssertionInfo +org.assertj.core.presentation.Representation +org.assertj.core.configuration.Configuration +org.assertj.core.configuration.PreferredAssumptionException +org.assertj.core.configuration.PreferredAssumptionException$1 +org.assertj.core.configuration.Services +org.assertj.core.util.Lists +org.assertj.core.util.Streams +org.assertj.core.util.Lists$$Lambda$777/0x00007faab441c950 +org.assertj.core.presentation.CompositeRepresentation +java.lang.invoke.LambdaForm$DMH/0x00007faab4420000 +org.assertj.core.presentation.CompositeRepresentation$$Lambda$778/0x00007faab441cdc8 +java.util.stream.SortedOps$SizedRefSortingSink +org.assertj.core.presentation.StandardRepresentation +java.nio.file.DirectoryStream +org.assertj.core.internal.Strings +org.assertj.core.internal.Comparables +org.junit.jupiter.api.extension.AfterTestExecutionCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$779/0x00007faab441dc38 +org.junit.jupiter.engine.descriptor.CallbackSupport$$Lambda$780/0x00007faab441de58 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$781/0x00007faab441e090 +org.junit.jupiter.api.extension.AfterEachCallback +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$782/0x00007faab441e4b8 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$783/0x00007faab441e6d8 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$784/0x00007faab441e900 +org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$785/0x00007faab441eb28 +org.junit.jupiter.engine.descriptor.MethodExtensionContext$$Lambda$786/0x00007faab441ed50 +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext$$Lambda$787/0x00007faab441ef90 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$788/0x00007faab441f1d0 +org.junit.jupiter.engine.descriptor.CallbackSupport$$Lambda$789/0x00007faab441f3f0 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$790/0x00007faab441f618 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$791/0x00007faab441f868 +org.junit.jupiter.api.extension.TestInstancePreDestroyCallback$$Lambda$792/0x00007faab441faa0 +org.junit.jupiter.api.extension.TestInstancePreDestroyCallback$$Lambda$793/0x00007faab441fce0 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$794/0x00007faab4424000 +org.junit.jupiter.api.AutoClose +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$795/0x00007faab4424450 +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$796/0x00007faab4424688 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$797/0x00007faab44248b0 +java.util.concurrent.ConcurrentHashMap$EntrySpliterator +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$798/0x00007faab4424d10 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$799/0x00007faab4424f50 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue$$Lambda$800/0x00007faab44251a0 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$801/0x00007faab44253e0 +org.junit.platform.engine.support.store.NamespacedHierarchicalStore$$Lambda$802/0x00007faab4425618 +org.junit.jupiter.engine.config.CachingJupiterConfiguration$$Lambda$803/0x00007faab4425840 +org.junit.platform.engine.TestExecutionResult +org.junit.platform.engine.TestExecutionResult$Status +org.junit.jupiter.api.extension.TestWatcher +org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$804/0x00007faab44262e8 +org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$$Lambda$805/0x00007faab4426520 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$806/0x00007faab4426758 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$807/0x00007faab4426998 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$808/0x00007faab4426be8 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$809/0x00007faab4426e28 +org.junit.platform.launcher.core.StackTracePruningEngineExecutionListener$$Lambda$810/0x00007faab4427068 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$811/0x00007faab44272b8 +org.junit.platform.launcher.core.CompositeEngineExecutionListener$$Lambda$812/0x00007faab44274f0 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$813/0x00007faab4427718 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$814/0x00007faab4427950 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$815/0x00007faab4427b78 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$816/0x00007faab4427db0 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$817/0x00007faab4422000 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$1 +org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor$$Lambda$818/0x00007faab4422460 +org.apache.maven.surefire.junitplatform.RunListenerAdapter$$Lambda$819/0x00007faab4422698 +com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage +com.amazonaws.services.lambda.runtime.events.SQSEvent$MessageAttribute +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringKD +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.ContextualKeyDeserializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JacksonStdImpl +jdk.proxy2.$Proxy72 +software.amazon.lambda.powertools.validation.handlers.StandardSQSHandler +software.amazon.lambda.powertools.validation.handlers.StandardSQSHandler$AjcClosure1 +com.amazonaws.services.lambda.runtime.events.SQSBatchResponse$SQSBatchResponseBuilder +com.networknt.schema.result.JsonNodeResults +com.networknt.schema.result.JsonNodeResult +com.networknt.schema.result.JsonNodeResults$$Lambda$820/0x00007faab4421230 +software.amazon.lambda.powertools.validation.ValidationUtils$ValidationErrors +com.fasterxml.jackson.databind.introspect.PotentialCreators +com.fasterxml.jackson.databind.introspect.CollectorBase +com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector +com.fasterxml.jackson.databind.introspect.AnnotationMap +com.fasterxml.jackson.databind.introspect.TypeResolutionContext$Basic +com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector$FieldBuilder +com.fasterxml.jackson.annotation.JsonKey +com.fasterxml.jackson.annotation.JsonValue +com.fasterxml.jackson.annotation.JsonAnyGetter +com.fasterxml.jackson.annotation.JsonAnySetter +com.fasterxml.jackson.databind.PropertyName +com.fasterxml.jackson.annotation.JsonGetter +com.fasterxml.jackson.annotation.JsonProperty +com.fasterxml.jackson.annotation.JsonAutoDetect$1 +com.fasterxml.jackson.annotation.PropertyAccessor +com.fasterxml.jackson.annotation.JsonIgnore +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$WithMember +com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty +com.fasterxml.jackson.databind.AnnotationIntrospector$ReferenceProperty$Type +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$Linked +com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector +com.fasterxml.jackson.databind.introspect.MemberKey +com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector$MethodBuilder +com.fasterxml.jackson.databind.introspect.AnnotatedMethodMap +com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector +com.fasterxml.jackson.annotation.JsonCreator +com.fasterxml.jackson.databind.introspect.PotentialCreator +com.fasterxml.jackson.annotation.JsonCreator$Mode +com.fasterxml.jackson.databind.cfg.ConstructorDetector +com.fasterxml.jackson.databind.cfg.ConstructorDetector$SingleArgConstructor +sun.reflect.generics.scope.ConstructorScope +com.fasterxml.jackson.databind.type.TypeBindings$TypeParamStash +com.fasterxml.jackson.databind.type.TypeBindings$AsKey +com.fasterxml.jackson.annotation.JsonSetter +com.fasterxml.jackson.annotation.JacksonInject +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$5 +com.fasterxml.jackson.annotation.JsonProperty$Access +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$6 +com.fasterxml.jackson.databind.annotation.JsonNaming +com.fasterxml.jackson.annotation.JsonPropertyOrder +com.fasterxml.jackson.annotation.JsonPropertyDescription +com.fasterxml.jackson.databind.PropertyMetadata +com.fasterxml.jackson.databind.ext.OptionalHandlerFactory +com.fasterxml.jackson.databind.ext.Java7Handlers +com.fasterxml.jackson.databind.ext.Java7HandlersImpl +com.fasterxml.jackson.databind.ext.NioPathSerializer +com.fasterxml.jackson.databind.ext.NioPathDeserializer +com.fasterxml.jackson.databind.util.BeanUtil +com.fasterxml.jackson.databind.ser.BeanSerializerBuilder +com.fasterxml.jackson.annotation.JsonIgnoreType +com.fasterxml.jackson.databind.ser.PropertyBuilder +com.fasterxml.jackson.annotation.JsonInclude +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$3 +com.fasterxml.jackson.annotation.JsonTypeId +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$2 +com.fasterxml.jackson.databind.BeanProperty$Std +com.fasterxml.jackson.annotation.JsonIgnoreProperties +com.fasterxml.jackson.annotation.JacksonAnnotation +jdk.proxy2.$Proxy73 +jdk.proxy2.$Proxy74 +jdk.proxy2.$Proxy75 +com.fasterxml.jackson.databind.introspect.AnnotationCollector$NCollector +com.fasterxml.jackson.annotation.JacksonAnnotationsInside +jdk.proxy2.$Proxy76 +com.fasterxml.jackson.databind.ser.PropertyBuilder$1 +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$1 +com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Empty +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Single +com.fasterxml.jackson.databind.annotation.JsonAppend +com.fasterxml.jackson.annotation.JsonIgnoreProperties$Value +com.fasterxml.jackson.annotation.JsonIncludeProperties +com.fasterxml.jackson.annotation.JsonIncludeProperties$Value +com.fasterxml.jackson.annotation.JsonFilter +com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer +com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer +com.fasterxml.jackson.databind.ser.AnyGetterWriter +java.util.stream.DoubleStream +java.util.stream.LongStream +com.fasterxml.jackson.annotation.JsonFormat$Feature +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$SerializerAndMapResult +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Double +com.networknt.schema.utils.SetView$SetViewIterator +com.fasterxml.jackson.databind.annotation.JsonSerialize$Inclusion +com.fasterxml.jackson.databind.annotation.JsonSerialize$Typing +com.fasterxml.jackson.databind.util.Converter +com.fasterxml.jackson.databind.util.Converter$None +com.fasterxml.jackson.databind.JsonSerializer$None +jdk.proxy2.$Proxy77 +com.networknt.schema.ValidationMessage$Builder +com.fasterxml.jackson.databind.introspect.MethodGenericTypeResolver +com.fasterxml.jackson.databind.annotation.NoClass +com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector$1 +com.fasterxml.jackson.databind.util.IgnorePropertiesUtil +com.fasterxml.jackson.databind.ser.std.StdArraySerializers +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$BooleanArraySerializer +com.fasterxml.jackson.databind.ser.std.ByteArraySerializer +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$CharArraySerializer +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$TypedPrimitiveArraySerializer +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$ShortArraySerializer +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$IntArraySerializer +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$LongArraySerializer +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$FloatArraySerializer +com.fasterxml.jackson.databind.ser.std.StdArraySerializers$DoubleArraySerializer +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$821/0x00007faab4437d00 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$822/0x00007faab4439a70 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$823/0x00007faab4439cb8 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$824/0x00007faab4439f00 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$825/0x00007faab443a150 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$826/0x00007faab443a398 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$827/0x00007faab443a5e8 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$828/0x00007faab443a828 +java.util.ResourceBundle$ResourceBundleControlProviderHolder +java.util.ResourceBundle$ResourceBundleControlProviderHolder$$Lambda$829/0x00007faab41e71c0 +java.util.spi.ResourceBundleControlProvider +java.util.ResourceBundle$ResourceBundleControlProviderHolder$$Lambda$830/0x00007faab41e75e0 +java.util.ImmutableCollections$Access +jdk.internal.access.JavaUtilCollectionAccess +java.util.ImmutableCollections$Access$1 +java.util.ResourceBundle$CacheKey +java.util.ResourceBundle$CacheKeyReference +java.util.ResourceBundle$KeyElementReference +java.util.ResourceBundle$$Lambda$831/0x00007faab41e84e0 +java.util.ResourceBundle$Control$2 +java.util.PropertyResourceBundle +sun.util.PropertyResourceBundleCharset +sun.util.PropertyResourceBundleCharset$PropertiesFileDecoder +java.util.ResourceBundle$BundleReference +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$832/0x00007faab443aa70 +com.networknt.schema.i18n.ResourceBundleMessageSource$$Lambda$833/0x00007faab443acb0 +java.text.Format$FieldDelegate +java.text.FieldPosition$Delegate +java.text.NumberFormat$Field +com.fasterxml.jackson.databind.ser.std.NumberSerializers$1 +org.slf4j.event.Level +org.slf4j.helpers.MessageFormatter +org.slf4j.helpers.FormattingTuple +org.apache.maven.surefire.api.report.TestOutputReportEntry +com.amazonaws.services.lambda.runtime.events.SQSBatchResponse$BatchItemFailure +com.amazonaws.services.lambda.runtime.events.SQSBatchResponse$BatchItemFailure$BatchItemFailureBuilder +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Multi +org.assertj.core.api.AssertionsForInterfaceTypes +org.assertj.core.api.GenericComparableAssert +org.assertj.core.api.AbstractUniversalComparableAssert +org.assertj.core.api.UniversalComparableAssert +org.assertj.core.api.AbstractIterableSizeAssert +org.assertj.core.api.IterableSizeAssert +org.assertj.core.api.AssertFactory +org.assertj.core.api.ObjectAssert +org.assertj.core.api.ListAssert$$Lambda$834/0x00007faab443f898 +org.assertj.core.internal.Iterables +org.assertj.core.internal.Predicates +org.assertj.core.internal.Lists +org.assertj.core.util.IterableUtil +org.assertj.core.internal.CommonValidations +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$835/0x00007faab4441120 +org.assertj.core.internal.Iterables$$Lambda$836/0x00007faab4441360 +org.assertj.core.internal.Iterables$$Lambda$837/0x00007faab44415b8 +org.assertj.core.internal.StandardComparisonStrategy$$Lambda$838/0x00007faab44417d8 +org.junit.jupiter.engine.extension.TimeoutExtension$$Lambda$839/0x00007faab4441a30 +org.junit.jupiter.engine.extension.TimeoutConfiguration$$Lambda$840/0x00007faab4441c88 +software.amazon.lambda.powertools.validation.handlers.ValidationInboundAPIGatewayV2HTTPEventHandler +software.amazon.lambda.powertools.validation.handlers.ValidationInboundAPIGatewayV2HTTPEventHandler$AjcClosure1 +org.assertj.core.internal.WholeNumbers +org.assertj.core.internal.Numbers +org.assertj.core.internal.Integers +org.mockito.internal.invocation.RealMethod$IsIllegal +org.mockito.internal.debugging.LocationFactory +org.mockito.internal.debugging.LocationFactory$Factory +org.mockito.internal.util.Platform +org.mockito.internal.debugging.LocationFactory$DefaultLocationFactory +org.mockito.invocation.Location +org.mockito.internal.debugging.LocationImpl +org.mockito.exceptions.stacktrace.StackTraceCleaner +org.mockito.exceptions.stacktrace.StackTraceCleaner$StackFrameMetadata +org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleaner +org.mockito.internal.debugging.LocationImpl$$Lambda$841/0x00007faab4444500 +org.mockito.internal.debugging.LocationImpl$$Lambda$842/0x00007faab4444740 +org.mockito.internal.debugging.LocationImpl$$Lambda$843/0x00007faab4444998 +java.lang.StackStreamFactory +java.lang.StackWalker$ExtendedOption +java.lang.StackStreamFactory$StackFrameTraverser +java.lang.StackStreamFactory$WalkerState +java.lang.StackStreamFactory$1 +java.lang.StackStreamFactory$FrameBuffer +java.lang.StackStreamFactory$StackFrameTraverser$StackFrameBuffer +org.mockito.internal.debugging.LocationImpl$$Lambda$844/0x00007faab4444bd8 +org.mockito.internal.debugging.LocationImpl$MetadataShim +org.mockito.internal.debugging.LocationImpl$$Lambda$845/0x00007faab4445058 +org.mockito.internal.debugging.LocationImpl$$Lambda$846/0x00007faab4445298 +org.mockito.invocation.InvocationFactory +org.mockito.internal.invocation.DefaultInvocationFactory +org.mockito.internal.invocation.mockref.MockReference +org.mockito.internal.invocation.AbstractAwareMethod +org.mockito.internal.invocation.MockitoMethod +org.mockito.internal.exceptions.VerificationAwareInvocation +org.mockito.internal.invocation.InterceptedInvocation +org.mockito.internal.invocation.InterceptedInvocation$1 +org.mockito.internal.invocation.mockref.MockWeakReference +org.mockito.internal.creation.DelegatingMethod +org.mockito.internal.creation.SuspendMethod +org.mockito.internal.progress.SequenceNumber +org.mockito.internal.invocation.ArgumentsProcessor +org.mockito.internal.invocation.InvocationMatcher +org.mockito.internal.invocation.ArgumentMatcherAction +org.mockito.internal.stubbing.BaseStubbing +org.mockito.internal.stubbing.OngoingStubbingImpl +org.mockito.internal.listeners.StubbingLookupNotifier +org.mockito.listeners.StubbingLookupEvent +org.mockito.internal.util.ObjectMethodsGuru +org.mockito.internal.util.Primitives +org.mockito.internal.stubbing.answers.DefaultAnswerValidator +org.mockito.internal.stubbing.answers.InvocationInfo +org.mockito.internal.stubbing.answers.Returns +org.mockito.internal.util.reflection.GenericMetadataSupport +org.mockito.internal.util.reflection.GenericMetadataSupport$GenericArrayReturnType +org.mockito.internal.util.reflection.GenericMetadataSupport$FromClassGenericMetadataSupport +org.mockito.internal.util.reflection.GenericMetadataSupport$FromParameterizedTypeGenericMetadataSupport +org.mockito.internal.util.reflection.GenericMetadataSupport$BoundedType +org.mockito.internal.util.reflection.GenericMetadataSupport$NotGenericReturnTypeSupport +org.mockito.internal.util.reflection.GenericMetadataSupport$ParameterizedReturnType +org.mockito.internal.util.reflection.GenericMetadataSupport$TypeVariableReturnType +org.mockito.internal.stubbing.StubbedInvocationMatcher +org.mockito.internal.stubbing.ConsecutiveStubbing +org.mockito.internal.invocation.MatcherApplicationStrategy +org.mockito.internal.invocation.TypeSafeMatching +org.mockito.internal.invocation.StubInfoImpl +org.mockito.internal.invocation.InvocationMatcher$1 +org.mockito.internal.util.KotlinInlineClassUtil +org.mockito.internal.matchers.ContainsExtraTypeInfo +org.mockito.internal.matchers.Equals +org.mockito.internal.matchers.ArrayEquals +org.assertj.core.api.NotThrownAssert +org.assertj.core.api.ThrowableAssert$ThrowingCallable +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$847/0x00007faab444b9b0 +org.mockito.internal.invocation.TypeSafeMatching$$Lambda$848/0x00007faab444bbd8 +org.mockito.internal.matchers.CapturesArguments +org.assertj.core.internal.Throwables +org.assertj.core.description.EmptyTextDescription +software.amazon.lambda.powertools.validation.handlers.KinesisHandlerWithError +software.amazon.lambda.powertools.validation.handlers.KinesisHandlerWithError$AjcClosure1 +com.amazonaws.services.lambda.runtime.events.StreamsEventResponse$BatchItemFailure +com.amazonaws.services.lambda.runtime.events.StreamsEventResponse$BatchItemFailure$BatchItemFailureBuilder +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$849/0x00007faab444cdd0 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$850/0x00007faab444d010 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$851/0x00007faab444d260 +org.junit.jupiter.params.provider.AnnotationBasedArgumentsProvider$$Lambda$852/0x00007faab444d4b0 +org.junit.jupiter.engine.descriptor.TestTemplateExtensionContext$$Lambda$853/0x00007faab444d6f8 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$854/0x00007faab444d938 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$855/0x00007faab444db80 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$856/0x00007faab444ddc8 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$857/0x00007faab444e010 +org.junit.jupiter.params.provider.ArgumentsUtils +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$858/0x00007faab444e458 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$859/0x00007faab444e698 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$860/0x00007faab444e8c0 +org.junit.platform.commons.util.ReflectionUtils$$Lambda$861/0x00007faab444eb08 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$862/0x00007faab444ed60 +org.junit.jupiter.params.provider.MethodArgumentsProvider$$Lambda$863/0x00007faab444ef88 +com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent +com.amazonaws.services.lambda.runtime.events.KafkaEvent +com.amazonaws.services.lambda.runtime.events.ActiveMQEvent +com.amazonaws.services.lambda.runtime.events.RabbitMQEvent +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent +com.fasterxml.jackson.databind.deser.std.JdkDeserializers +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer +com.fasterxml.jackson.databind.deser.std.UUIDDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicBooleanDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicIntegerDeserializer +com.fasterxml.jackson.databind.deser.std.AtomicLongDeserializer +com.fasterxml.jackson.databind.deser.std.ByteBufferDeserializer +com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer +com.fasterxml.jackson.databind.deser.std.StdNodeBasedDeserializer +com.fasterxml.jackson.databind.deser.std.ThreadGroupDeserializer +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBuilderDeserializer +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$StringBufferDeserializer +com.fasterxml.jackson.databind.deser.std.FromStringDeserializer$Std +com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator +com.fasterxml.jackson.databind.annotation.JsonValueInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators +com.fasterxml.jackson.databind.deser.ValueInstantiator +com.fasterxml.jackson.databind.deser.ValueInstantiator$Base +com.fasterxml.jackson.databind.deser.std.JsonLocationInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$JDKValueInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ArrayListInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$HashSetInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$LinkedListInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$TreeSetInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ConstantValueInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$LinkedHashMapInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$HashMapInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$ConcurrentHashMapInstantiator +com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators$TreeMapInstantiator +com.fasterxml.jackson.core.JsonLocation +com.amazonaws.services.lambda.runtime.events.SNSEvent$SNSRecord +com.fasterxml.jackson.databind.deser.impl.CreatorCollector +com.fasterxml.jackson.databind.deser.std.StdValueInstantiator +com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder +com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty +com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer +com.fasterxml.jackson.databind.deser.impl.FailingDeserializer +com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider +com.fasterxml.jackson.databind.util.AccessPattern +com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$4 +com.fasterxml.jackson.annotation.JsonAlias +com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap +com.fasterxml.jackson.databind.exc.IgnoredPropertyException +com.fasterxml.jackson.databind.deser.SettableBeanProperty$Delegating +com.fasterxml.jackson.databind.deser.impl.ManagedReferenceProperty +com.fasterxml.jackson.databind.deser.impl.ObjectIdReferenceProperty +com.fasterxml.jackson.databind.deser.impl.MergingSettableBeanProperty +com.fasterxml.jackson.databind.deser.impl.InnerClassProperty +com.fasterxml.jackson.databind.deser.impl.ReadableObjectId$Referring +com.fasterxml.jackson.databind.deser.BeanDeserializer$BeanReferring +com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer +com.fasterxml.jackson.databind.deser.BasicDeserializerFactory$ContainerDefaultMappings +com.amazonaws.services.lambda.runtime.events.SNSEvent$SNS +com.amazonaws.services.lambda.runtime.events.SNSEvent$MessageAttribute +com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer$StringKD +com.fasterxml.jackson.databind.deser.ContextualKeyDeserializer +com.fasterxml.jackson.databind.annotation.JacksonStdImpl +jdk.proxy2.$Proxy78 +com.fasterxml.jackson.core.util.InternalJacksonUtil +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$864/0x00007faab445d7b0 +com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializerNR +com.fasterxml.jackson.databind.deser.std.NumberDeserializers +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$PrimitiveOrWrapperDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BooleanDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$LongDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$DoubleDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$CharacterDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ByteDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$ShortDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$FloatDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$NumberDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigDecimalDeserializer +com.fasterxml.jackson.databind.deser.std.NumberDeserializers$BigIntegerDeserializer +com.fasterxml.jackson.databind.util.TokenBuffer$Parser +com.fasterxml.jackson.databind.util.TokenBuffer$Segment +com.fasterxml.jackson.databind.ser.std.MapProperty +com.fasterxml.jackson.databind.ser.BasicSerializerFactory$1 +com.fasterxml.jackson.databind.ser.std.StdKeySerializers +com.fasterxml.jackson.databind.ser.std.StdKeySerializer +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$StringKeySerializer +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Dynamic +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Default +com.fasterxml.jackson.databind.ser.std.StdKeySerializers$EnumKeySerializer +com.fasterxml.jackson.databind.ser.std.MapSerializer$1 +com.fasterxml.jackson.databind.util.TokenBufferReadContext +com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$RequestContext +com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent$Elb +com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent$AWSLogs +java.util.Base64 +java.util.Base64$Decoder +java.util.Base64$Encoder +java.lang.invoke.LambdaForm$MH/0x00007faab4468000 +java.lang.invoke.LambdaForm$MH/0x00007faab4468400 +com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent$CloudFormationCustomResourceEventBuilder +com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent$Record +com.fasterxml.jackson.databind.deser.std.DateDeserializers +com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateBasedDeserializer +com.fasterxml.jackson.databind.deser.std.DateDeserializers$CalendarDeserializer +com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer +com.fasterxml.jackson.databind.util.ByteBufferBackedOutputStream +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$865/0x00007faab4465ee8 +com.amazonaws.services.lambda.runtime.events.KafkaEvent$KafkaEventBuilder +com.amazonaws.services.lambda.runtime.events.KafkaEvent$KafkaEventRecord +com.amazonaws.services.lambda.runtime.events.KafkaEvent$SchemaMetadata +com.amazonaws.services.lambda.runtime.events.KafkaEvent$KafkaEventRecord$KafkaEventRecordBuilder +sun.reflect.generics.tree.IntSignature +sun.reflect.generics.tree.LongSignature +sun.reflect.generics.tree.ByteSignature +com.amazonaws.services.lambda.runtime.events.KafkaEvent$SchemaMetadata$SchemaMetadataBuilder +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$IntDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$LongDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$ByteDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$ShortDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$FloatDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$DoubleDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$BooleanDeser +com.fasterxml.jackson.databind.deser.std.PrimitiveArrayDeserializers$CharDeser +com.fasterxml.jackson.databind.exc.InvalidNullException +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$866/0x00007faab446de00 +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$867/0x00007faab446e038 +com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$ActiveMQEventBuilder +com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$ActiveMQMessage +com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$Destination +com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$ActiveMQMessage$ActiveMQMessageBuilder +sun.reflect.generics.tree.BooleanSignature +com.amazonaws.services.lambda.runtime.events.ActiveMQEvent$Destination$DestinationBuilder +com.fasterxml.jackson.databind.cfg.CoercionConfigs$1 +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$868/0x00007faab446f088 +com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$RabbitMQEventBuilder +com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$RabbitMessage +com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$BasicProperties +com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$RabbitMessage$RabbitMessageBuilder +com.amazonaws.services.lambda.runtime.events.RabbitMQEvent$BasicProperties$BasicPropertiesBuilder +com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializerNR$Scope +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$869/0x00007faab446a210 +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$870/0x00007faab446a448 +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent$Record +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent$Record$KinesisFirehoseRecordMetadata +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$871/0x00007faab446aae0 +jdk.internal.reflect.GeneratedConstructorAccessor8 +jdk.internal.reflect.GeneratedMethodAccessor19 +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent$Record +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent$Record$KinesisStreamRecordMetadata +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$872/0x00007faab446b1a8 +software.amazon.lambda.powertools.validation.model.Basket +com.amazonaws.services.lambda.runtime.events.APIGatewayV2WebSocketResponse +com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse$Record +com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse$Result +software.amazon.lambda.powertools.validation.internal.ResponseEventsArgumentsProvider$$Lambda$873/0x00007faab4469440 +org.assertj.core.api.ThrowableTypeAssert +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$874/0x00007faab4469920 +org.assertj.core.api.ThrowableAssertAlternative +software.amazon.lambda.powertools.validation.internal.ValidationAspect$$Lambda$875/0x00007faab4469b48 +software.amazon.lambda.powertools.validation.handlers.SQSHandlerWithError +software.amazon.lambda.powertools.validation.handlers.SQSHandlerWithError$AjcClosure1 +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$876/0x00007faab4470bc0 +software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7APIGatewayProxyRequestEventHandler +software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7APIGatewayProxyRequestEventHandler$AjcClosure1 +org.assertj.core.api.AbstractCharSequenceAssert$$Lambda$877/0x00007faab4471280 +org.assertj.core.api.AbstractMapAssert +org.assertj.core.api.MapAssert +org.assertj.core.api.AbstractMapSizeAssert +org.assertj.core.api.MapSizeAssert +org.assertj.core.internal.Maps +software.amazon.lambda.powertools.validation.internal.HandledResponseEventsArgumentsProvider$$Lambda$878/0x00007faab4474720 +org.assertj.core.util.Preconditions +org.assertj.core.internal.ComparatorBasedComparisonStrategy +java.util.concurrent.atomic.Striped64 +java.util.concurrent.atomic.LongAdder +java.util.concurrent.atomic.AtomicMarkableReference +java.util.concurrent.atomic.AtomicStampedReference +java.util.concurrent.atomic.AtomicIntegerFieldUpdater +java.util.concurrent.atomic.AtomicLongFieldUpdater +java.util.concurrent.atomic.AtomicReferenceFieldUpdater +org.assertj.core.presentation.PredicateDescription +org.assertj.core.presentation.TransformingList +org.assertj.core.presentation.StandardRepresentation$$Lambda$879/0x00007faab4475580 +java.lang.invoke.LambdaForm$DMH/0x00007faab4478000 +java.lang.invoke.LambdaForm$MH/0x00007faab4478400 +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$880/0x00007faab44757c8 +org.assertj.core.data.MapEntry +org.assertj.core.internal.ErrorMessages +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$881/0x00007faab4476048 +software.amazon.lambda.powertools.validation.handlers.StandardKinesisHandler +software.amazon.lambda.powertools.validation.handlers.StandardKinesisHandler$AjcClosure1 +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$882/0x00007faab4476700 +software.amazon.lambda.powertools.validation.handlers.SQSWithCustomEnvelopeHandler +software.amazon.lambda.powertools.validation.handlers.SQSWithCustomEnvelopeHandler$AjcClosure1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.exc.StreamWriteException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.JsonGenerationException +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.json.JsonWriteContext +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.StreamWriteCapability +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$Bucket +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.TypeKey +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap$$Lambda$883/0x00007faab447c420 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntrySet +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$EntryIterator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BeanSerializerBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.PropertyBuilder +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonInclude +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder$3 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonTypeId +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.BeanProperty$Std +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.PropertyBuilder$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Empty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Single +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.annotation.JsonAppend +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonFilter +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$SerializerAndMapResult +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$Double +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.MapProperty +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.BasicSerializerFactory$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdKeySerializers +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdKeySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdKeySerializers$StringKeySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Dynamic +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Default +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.StdKeySerializers$EnumKeySerializer +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.MapSerializer$1 +io.burt.jmespath.antlr.v4.runtime.tree.ParseTreeVisitor +io.burt.jmespath.parser.JmesPathVisitor +io.burt.jmespath.antlr.v4.runtime.tree.AbstractParseTreeVisitor +io.burt.jmespath.parser.JmesPathBaseVisitor +io.burt.jmespath.parser.ExpressionParser +io.burt.jmespath.antlr.v4.runtime.tree.Tree +io.burt.jmespath.antlr.v4.runtime.tree.SyntaxTree +io.burt.jmespath.antlr.v4.runtime.tree.ParseTree +io.burt.jmespath.antlr.v4.runtime.tree.RuleNode +io.burt.jmespath.antlr.v4.runtime.ANTLRErrorListener +io.burt.jmespath.parser.ParseException +io.burt.jmespath.util.StringEscapeHelper +io.burt.jmespath.antlr.v4.runtime.BaseErrorListener +io.burt.jmespath.parser.ParseErrorAccumulator +io.burt.jmespath.util.AntlrHelper +io.burt.jmespath.antlr.v4.runtime.TokenSource +io.burt.jmespath.antlr.v4.runtime.IntStream +io.burt.jmespath.antlr.v4.runtime.TokenStream +io.burt.jmespath.antlr.v4.runtime.CharStream +io.burt.jmespath.antlr.v4.runtime.Recognizer +io.burt.jmespath.antlr.v4.runtime.Lexer +io.burt.jmespath.parser.JmesPathLexer +io.burt.jmespath.antlr.v4.runtime.RecognitionException +io.burt.jmespath.antlr.v4.runtime.LexerNoViableAltException +java.util.EmptyStackException +io.burt.jmespath.antlr.v4.runtime.atn.ATNSimulator +io.burt.jmespath.antlr.v4.runtime.atn.LexerATNSimulator +io.burt.jmespath.antlr.v4.runtime.Vocabulary +io.burt.jmespath.antlr.v4.runtime.RuntimeMetaData +io.burt.jmespath.antlr.v4.runtime.atn.PredictionContextCache +io.burt.jmespath.antlr.v4.runtime.atn.PredictionContext +io.burt.jmespath.antlr.v4.runtime.atn.SingletonPredictionContext +io.burt.jmespath.antlr.v4.runtime.atn.EmptyPredictionContext +io.burt.jmespath.antlr.v4.runtime.VocabularyImpl +io.burt.jmespath.antlr.v4.runtime.atn.ATNDeserializer +java.io.InvalidClassException +io.burt.jmespath.antlr.v4.runtime.atn.Transition +io.burt.jmespath.antlr.v4.runtime.atn.EpsilonTransition +io.burt.jmespath.antlr.v4.runtime.atn.ActionTransition +io.burt.jmespath.antlr.v4.runtime.atn.ATNState +io.burt.jmespath.antlr.v4.runtime.atn.DecisionState +io.burt.jmespath.antlr.v4.runtime.atn.BlockStartState +io.burt.jmespath.antlr.v4.runtime.atn.BasicBlockStartState +io.burt.jmespath.antlr.v4.runtime.atn.BlockEndState +io.burt.jmespath.antlr.v4.runtime.atn.RuleStopState +io.burt.jmespath.antlr.v4.runtime.atn.BasicState +io.burt.jmespath.antlr.v4.runtime.atn.AtomTransition +io.burt.jmespath.antlr.v4.runtime.atn.RangeTransition +io.burt.jmespath.antlr.v4.runtime.atn.RuleTransition +io.burt.jmespath.antlr.v4.runtime.atn.AbstractPredicateTransition +io.burt.jmespath.antlr.v4.runtime.atn.PredicateTransition +io.burt.jmespath.antlr.v4.runtime.atn.PrecedencePredicateTransition +io.burt.jmespath.antlr.v4.runtime.atn.SetTransition +io.burt.jmespath.antlr.v4.runtime.atn.NotSetTransition +io.burt.jmespath.antlr.v4.runtime.atn.WildcardTransition +io.burt.jmespath.antlr.v4.runtime.atn.RuleStartState +io.burt.jmespath.antlr.v4.runtime.atn.PlusBlockStartState +io.burt.jmespath.antlr.v4.runtime.atn.StarBlockStartState +io.burt.jmespath.antlr.v4.runtime.atn.TokensStartState +io.burt.jmespath.antlr.v4.runtime.atn.StarLoopbackState +io.burt.jmespath.antlr.v4.runtime.atn.StarLoopEntryState +io.burt.jmespath.antlr.v4.runtime.atn.PlusLoopbackState +io.burt.jmespath.antlr.v4.runtime.atn.LoopEndState +io.burt.jmespath.antlr.v4.runtime.atn.ATNDeserializer$UnicodeDeserializer +io.burt.jmespath.antlr.v4.runtime.atn.LexerAction +io.burt.jmespath.antlr.v4.runtime.atn.ATNDeserializationOptions +io.burt.jmespath.antlr.v4.runtime.atn.ATNType +io.burt.jmespath.antlr.v4.runtime.atn.ATN +io.burt.jmespath.antlr.v4.runtime.misc.IntSet +io.burt.jmespath.antlr.v4.runtime.misc.Pair +io.burt.jmespath.antlr.v4.runtime.atn.ATNDeserializer$UnicodeDeserializingMode +io.burt.jmespath.antlr.v4.runtime.atn.ATNDeserializer$1 +io.burt.jmespath.antlr.v4.runtime.misc.IntervalSet +io.burt.jmespath.antlr.v4.runtime.misc.Interval +io.burt.jmespath.antlr.v4.runtime.atn.ATNDeserializer$2 +io.burt.jmespath.antlr.v4.runtime.atn.Transition$1 +io.burt.jmespath.antlr.v4.runtime.atn.LexerActionType +io.burt.jmespath.antlr.v4.runtime.atn.ATNDeserializer$3 +io.burt.jmespath.antlr.v4.runtime.atn.LexerSkipAction +io.burt.jmespath.antlr.v4.runtime.dfa.DFA +io.burt.jmespath.antlr.v4.runtime.dfa.DFASerializer +io.burt.jmespath.antlr.v4.runtime.dfa.LexerDFASerializer +io.burt.jmespath.antlr.v4.runtime.CharStreams +io.burt.jmespath.antlr.v4.runtime.CodePointBuffer +io.burt.jmespath.antlr.v4.runtime.CodePointBuffer$Builder +io.burt.jmespath.antlr.v4.runtime.CodePointBuffer$Type +io.burt.jmespath.antlr.v4.runtime.CodePointBuffer$1 +io.burt.jmespath.antlr.v4.runtime.CodePointCharStream +io.burt.jmespath.antlr.v4.runtime.CodePointCharStream$CodePoint8BitCharStream +io.burt.jmespath.antlr.v4.runtime.CodePointCharStream$CodePoint16BitCharStream +io.burt.jmespath.antlr.v4.runtime.CodePointCharStream$CodePoint32BitCharStream +io.burt.jmespath.antlr.v4.runtime.CodePointCharStream$1 +io.burt.jmespath.antlr.v4.runtime.Recognizer$1 +io.burt.jmespath.antlr.v4.runtime.ConsoleErrorListener +io.burt.jmespath.antlr.v4.runtime.TokenFactory +io.burt.jmespath.antlr.v4.runtime.CommonTokenFactory +io.burt.jmespath.antlr.v4.runtime.Token +io.burt.jmespath.antlr.v4.runtime.misc.IntegerList +io.burt.jmespath.antlr.v4.runtime.misc.IntegerStack +io.burt.jmespath.antlr.v4.runtime.atn.ATNConfig +io.burt.jmespath.antlr.v4.runtime.atn.LexerATNConfig +io.burt.jmespath.antlr.v4.runtime.atn.ATNConfigSet +io.burt.jmespath.antlr.v4.runtime.atn.OrderedATNConfigSet +io.burt.jmespath.antlr.v4.runtime.dfa.DFAState +io.burt.jmespath.antlr.v4.runtime.misc.Array2DHashSet +io.burt.jmespath.antlr.v4.runtime.atn.ATNConfigSet$AbstractConfigHashSet +io.burt.jmespath.antlr.v4.runtime.atn.ATNConfigSet$ConfigHashSet +io.burt.jmespath.antlr.v4.runtime.misc.EqualityComparator +io.burt.jmespath.antlr.v4.runtime.misc.AbstractEqualityComparator +io.burt.jmespath.antlr.v4.runtime.misc.ObjectEqualityComparator +io.burt.jmespath.antlr.v4.runtime.atn.ATNConfigSet$ConfigEqualityComparator +io.burt.jmespath.antlr.v4.runtime.atn.LexerATNSimulator$SimState +io.burt.jmespath.antlr.v4.runtime.BufferedTokenStream +io.burt.jmespath.antlr.v4.runtime.CommonTokenStream +io.burt.jmespath.antlr.v4.runtime.Parser +io.burt.jmespath.parser.JmesPathParser +io.burt.jmespath.antlr.v4.runtime.ANTLRErrorStrategy +io.burt.jmespath.antlr.v4.runtime.RuleContext +io.burt.jmespath.antlr.v4.runtime.ParserRuleContext +io.burt.jmespath.antlr.v4.runtime.tree.ParseTreeListener +io.burt.jmespath.antlr.v4.runtime.tree.TerminalNode +io.burt.jmespath.antlr.v4.runtime.tree.ErrorNode +io.burt.jmespath.antlr.v4.runtime.atn.ParserATNSimulator +io.burt.jmespath.antlr.v4.runtime.atn.ProfilingATNSimulator +io.burt.jmespath.parser.JmesPathParser$SliceContext +io.burt.jmespath.parser.JmesPathParser$WildcardContext +io.burt.jmespath.parser.JmesPathParser$LiteralContext +io.burt.jmespath.parser.JmesPathParser$ExpressionContext +io.burt.jmespath.parser.JmesPathParser$BracketExpressionContext +io.burt.jmespath.parser.JmesPathParser$NotExpressionContext +io.burt.jmespath.parser.JmesPathParser$IdentifierExpressionContext +io.burt.jmespath.parser.JmesPathParser$ParenExpressionContext +io.burt.jmespath.parser.JmesPathParser$WildcardExpressionContext +io.burt.jmespath.parser.JmesPathParser$MultiSelectListExpressionContext +io.burt.jmespath.parser.JmesPathParser$MultiSelectHashExpressionContext +io.burt.jmespath.parser.JmesPathParser$LiteralExpressionContext +io.burt.jmespath.parser.JmesPathParser$FunctionCallExpressionContext +io.burt.jmespath.parser.JmesPathParser$RawStringExpressionContext +io.burt.jmespath.parser.JmesPathParser$CurrentNodeExpressionContext +io.burt.jmespath.parser.JmesPathParser$ComparisonExpressionContext +io.burt.jmespath.antlr.v4.runtime.FailedPredicateException +io.burt.jmespath.parser.JmesPathParser$AndExpressionContext +io.burt.jmespath.parser.JmesPathParser$OrExpressionContext +io.burt.jmespath.parser.JmesPathParser$PipeExpressionContext +io.burt.jmespath.parser.JmesPathParser$ChainExpressionContext +io.burt.jmespath.parser.JmesPathParser$BracketedExpressionContext +io.burt.jmespath.parser.JmesPathParser$IdentifierContext +io.burt.jmespath.parser.JmesPathParser$JsonObjectContext +io.burt.jmespath.parser.JmesPathParser$CurrentNodeContext +io.burt.jmespath.parser.JmesPathParser$JmesPathExpressionContext +io.burt.jmespath.parser.JmesPathParser$BracketSpecifierContext +io.burt.jmespath.parser.JmesPathParser$BracketIndexContext +io.burt.jmespath.parser.JmesPathParser$BracketStarContext +io.burt.jmespath.parser.JmesPathParser$BracketSliceContext +io.burt.jmespath.parser.JmesPathParser$BracketFlattenContext +io.burt.jmespath.parser.JmesPathParser$SelectContext +io.burt.jmespath.parser.JmesPathParser$ChainedExpressionContext +io.burt.jmespath.parser.JmesPathParser$KeyvalExprContext +io.burt.jmespath.parser.JmesPathParser$FunctionArgContext +io.burt.jmespath.antlr.v4.runtime.NoViableAltException +io.burt.jmespath.parser.JmesPathParser$JsonValueContext +io.burt.jmespath.parser.JmesPathParser$JsonStringValueContext +io.burt.jmespath.parser.JmesPathParser$JsonNumberValueContext +io.burt.jmespath.parser.JmesPathParser$JsonObjectValueContext +io.burt.jmespath.parser.JmesPathParser$JsonArrayValueContext +io.burt.jmespath.parser.JmesPathParser$JsonConstantValueContext +io.burt.jmespath.parser.JmesPathParser$MultiSelectListContext +io.burt.jmespath.parser.JmesPathParser$MultiSelectHashContext +io.burt.jmespath.parser.JmesPathParser$FunctionExpressionContext +io.burt.jmespath.parser.JmesPathParser$ExpressionTypeContext +io.burt.jmespath.parser.JmesPathParser$JsonObjectPairContext +io.burt.jmespath.parser.JmesPathParser$JsonArrayContext +io.burt.jmespath.antlr.v4.runtime.DefaultErrorStrategy +io.burt.jmespath.antlr.v4.runtime.InputMismatchException +io.burt.jmespath.antlr.v4.runtime.atn.SemanticContext +io.burt.jmespath.antlr.v4.runtime.atn.SemanticContext$PrecedencePredicate +io.burt.jmespath.antlr.v4.runtime.atn.SemanticContext$Predicate +io.burt.jmespath.antlr.v4.runtime.atn.PredictionMode +io.burt.jmespath.antlr.v4.runtime.atn.ArrayPredictionContext +io.burt.jmespath.antlr.v4.runtime.misc.MurmurHash +io.burt.jmespath.antlr.v4.runtime.atn.OrderedATNConfigSet$LexerConfigHashSet +io.burt.jmespath.antlr.v4.runtime.atn.SemanticContext$Operator +io.burt.jmespath.antlr.v4.runtime.atn.SemanticContext$OR +io.burt.jmespath.antlr.v4.runtime.atn.SemanticContext$AND +io.burt.jmespath.antlr.v4.runtime.WritableToken +io.burt.jmespath.antlr.v4.runtime.CommonToken +io.burt.jmespath.antlr.v4.runtime.atn.LL1Analyzer +io.burt.jmespath.antlr.v4.runtime.misc.DoubleKeyMap +io.burt.jmespath.antlr.v4.runtime.misc.FlexibleHashMap +io.burt.jmespath.antlr.v4.runtime.atn.PredictionMode$AltAndContextMap +io.burt.jmespath.antlr.v4.runtime.atn.PredictionMode$AltAndContextConfigEqualityComparator +io.burt.jmespath.antlr.v4.runtime.misc.FlexibleHashMap$Entry +io.burt.jmespath.antlr.v4.runtime.tree.TerminalNodeImpl +io.burt.jmespath.antlr.v4.runtime.dfa.DFAState$PredPrediction +io.burt.jmespath.jackson.JacksonRuntime$1 +io.burt.jmespath.jackson.JacksonRuntime$ArrayNodeListWrapper +io.burt.jmespath.function.FunctionArgument +io.burt.jmespath.function.FunctionArgument$V +io.burt.jmespath.function.FunctionArgument$E +software.amazon.lambda.powertools.validation.ValidationUtils$$Lambda$884/0x00007faab44a4e30 +software.amazon.lambda.powertools.validation.handlers.SQSWithWrongEnvelopeHandler$AjcClosure1 +software.amazon.lambda.powertools.validation.internal.ValidationAspectTest$$Lambda$885/0x00007faab44a52b8 +java.lang.invoke.LambdaForm$DMH/0x00007faab44a8000 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$886/0x00007faab44a54e0 +org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$887/0x00007faab44a5718 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$888/0x00007faab44a5938 +org.junit.jupiter.engine.extension.AutoCloseExtension$$Lambda$889/0x00007faab44a5b88 +org.apache.maven.surefire.api.util.internal.ObjectUtils +org.apache.maven.surefire.api.util.internal.ImmutableMap$Node +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$890/0x00007faab44a6228 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$891/0x00007faab44a6450 +com.fasterxml.jackson.databind.util.NativeImageUtil +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$892/0x00007faab44a6880 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$893/0x00007faab44a6aa8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonCreator +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.annotation.JsonCreator$Mode +java.util.concurrent.ConcurrentHashMap$TreeNode +java.util.concurrent.ConcurrentHashMap$TreeBin +com.networknt.schema.Version201909 +com.networknt.schema.Version201909$Holder +com.networknt.schema.Vocabularies +com.networknt.schema.Vocabulary +java.lang.invoke.LambdaForm$DMH/0x00007faab44a8400 +com.networknt.schema.RefValidator$$Lambda$894/0x00007faab44a7b68 +java.lang.invoke.LambdaForm$DMH/0x00007faab44a8800 +java.lang.invoke.LambdaForm$DMH/0x00007faab44a8c00 +java.lang.invoke.LambdaForm$MH/0x00007faab44a9000 +com.networknt.schema.RecursiveRefValidator$$Lambda$895/0x00007faab44a7d90 +com.networknt.schema.utils.JsonNodes +com.networknt.schema.PatternValidator$$Lambda$896/0x00007faab44ac208 +com.networknt.schema.PatternValidator$$Lambda$897/0x00007faab44ac458 +com.networknt.schema.regex.JDKRegularExpression +com.networknt.schema.Version202012 +com.networknt.schema.Version202012$Holder +com.networknt.schema.DynamicRefValidator$$Lambda$898/0x00007faab44accf8 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$899/0x00007faab44acf20 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.util.internal.PrivateMaxEntriesMap$WriteThroughEntry +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$900/0x00007faab44ad3a8 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.std.NumberSerializers$1 +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.core.io.NumberOutput +io.burt.jmespath.antlr.v4.runtime.ProxyErrorListener +io.burt.jmespath.parser.ParseError +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$901/0x00007faab44ade60 +com.networknt.schema.Version4 +com.networknt.schema.Version4$Holder +com.networknt.schema.MinimumValidator$2 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$902/0x00007faab44ae6f0 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$903/0x00007faab44ae910 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$904/0x00007faab44aeb38 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$905/0x00007faab44aed60 +jdk.internal.reflect.GeneratedConstructorAccessor9 +jdk.internal.reflect.GeneratedMethodAccessor20 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$906/0x00007faab44aef88 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$907/0x00007faab44af1b0 +java.lang.Throwable$PrintStreamOrWriter +java.lang.Throwable$WrappedPrintStream +java.lang.StackTraceElement$HashedModules +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$908/0x00007faab44af3d0 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$909/0x00007faab44af5f8 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$910/0x00007faab44af820 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$911/0x00007faab44afa48 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$912/0x00007faab44afc70 +com.fasterxml.jackson.databind.JsonMappingException$Reference +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$913/0x00007faab44aa250 +jdk.internal.reflect.GeneratedMethodAccessor21 +jdk.internal.reflect.GeneratedMethodAccessor22 +jdk.internal.reflect.GeneratedMethodAccessor23 +jdk.internal.reflect.GeneratedMethodAccessor24 +jdk.internal.reflect.GeneratedMethodAccessor25 +jdk.internal.reflect.GeneratedMethodAccessor26 +jdk.internal.reflect.GeneratedMethodAccessor27 +jdk.internal.reflect.GeneratedMethodAccessor28 +jdk.internal.reflect.GeneratedMethodAccessor29 +jdk.internal.reflect.GeneratedMethodAccessor30 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$914/0x00007faab44aa478 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$915/0x00007faab44aa6a0 +com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap$TypeAndSerializer +jdk.internal.reflect.GeneratedMethodAccessor31 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$916/0x00007faab44aacd0 +software.amazon.lambda.powertools.validation.ValidationUtilsTest$$Lambda$917/0x00007faab44aaef8 +com.networknt.schema.Version6 +com.networknt.schema.Version6$Holder +org.junit.platform.launcher.core.OutcomeDelayingEngineExecutionListener$Outcome +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$918/0x00007faab44ab990 +org.junit.platform.launcher.core.CompositeTestExecutionListener$$Lambda$919/0x00007faab44abbc8 +java.lang.invoke.LambdaForm$DMH/0x00007faab44b2800 +org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$920/0x00007faab44b4000 +org.junit.platform.launcher.core.DefaultLauncherSession$ClosedLauncher +org.apache.maven.surefire.api.suite.RunResult +org.apache.maven.surefire.booter.ForkedBooter$6 +org.apache.maven.surefire.booter.ForkedBooter$7 +java.util.concurrent.locks.AbstractQueuedSynchronizer$SharedNode +java.util.concurrent.locks.AbstractQueuedSynchronizer$ExclusiveNode +org.apache.maven.surefire.booter.ForkedBooter$1 +org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory$2 +java.util.IdentityHashMap$IdentityHashMapIterator +java.util.IdentityHashMap$KeyIterator diff --git a/powertools-validation/src/main/resources/schemas/meta/applicator b/powertools-validation/src/main/resources/schemas/meta/applicator deleted file mode 100644 index 24a1cc4f4..000000000 --- a/powertools-validation/src/main/resources/schemas/meta/applicator +++ /dev/null @@ -1,56 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://json-schema.org/draft/2019-09/meta/applicator", - "$vocabulary": { - "https://json-schema.org/draft/2019-09/vocab/applicator": true - }, - "$recursiveAnchor": true, - - "title": "Applicator vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "additionalItems": { "$recursiveRef": "#" }, - "unevaluatedItems": { "$recursiveRef": "#" }, - "items": { - "anyOf": [ - { "$recursiveRef": "#" }, - { "$ref": "#/$defs/schemaArray" } - ] - }, - "contains": { "$recursiveRef": "#" }, - "additionalProperties": { "$recursiveRef": "#" }, - "unevaluatedProperties": { "$recursiveRef": "#" }, - "properties": { - "type": "object", - "additionalProperties": { "$recursiveRef": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$recursiveRef": "#" }, - "propertyNames": { "format": "regex" }, - "default": {} - }, - "dependentSchemas": { - "type": "object", - "additionalProperties": { - "$recursiveRef": "#" - } - }, - "propertyNames": { "$recursiveRef": "#" }, - "if": { "$recursiveRef": "#" }, - "then": { "$recursiveRef": "#" }, - "else": { "$recursiveRef": "#" }, - "allOf": { "$ref": "#/$defs/schemaArray" }, - "anyOf": { "$ref": "#/$defs/schemaArray" }, - "oneOf": { "$ref": "#/$defs/schemaArray" }, - "not": { "$recursiveRef": "#" } - }, - "$defs": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$recursiveRef": "#" } - } - } -} diff --git a/powertools-validation/src/main/resources/schemas/meta/content b/powertools-validation/src/main/resources/schemas/meta/content deleted file mode 100644 index f6752a8ef..000000000 --- a/powertools-validation/src/main/resources/schemas/meta/content +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://json-schema.org/draft/2019-09/meta/content", - "$vocabulary": { - "https://json-schema.org/draft/2019-09/vocab/content": true - }, - "$recursiveAnchor": true, - - "title": "Content vocabulary meta-schema", - - "type": ["object", "boolean"], - "properties": { - "contentMediaType": { "type": "string" }, - "contentEncoding": { "type": "string" }, - "contentSchema": { "$recursiveRef": "#" } - } -} diff --git a/powertools-validation/src/main/resources/schemas/meta/core b/powertools-validation/src/main/resources/schemas/meta/core deleted file mode 100644 index eb708a560..000000000 --- a/powertools-validation/src/main/resources/schemas/meta/core +++ /dev/null @@ -1,57 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://json-schema.org/draft/2019-09/meta/core", - "$vocabulary": { - "https://json-schema.org/draft/2019-09/vocab/core": true - }, - "$recursiveAnchor": true, - - "title": "Core vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "$id": { - "type": "string", - "format": "uri-reference", - "$comment": "Non-empty fragments not allowed.", - "pattern": "^[^#]*#?$" - }, - "$schema": { - "type": "string", - "format": "uri" - }, - "$anchor": { - "type": "string", - "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" - }, - "$ref": { - "type": "string", - "format": "uri-reference" - }, - "$recursiveRef": { - "type": "string", - "format": "uri-reference" - }, - "$recursiveAnchor": { - "type": "boolean", - "default": false - }, - "$vocabulary": { - "type": "object", - "propertyNames": { - "type": "string", - "format": "uri" - }, - "additionalProperties": { - "type": "boolean" - } - }, - "$comment": { - "type": "string" - }, - "$defs": { - "type": "object", - "additionalProperties": { "$recursiveRef": "#" }, - "default": {} - } - } -} diff --git a/powertools-validation/src/main/resources/schemas/meta/format b/powertools-validation/src/main/resources/schemas/meta/format deleted file mode 100644 index 09bbfdda9..000000000 --- a/powertools-validation/src/main/resources/schemas/meta/format +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://json-schema.org/draft/2019-09/meta/format", - "$vocabulary": { - "https://json-schema.org/draft/2019-09/vocab/format": true - }, - "$recursiveAnchor": true, - - "title": "Format vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "format": { "type": "string" } - } -} diff --git a/powertools-validation/src/main/resources/schemas/meta/meta-data b/powertools-validation/src/main/resources/schemas/meta/meta-data deleted file mode 100644 index da04cff6d..000000000 --- a/powertools-validation/src/main/resources/schemas/meta/meta-data +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://json-schema.org/draft/2019-09/meta/meta-data", - "$vocabulary": { - "https://json-schema.org/draft/2019-09/vocab/meta-data": true - }, - "$recursiveAnchor": true, - - "title": "Meta-data vocabulary meta-schema", - - "type": ["object", "boolean"], - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": true, - "deprecated": { - "type": "boolean", - "default": false - }, - "readOnly": { - "type": "boolean", - "default": false - }, - "writeOnly": { - "type": "boolean", - "default": false - }, - "examples": { - "type": "array", - "items": true - } - } -} diff --git a/powertools-validation/src/main/resources/schemas/meta/validation b/powertools-validation/src/main/resources/schemas/meta/validation deleted file mode 100644 index 9f59677b3..000000000 --- a/powertools-validation/src/main/resources/schemas/meta/validation +++ /dev/null @@ -1,98 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://json-schema.org/draft/2019-09/meta/validation", - "$vocabulary": { - "https://json-schema.org/draft/2019-09/vocab/validation": true - }, - "$recursiveAnchor": true, - - "title": "Validation vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "multipleOf": { - "type": "number", - "exclusiveMinimum": 0 - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "number" - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "number" - }, - "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, - "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, - "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, - "minContains": { - "$ref": "#/$defs/nonNegativeInteger", - "default": 1 - }, - "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, - "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, - "required": { "$ref": "#/$defs/stringArray" }, - "dependentRequired": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/stringArray" - } - }, - "const": true, - "enum": { - "type": "array", - "items": true - }, - "type": { - "anyOf": [ - { "$ref": "#/$defs/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/$defs/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - } - }, - "$defs": { - "nonNegativeInteger": { - "type": "integer", - "minimum": 0 - }, - "nonNegativeIntegerDefault0": { - "$ref": "#/$defs/nonNegativeInteger", - "default": 0 - }, - "simpleTypes": { - "enum": [ - "array", - "boolean", - "integer", - "null", - "number", - "object", - "string" - ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true, - "default": [] - } - } -} diff --git a/powertools-validation/src/main/resources/schemas/meta_schema_V201909 b/powertools-validation/src/main/resources/schemas/meta_schema_V201909 deleted file mode 100644 index 2248a0c80..000000000 --- a/powertools-validation/src/main/resources/schemas/meta_schema_V201909 +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://json-schema.org/draft/2019-09/schema", - "$vocabulary": { - "https://json-schema.org/draft/2019-09/vocab/core": true, - "https://json-schema.org/draft/2019-09/vocab/applicator": true, - "https://json-schema.org/draft/2019-09/vocab/validation": true, - "https://json-schema.org/draft/2019-09/vocab/meta-data": true, - "https://json-schema.org/draft/2019-09/vocab/format": false, - "https://json-schema.org/draft/2019-09/vocab/content": true - }, - "$recursiveAnchor": true, - - "title": "Core and Validation specifications meta-schema", - "allOf": [ - {"$ref": "meta/core"}, - {"$ref": "meta/applicator"}, - {"$ref": "meta/validation"}, - {"$ref": "meta/meta-data"}, - {"$ref": "meta/format"}, - {"$ref": "meta/content"} - ], - "type": ["object", "boolean"], - "properties": { - "definitions": { - "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.", - "type": "object", - "additionalProperties": { "$recursiveRef": "#" }, - "default": {} - }, - "dependencies": { - "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"", - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$recursiveRef": "#" }, - { "$ref": "meta/validation#/$defs/stringArray" } - ] - } - } - } -} diff --git a/powertools-validation/src/main/resources/schemas/meta_schema_V4 b/powertools-validation/src/main/resources/schemas/meta_schema_V4 deleted file mode 100644 index bcbb84743..000000000 --- a/powertools-validation/src/main/resources/schemas/meta_schema_V4 +++ /dev/null @@ -1,149 +0,0 @@ -{ - "id": "http://json-schema.org/draft-04/schema#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Core schema meta-schema", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$ref": "#" } - }, - "positiveInteger": { - "type": "integer", - "minimum": 0 - }, - "positiveIntegerDefault0": { - "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] - }, - "simpleTypes": { - "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "minItems": 1, - "uniqueItems": true - } - }, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "$schema": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": {}, - "multipleOf": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "boolean", - "default": false - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "boolean", - "default": false - }, - "maxLength": { "$ref": "#/definitions/positiveInteger" }, - "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "additionalItems": { - "anyOf": [ - { "type": "boolean" }, - { "$ref": "#" } - ], - "default": {} - }, - "items": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/schemaArray" } - ], - "default": {} - }, - "maxItems": { "$ref": "#/definitions/positiveInteger" }, - "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "maxProperties": { "$ref": "#/definitions/positiveInteger" }, - "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, - "required": { "$ref": "#/definitions/stringArray" }, - "additionalProperties": { - "anyOf": [ - { "type": "boolean" }, - { "$ref": "#" } - ], - "default": {} - }, - "definitions": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/stringArray" } - ] - } - }, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "type": { - "anyOf": [ - { "$ref": "#/definitions/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/definitions/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "format": { "type": "string" }, - "allOf": { "$ref": "#/definitions/schemaArray" }, - "anyOf": { "$ref": "#/definitions/schemaArray" }, - "oneOf": { "$ref": "#/definitions/schemaArray" }, - "not": { "$ref": "#" } - }, - "dependencies": { - "exclusiveMaximum": [ "maximum" ], - "exclusiveMinimum": [ "minimum" ] - }, - "default": {} -} diff --git a/powertools-validation/src/main/resources/schemas/meta_schema_V6 b/powertools-validation/src/main/resources/schemas/meta_schema_V6 deleted file mode 100644 index bd3e763bc..000000000 --- a/powertools-validation/src/main/resources/schemas/meta_schema_V6 +++ /dev/null @@ -1,155 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-06/schema#", - "$id": "http://json-schema.org/draft-06/schema#", - "title": "Core schema meta-schema", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$ref": "#" } - }, - "nonNegativeInteger": { - "type": "integer", - "minimum": 0 - }, - "nonNegativeIntegerDefault0": { - "allOf": [ - { "$ref": "#/definitions/nonNegativeInteger" }, - { "default": 0 } - ] - }, - "simpleTypes": { - "enum": [ - "array", - "boolean", - "integer", - "null", - "number", - "object", - "string" - ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true, - "default": [] - } - }, - "type": ["object", "boolean"], - "properties": { - "$id": { - "type": "string", - "format": "uri-reference" - }, - "$schema": { - "type": "string", - "format": "uri" - }, - "$ref": { - "type": "string", - "format": "uri-reference" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": {}, - "examples": { - "type": "array", - "items": {} - }, - "multipleOf": { - "type": "number", - "exclusiveMinimum": 0 - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "number" - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "number" - }, - "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, - "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "additionalItems": { "$ref": "#" }, - "items": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/schemaArray" } - ], - "default": {} - }, - "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, - "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "contains": { "$ref": "#" }, - "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, - "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "required": { "$ref": "#/definitions/stringArray" }, - "additionalProperties": { "$ref": "#" }, - "definitions": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "propertyNames": { "format": "regex" }, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/stringArray" } - ] - } - }, - "propertyNames": { "$ref": "#" }, - "const": {}, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "type": { - "anyOf": [ - { "$ref": "#/definitions/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/definitions/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "format": { "type": "string" }, - "allOf": { "$ref": "#/definitions/schemaArray" }, - "anyOf": { "$ref": "#/definitions/schemaArray" }, - "oneOf": { "$ref": "#/definitions/schemaArray" }, - "not": { "$ref": "#" } - }, - "default": {} -} diff --git a/powertools-validation/src/main/resources/schemas/meta_schema_V7 b/powertools-validation/src/main/resources/schemas/meta_schema_V7 deleted file mode 100644 index fb92c7f75..000000000 --- a/powertools-validation/src/main/resources/schemas/meta_schema_V7 +++ /dev/null @@ -1,172 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://json-schema.org/draft-07/schema#", - "title": "Core schema meta-schema", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$ref": "#" } - }, - "nonNegativeInteger": { - "type": "integer", - "minimum": 0 - }, - "nonNegativeIntegerDefault0": { - "allOf": [ - { "$ref": "#/definitions/nonNegativeInteger" }, - { "default": 0 } - ] - }, - "simpleTypes": { - "enum": [ - "array", - "boolean", - "integer", - "null", - "number", - "object", - "string" - ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true, - "default": [] - } - }, - "type": ["object", "boolean"], - "properties": { - "$id": { - "type": "string", - "format": "uri-reference" - }, - "$schema": { - "type": "string", - "format": "uri" - }, - "$ref": { - "type": "string", - "format": "uri-reference" - }, - "$comment": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": true, - "readOnly": { - "type": "boolean", - "default": false - }, - "writeOnly": { - "type": "boolean", - "default": false - }, - "examples": { - "type": "array", - "items": true - }, - "multipleOf": { - "type": "number", - "exclusiveMinimum": 0 - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "number" - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "number" - }, - "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, - "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "additionalItems": { "$ref": "#" }, - "items": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/schemaArray" } - ], - "default": true - }, - "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, - "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "contains": { "$ref": "#" }, - "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, - "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "required": { "$ref": "#/definitions/stringArray" }, - "additionalProperties": { "$ref": "#" }, - "definitions": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "propertyNames": { "format": "regex" }, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/stringArray" } - ] - } - }, - "propertyNames": { "$ref": "#" }, - "const": true, - "enum": { - "type": "array", - "items": true, - "minItems": 1, - "uniqueItems": true - }, - "type": { - "anyOf": [ - { "$ref": "#/definitions/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/definitions/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "format": { "type": "string" }, - "contentMediaType": { "type": "string" }, - "contentEncoding": { "type": "string" }, - "if": { "$ref": "#" }, - "then": { "$ref": "#" }, - "else": { "$ref": "#" }, - "allOf": { "$ref": "#/definitions/schemaArray" }, - "anyOf": { "$ref": "#/definitions/schemaArray" }, - "oneOf": { "$ref": "#/definitions/schemaArray" }, - "not": { "$ref": "#" } - }, - "default": true -} diff --git a/powertools-validation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors b/powertools-validation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors new file mode 100644 index 000000000..bcaa3fabf --- /dev/null +++ b/powertools-validation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors @@ -0,0 +1 @@ +software.amazon.lambda.powertools.validation.internal.ValidationUserAgentInterceptor diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationConfigTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationConfigTest.java new file mode 100644 index 000000000..bde195271 --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationConfigTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.validation; + +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.Mockito.mock; + +import org.crac.Context; +import org.crac.Resource; +import org.junit.jupiter.api.Test; + +class ValidationConfigTest { + + ValidationConfig config = ValidationConfig.get(); + Context<Resource> context = mock(Context.class); + + @Test + void testBeforeCheckpointDoesNotThrowException() { + assertThatNoException().isThrownBy(() -> config.beforeCheckpoint(context)); + } + + @Test + void testAfterRestoreDoesNotThrowException() { + assertThatNoException().isThrownBy(() -> config.afterRestore(context)); + } +} diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java index fa0d1394c..73c3c6567 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java @@ -48,7 +48,7 @@ public void testLoadSchemaV7OK() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); JsonSchema jsonSchema = getJsonSchema("classpath:/schema_v7.json", true); assertThat(jsonSchema).isNotNull(); - assertThat(jsonSchema.getCurrentUri()).asString().isEqualTo("http://example.com/product.json"); + assertThat(jsonSchema.getId()).isEqualTo("http://example.com/product.json"); } @Test @@ -57,7 +57,7 @@ public void testLoadSchemaV7KO() { assertThatThrownBy(() -> getJsonSchema("classpath:/schema_v7_ko.json", true)) .isInstanceOf(IllegalArgumentException.class) .hasMessage( - "The schema classpath:/schema_v7_ko.json is not valid, it does not respect the specification V7"); + "The schema classpath:/schema_v7_ko.json is not valid, it does not respect the specification /draft-07/schema#"); } @Test @@ -73,28 +73,35 @@ public void testLoadMetaSchema_NoValidation() { @Test public void testLoadMetaSchemaV2019() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V201909); - JsonSchema jsonSchema = getJsonSchema("classpath:/schemas/meta_schema_V201909", true); + JsonSchema jsonSchema = getJsonSchema("classpath:/draft/2019-09/schema", true); + assertThat(jsonSchema).isNotNull(); + } + + @Test + public void testLoadMetaSchemaV2020() { + ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V202012); + JsonSchema jsonSchema = getJsonSchema("classpath:/draft/2020-12/schema", true); assertThat(jsonSchema).isNotNull(); } @Test public void testLoadMetaSchemaV7() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); - JsonSchema jsonSchema = getJsonSchema("classpath:/schemas/meta_schema_V7", true); + JsonSchema jsonSchema = getJsonSchema("classpath:/draft-07/schema", true); assertThat(jsonSchema).isNotNull(); } @Test public void testLoadMetaSchemaV6() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V6); - JsonSchema jsonSchema = getJsonSchema("classpath:/schemas/meta_schema_V6", true); + JsonSchema jsonSchema = getJsonSchema("classpath:/draft-06/schema", true); assertThat(jsonSchema).isNotNull(); } @Test public void testLoadMetaSchemaV4() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V4); - JsonSchema jsonSchema = getJsonSchema("classpath:/schemas/meta_schema_V4", true); + JsonSchema jsonSchema = getJsonSchema("classpath:/draft-04/schema", true); assertThat(jsonSchema).isNotNull(); } diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7APIGatewayProxyRequestEventHandler.java similarity index 54% rename from powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java rename to powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7APIGatewayProxyRequestEventHandler.java index 3bad9644f..74e8605a5 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7APIGatewayProxyRequestEventHandler.java @@ -12,21 +12,23 @@ * */ -package software.amazon.lambda.powertools.sqs.handlers; +package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import software.amazon.lambda.powertools.sqs.SampleSqsHandler; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -public class LambdaHandlerApiGateway implements RequestHandler<APIGatewayProxyRequestEvent, String> { +import software.amazon.lambda.powertools.validation.Validation; +public class GenericSchemaV7APIGatewayProxyRequestEventHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Validation(inboundSchema = "classpath:/schema_v7.json") @Override - @SqsLargeMessage - @SqsBatch(value = SampleSqsHandler.class) - public String handleRequest(APIGatewayProxyRequestEvent sqsEvent, Context context) { - return sqsEvent.getBody(); + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent(); + response.setBody("valid-test"); + response.setStatusCode(200); + return response; } -} +} \ No newline at end of file diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7StringHandler.java similarity index 92% rename from powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java rename to powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7StringHandler.java index 5b8343d1b..ab0645f29 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7StringHandler.java @@ -16,13 +16,14 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; + import software.amazon.lambda.powertools.validation.Validation; -public class GenericSchemaV7Handler<T> implements RequestHandler<T, String> { +public class GenericSchemaV7StringHandler<T> implements RequestHandler<T, String> { @Validation(inboundSchema = "classpath:/schema_v7.json") @Override public String handleRequest(T input, Context context) { return "OK"; } -} +} \ No newline at end of file diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandlerWithError.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandlerWithError.java new file mode 100644 index 000000000..e6e702fb6 --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandlerWithError.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.validation.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; +import java.util.ArrayList; +import software.amazon.lambda.powertools.validation.Validation; + +public class KinesisHandlerWithError implements RequestHandler<KinesisEvent, StreamsEventResponse> { + + @Override + @Validation(inboundSchema = "classpath:/schema_v7.json") + public StreamsEventResponse handleRequest(KinesisEvent input, Context context) { + StreamsEventResponse response = StreamsEventResponse.builder().withBatchItemFailures(new ArrayList<>()).build(); + assert input.getRecords().size() == 2; // invalid messages have been removed from the input + response.getBatchItemFailures().add(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier("1234").build()); + return response; + } +} diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSHandlerWithError.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSHandlerWithError.java new file mode 100644 index 000000000..23fceab5b --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSHandlerWithError.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.validation.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import java.util.ArrayList; +import software.amazon.lambda.powertools.validation.Validation; + +public class SQSHandlerWithError implements RequestHandler<SQSEvent, SQSBatchResponse> { + + @Override + @Validation(inboundSchema = "classpath:/schema_v7.json") + public SQSBatchResponse handleRequest(SQSEvent input, Context context) { + SQSBatchResponse response = SQSBatchResponse.builder().withBatchItemFailures(new ArrayList<>()).build(); + assert input.getRecords().size() == 2; // invalid messages have been removed from the input + response.getBatchItemFailures().add(SQSBatchResponse.BatchItemFailure.builder().withItemIdentifier("1234").build()); + return response; + } +} diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardKinesisHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardKinesisHandler.java new file mode 100644 index 000000000..1afc5c5ec --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardKinesisHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.validation.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; +import software.amazon.lambda.powertools.validation.Validation; + +public class StandardKinesisHandler implements RequestHandler<KinesisEvent, StreamsEventResponse> { + + @Override + @Validation(inboundSchema = "classpath:/schema_v7.json") + public StreamsEventResponse handleRequest(KinesisEvent input, Context context) { + StreamsEventResponse response = StreamsEventResponse.builder().build(); + assert input.getRecords().size() == 2; // invalid messages have been removed from the input + return response; + } +} diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardSQSHandler.java similarity index 53% rename from powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java rename to powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardSQSHandler.java index 5592b1fd3..e0f0ece2d 100644 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardSQSHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Copyright 2024 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -12,20 +12,21 @@ * */ -package software.amazon.lambda.powertools.testsuite.handler; +package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; +import software.amazon.lambda.powertools.validation.Validation; -public class LoggingOrderMessageHandler implements RequestHandler<SQSEvent, String> { +public class StandardSQSHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { @Override - @SqsLargeMessage - @Logging(logEvent = true) - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); + @Validation(inboundSchema = "classpath:/schema_v7.json") + public SQSBatchResponse handleRequest(SQSEvent input, Context context) { + SQSBatchResponse response = SQSBatchResponse.builder().build(); + assert input.getRecords().size() == 2; // invalid messages have been removed from the input + return response; } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundStringHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundAPIGatewayV2HTTPEventHandler.java similarity index 87% rename from powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundStringHandler.java rename to powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundAPIGatewayV2HTTPEventHandler.java index fd5692884..b8c67b1eb 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundStringHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundAPIGatewayV2HTTPEventHandler.java @@ -17,10 +17,12 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse; + import software.amazon.lambda.powertools.validation.Validation; -public class ValidationInboundStringHandler implements RequestHandler<APIGatewayV2HTTPEvent, String> { +public class ValidationInboundAPIGatewayV2HTTPEventHandler implements RequestHandler<APIGatewayV2HTTPEvent, APIGatewayV2HTTPResponse> { private static final String schema = "{\n" + " \"$schema\": \"http://json-schema.org/draft-07/schema\",\n" + @@ -80,7 +82,10 @@ public class ValidationInboundStringHandler implements RequestHandler<APIGateway @Override @Validation(inboundSchema = schema) - public String handleRequest(APIGatewayV2HTTPEvent input, Context context) { - return "OK"; + public APIGatewayV2HTTPResponse handleRequest(APIGatewayV2HTTPEvent input, Context context) { + APIGatewayV2HTTPResponse response = new APIGatewayV2HTTPResponse(); + response.setBody("valid-test"); + response.setStatusCode(200); + return response; } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/HandledResponseEventsArgumentsProvider.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/HandledResponseEventsArgumentsProvider.java new file mode 100644 index 000000000..728e0ae88 --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/HandledResponseEventsArgumentsProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.validation.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse; + +/** + * Provides test arguments that are used in unit tests. + * It creates API Gateway response arguments that can be used to confirm + * that @Validation validates responses and returns a response's headers even + * when validation fails + */ +public class HandledResponseEventsArgumentsProvider implements ArgumentsProvider { + + @Override + public Stream<? extends Arguments> provideArguments(ExtensionContext context) { + + String body = "{id"; + + Map<String, String> headers = new HashMap<>(); + headers.put("header1", "value1,value2,value3"); + Map<String, List<String>> headersList = new HashMap<>(); + List<String> headerValues = new ArrayList<>(); + headerValues.add("value1"); + headerValues.add("value2"); + headerValues.add("value3"); + headersList.put("header1", headerValues); + + final APIGatewayProxyResponseEvent apiGWProxyResponseEvent = new APIGatewayProxyResponseEvent() + .withBody(body) + .withHeaders(headers) + .withMultiValueHeaders(headersList); + + APIGatewayV2HTTPResponse apiGWV2HTTPResponse = new APIGatewayV2HTTPResponse(); + apiGWV2HTTPResponse.setBody(body); + apiGWV2HTTPResponse.setHeaders(headers); + apiGWV2HTTPResponse.setMultiValueHeaders(headersList); + + return Stream.of(apiGWProxyResponseEvent, apiGWV2HTTPResponse).map(Arguments::of); + } +} diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java index b634d6f8c..74803a05a 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java @@ -35,11 +35,6 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) { String body = "{id"; - final APIGatewayProxyResponseEvent apiGWProxyResponseEvent = new APIGatewayProxyResponseEvent().withBody(body); - - APIGatewayV2HTTPResponse apiGWV2HTTPResponse = new APIGatewayV2HTTPResponse(); - apiGWV2HTTPResponse.setBody(body); - APIGatewayV2WebSocketResponse apiGWV2WebSocketResponse = new APIGatewayV2WebSocketResponse(); apiGWV2WebSocketResponse.setBody(body); @@ -53,7 +48,7 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) { KinesisAnalyticsInputPreprocessingResponse.Result.Ok, buffer)); kaipResponse.setRecords(records); - return Stream.of(apiGWProxyResponseEvent, apiGWV2HTTPResponse, apiGWV2WebSocketResponse, albResponseEvent, + return Stream.of(apiGWV2WebSocketResponse, albResponseEvent, kaipResponse).map(Arguments::of); } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java index c8d5e7ade..42a18307e 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java @@ -17,11 +17,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.when; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse; import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; @@ -35,12 +37,18 @@ import com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent; import com.amazonaws.services.lambda.runtime.events.RabbitMQEvent; import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; -import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; -import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; +import com.amazonaws.services.lambda.runtime.tests.annotations.Event; import com.networknt.schema.SpecVersion; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; @@ -55,14 +63,19 @@ import software.amazon.lambda.powertools.validation.Validation; import software.amazon.lambda.powertools.validation.ValidationConfig; import software.amazon.lambda.powertools.validation.ValidationException; -import software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7Handler; +import software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7APIGatewayProxyRequestEventHandler; +import software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7StringHandler; +import software.amazon.lambda.powertools.validation.handlers.KinesisHandlerWithError; +import software.amazon.lambda.powertools.validation.handlers.SQSHandlerWithError; import software.amazon.lambda.powertools.validation.handlers.SQSWithCustomEnvelopeHandler; import software.amazon.lambda.powertools.validation.handlers.SQSWithWrongEnvelopeHandler; -import software.amazon.lambda.powertools.validation.handlers.ValidationInboundStringHandler; +import software.amazon.lambda.powertools.validation.handlers.StandardKinesisHandler; +import software.amazon.lambda.powertools.validation.handlers.StandardSQSHandler; +import software.amazon.lambda.powertools.validation.handlers.ValidationInboundAPIGatewayV2HTTPEventHandler; import software.amazon.lambda.powertools.validation.model.MyCustomEvent; -public class ValidationAspectTest { +class ValidationAspectTest { @Mock Validation validation; @@ -99,7 +112,7 @@ void setUp() { @ParameterizedTest @ArgumentsSource(ResponseEventsArgumentsProvider.class) - public void testValidateOutboundJsonSchema(Object object) throws Throwable { + void testValidateOutboundJsonSchemaWithExceptions(Object object) throws Throwable { when(validation.schemaVersion()).thenReturn(SpecVersion.VersionFlag.V7); when(pjp.getSignature()).thenReturn(signature); when(pjp.getSignature().getDeclaringType()).thenReturn(RequestHandler.class); @@ -114,9 +127,50 @@ public void testValidateOutboundJsonSchema(Object object) throws Throwable { validationAspect.around(pjp, validation); }); } + + @ParameterizedTest + @ArgumentsSource(HandledResponseEventsArgumentsProvider.class) + void testValidateOutboundJsonSchemaWithHandledExceptions(Object object) throws Throwable { + when(validation.schemaVersion()).thenReturn(SpecVersion.VersionFlag.V7); + when(pjp.getSignature()).thenReturn(signature); + when(pjp.getSignature().getDeclaringType()).thenReturn(RequestHandler.class); + Object[] args = {new Object(), context}; + when(pjp.getArgs()).thenReturn(args); + when(pjp.proceed(args)).thenReturn(object); + when(validation.inboundSchema()).thenReturn(""); + when(validation.outboundSchema()).thenReturn("classpath:/schema_v7.json"); + + Object response = validationAspect.around(pjp, validation); + assertThat(response).isInstanceOfAny(APIGatewayProxyResponseEvent.class, APIGatewayV2HTTPResponse.class); + + List<String> headerValues = new ArrayList<>(); + headerValues.add("value1"); + headerValues.add("value2"); + headerValues.add("value3"); + + if (response instanceof APIGatewayProxyResponseEvent) { + assertThat(response).isInstanceOfSatisfying(APIGatewayProxyResponseEvent.class, t -> { + assertThat(t.getStatusCode()).isEqualTo(400); + assertThat(t.getBody()).isNotBlank(); + assertThat(t.getIsBase64Encoded()).isFalse(); + assertThat(t.getHeaders()).containsEntry("header1", "value1,value2,value3"); + assertThat(t.getMultiValueHeaders()).containsEntry("header1", headerValues); + }); + } else if (response instanceof APIGatewayV2HTTPResponse) { + assertThat(response).isInstanceOfSatisfying(APIGatewayV2HTTPResponse.class, t -> { + assertThat(t.getStatusCode()).isEqualTo(400); + assertThat(t.getBody()).isNotBlank(); + assertThat(t.getIsBase64Encoded()).isFalse(); + assertThat(t.getHeaders()).containsEntry("header1", "value1,value2,value3"); + assertThat(t.getMultiValueHeaders()).containsEntry("header1", headerValues); + }); + } else { + fail(); + } + } @Test - public void testValidateOutboundJsonSchema_APIGWV2() throws Throwable { + void testValidateOutboundJsonSchema_APIGWV2() throws Throwable { when(validation.schemaVersion()).thenReturn(SpecVersion.VersionFlag.V7); when(pjp.getSignature()).thenReturn(signature); when(pjp.getSignature().getDeclaringType()).thenReturn(RequestHandler.class); @@ -136,100 +190,162 @@ public void testValidateOutboundJsonSchema_APIGWV2() throws Throwable { } @Test - public void validate_inputOK_schemaInClasspath_shouldValidate() { - GenericSchemaV7Handler<APIGatewayProxyRequestEvent> handler = new GenericSchemaV7Handler(); + void validate_inputOK_schemaInClasspath_shouldValidate() { + GenericSchemaV7APIGatewayProxyRequestEventHandler handler = new GenericSchemaV7APIGatewayProxyRequestEventHandler(); APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); event.setBody("{" + " \"id\": 1," + " \"name\": \"Lampshade\"," + " \"price\": 42" + "}"); - assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); + + + APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + assertThat(response.getBody()).isEqualTo("valid-test"); + assertThat(response.getStatusCode()).isEqualTo(200); + } @Test - public void validate_inputKO_schemaInClasspath_shouldThrowValidationException() { - GenericSchemaV7Handler<APIGatewayProxyRequestEvent> handler = new GenericSchemaV7Handler(); + void validate_inputKO_schemaInClasspath_shouldThrowValidationException() { + GenericSchemaV7APIGatewayProxyRequestEventHandler handler = new GenericSchemaV7APIGatewayProxyRequestEventHandler(); + + Map<String, String> headers = new HashMap<>(); + headers.put("header1", "value1"); + Map<String, List<String>> headersList = new HashMap<>(); + List<String> headerValues = new ArrayList<>(); + headerValues.add("value1"); + headersList.put("header1", headerValues); + APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); event.setBody("{" + - " \"id\": 1," + - " \"name\": \"Lampshade\"," + - " \"price\": -2" + - "}"); + " \"id\": 1," + + " \"name\": \"Lampshade\"," + + " \"price\": -2" + + "}"); + event.setHeaders(headers); + event.setMultiValueHeaders(headersList); + // price is negative - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> handler.handleRequest(event, context)); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + assertThat(response.getBody()).isNotBlank(); + assertThat(response.getStatusCode()).isEqualTo(400); + assertThat(response.getHeaders()).isEmpty(); + assertThat(response.getMultiValueHeaders()).isEmpty(); } @Test - public void validate_inputOK_schemaInString_shouldValidate() { - ValidationInboundStringHandler handler = new ValidationInboundStringHandler(); + void validate_inputOK_schemaInString_shouldValidate() { + ValidationInboundAPIGatewayV2HTTPEventHandler handler = new ValidationInboundAPIGatewayV2HTTPEventHandler(); APIGatewayV2HTTPEvent event = new APIGatewayV2HTTPEvent(); event.setBody("{" + - " \"id\": 1," + - " \"name\": \"Lampshade\"," + - " \"price\": 42" + - "}"); - assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); + " \"id\": 1," + + " \"name\": \"Lampshade\"," + + " \"price\": 42" + + "}"); + + APIGatewayV2HTTPResponse response = handler.handleRequest(event, context); + assertThat(response.getBody()).isEqualTo("valid-test"); + assertThat(response.getStatusCode()).isEqualTo(200); } + @Test - public void validate_inputKO_schemaInString_shouldThrowValidationException() { - ValidationInboundStringHandler handler = new ValidationInboundStringHandler(); + void validate_inputKO_schemaInString_shouldThrowValidationException() { + ValidationInboundAPIGatewayV2HTTPEventHandler handler = new ValidationInboundAPIGatewayV2HTTPEventHandler(); + + Map<String, String> headers = new HashMap<>(); + headers.put("header1", "value1"); + APIGatewayV2HTTPEvent event = new APIGatewayV2HTTPEvent(); event.setBody("{" + - " \"id\": 1," + - " \"name\": \"Lampshade\"" + - "}"); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> handler.handleRequest(event, context)); - } + " \"id\": 1," + + " \"name\": \"Lampshade\"" + + "}"); + event.setHeaders(headers); - @Test - public void validate_SQS() { - PojoSerializer<SQSEvent> pojoSerializer = - LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); - SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs.json")); + APIGatewayV2HTTPResponse response = handler.handleRequest(event, context); + assertThat(response.getBody()).isNotBlank(); + assertThat(response.getStatusCode()).isEqualTo(400); + assertThat(response.getHeaders()).isEmpty(); + assertThat(response.getMultiValueHeaders()).isEmpty(); + } - GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); + @ParameterizedTest + @Event(value = "sqs.json", type = SQSEvent.class) + void validate_SQS(SQSEvent event) { + GenericSchemaV7StringHandler<Object> handler = new GenericSchemaV7StringHandler<>(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } - @Test - public void validate_SQS_CustomEnvelopeTakePrecedence() { - PojoSerializer<SQSEvent> pojoSerializer = - LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); - SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); + @ParameterizedTest + @Event(value = "sqs_invalid_messages.json", type = SQSEvent.class) + void validate_SQS_with_validation_partial_failure(SQSEvent event) { + StandardSQSHandler handler = new StandardSQSHandler(); + SQSBatchResponse response = handler.handleRequest(event, context); + assertThat(response.getBatchItemFailures()).hasSize(2); + assertThat(response.getBatchItemFailures().stream().map(SQSBatchResponse.BatchItemFailure::getItemIdentifier).collect( + Collectors.toList())).contains("d9144555-9a4f-4ec3-99a0-fc4e625a8db3", "d9144555-9a4f-4ec3-99a0-fc4e625a8db5"); + } + + @ParameterizedTest + @Event(value = "sqs_invalid_messages.json", type = SQSEvent.class) + void validate_SQS_with_partial_failure(SQSEvent event) { + SQSHandlerWithError handler = new SQSHandlerWithError(); + SQSBatchResponse response = handler.handleRequest(event, context); + assertThat(response.getBatchItemFailures()).hasSize(3); + assertThat(response.getBatchItemFailures().stream().map(SQSBatchResponse.BatchItemFailure::getItemIdentifier).collect( + Collectors.toList())).contains("d9144555-9a4f-4ec3-99a0-fc4e625a8db3", "d9144555-9a4f-4ec3-99a0-fc4e625a8db5", "1234"); + } + @ParameterizedTest + @Event(value = "sqs_message.json", type = SQSEvent.class) + void validate_SQS_CustomEnvelopeTakePrecedence(SQSEvent event) { SQSWithCustomEnvelopeHandler handler = new SQSWithCustomEnvelopeHandler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } - @Test - public void validate_SQS_WrongEnvelope_shouldThrowValidationException() { - PojoSerializer<SQSEvent> pojoSerializer = - LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); - SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); - + @ParameterizedTest + @Event(value = "sqs_message.json", type = SQSEvent.class) + void validate_SQS_WrongEnvelope_shouldThrowValidationException(SQSEvent event) { SQSWithWrongEnvelopeHandler handler = new SQSWithWrongEnvelopeHandler(); assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> handler.handleRequest(event, context)); } - @Test - public void validate_Kinesis() { - PojoSerializer<KinesisEvent> pojoSerializer = - LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); - KinesisEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/kinesis.json")); - - GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); + @ParameterizedTest + @Event(value = "kinesis.json", type = KinesisEvent.class) + void validate_Kinesis(KinesisEvent event) { + GenericSchemaV7StringHandler<Object> handler = new GenericSchemaV7StringHandler<>(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } + @ParameterizedTest + @Event(value = "kinesis_invalid_messages.json", type = KinesisEvent.class) + void validate_Kinesis_with_validation_partial_failure(KinesisEvent event) { + StandardKinesisHandler handler = new StandardKinesisHandler(); + StreamsEventResponse response = handler.handleRequest(event, context); + assertThat(response.getBatchItemFailures()).hasSize(2); + assertThat(response.getBatchItemFailures().stream().map(StreamsEventResponse.BatchItemFailure::getItemIdentifier).collect( + Collectors.toList())).contains("49545115243490985018280067714973144582180062593244200962", "49545115243490985018280067714973144582180062593244200964"); + } + + @ParameterizedTest + @Event(value = "kinesis_invalid_messages.json", type = KinesisEvent.class) + void validate_Kinesis_with_partial_failure(KinesisEvent event) { + KinesisHandlerWithError handler = new KinesisHandlerWithError(); + StreamsEventResponse response = handler.handleRequest(event, context); + assertThat(response.getBatchItemFailures()).hasSize(3); + assertThat(response.getBatchItemFailures().stream().map(StreamsEventResponse.BatchItemFailure::getItemIdentifier).collect( + Collectors.toList())).contains("49545115243490985018280067714973144582180062593244200962", "49545115243490985018280067714973144582180062593244200964", "1234"); + } + @ParameterizedTest @MethodSource("provideEventAndEventType") - public void validateEEvent(String jsonResource, Class eventClass) throws IOException { + void validateEEvent(String jsonResource, Class eventClass) throws IOException { Object event = ValidationConfig.get().getObjectMapper() .readValue(this.getClass().getResourceAsStream(jsonResource), eventClass); - GenericSchemaV7Handler<Object> handler = new GenericSchemaV7Handler(); + GenericSchemaV7StringHandler<Object> handler = new GenericSchemaV7StringHandler<>(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptorTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptorTest.java new file mode 100644 index 000000000..9bacf33f9 --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package software.amazon.lambda.powertools.validation.internal; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.assertj.core.api.Assertions.assertThat; + +class ValidationUserAgentInterceptorTest { + + @Test + void shouldConfigureUserAgentWhenCreatingAwsSdkClient() { + // WHEN creating an AWS SDK client, the interceptor should be loaded + // We use S3 client but it can be any arbitrary AWS SDK client + S3Client.builder().region(Region.US_EAST_1).build(); + + // THEN the user agent system property should be set + String userAgent = System.getProperty("sdk.ua.appId"); + assertThat(userAgent).contains("PT/VALIDATION/"); + } +} diff --git a/powertools-validation/src/test/resources/kinesis_invalid_messages.json b/powertools-validation/src/test/resources/kinesis_invalid_messages.json new file mode 100644 index 000000000..3d805c4dd --- /dev/null +++ b/powertools-validation/src/test/resources/kinesis_invalid_messages.json @@ -0,0 +1,72 @@ +{ + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "ewogICJpZCI6IDQzMjQyLAogICJuYW1lIjogIkZvb0JhciBYWSIsCiAgInByaWNlIjogMjU4Cn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-04", + "kinesisSchemaVersion": "1.0", + "data": "ewogICJpZCI6InN0cmluZ0lkIiwKICAibmFtZSI6ICJGb29CYXIgWFkiLAogICJwcmljZSI6IDI1OAp9", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200962", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-04", + "kinesisSchemaVersion": "1.0", + "data": "ewogICJpZCI6IDQyNSwKICAibmFtZSI6ICJCYXJGb28iLAogICJwcmljZSI6IDQzCn0=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200963", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-04", + "kinesisSchemaVersion": "1.0", + "data": "ewogICJpZCI6MTIzNCwKICAibmFtZSI6ICJGb28iLAogICJwcmljZSI6IDI1OAp9", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200964", + "approximateArrivalTimestamp": 1428537600, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + } + ] +} diff --git a/powertools-validation/src/test/resources/schema_v7.json b/powertools-validation/src/test/resources/schema_v7.json index f38272f2d..e382b8971 100644 --- a/powertools-validation/src/test/resources/schema_v7.json +++ b/powertools-validation/src/test/resources/schema_v7.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/product.json", "type": "object", "title": "Product schema", diff --git a/powertools-validation/src/test/resources/schema_v7_ko.json b/powertools-validation/src/test/resources/schema_v7_ko.json index f54bcb3c7..aed187c34 100644 --- a/powertools-validation/src/test/resources/schema_v7_ko.json +++ b/powertools-validation/src/test/resources/schema_v7_ko.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Product schema", "description": "JSON schema to validate Products", diff --git a/powertools-validation/src/test/resources/sqs_invalid_messages.json b/powertools-validation/src/test/resources/sqs_invalid_messages.json new file mode 100644 index 000000000..aaec54bfd --- /dev/null +++ b/powertools-validation/src/test/resources/sqs_invalid_messages.json @@ -0,0 +1,72 @@ +{ + "Records": [ + { + "messageId": "d9144555-9a4f-4ec3-99a0-fc4e625a8db2", + "receiptHandle": "7kam5bfzbDsjtcjElvhSbxeLJbeey3A==", + "body": "{\n \"id\": 43242,\n \"name\": \"FooBar XY\",\n \"price\": 258\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975709495", + "SenderId": "AROAIFU457DVZ5L2J53F2", + "ApproximateFirstReceiveTimestamp": "1601975709499" + }, + "messageAttributes": { + }, + "md5OfBody": "0f96e88a291edb4429f2f7b9fdc3df96", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "d9144555-9a4f-4ec3-99a0-fc4e625a8db3", + "receiptHandle": "7kam5bfzbDsjtcjElvhSbxeLJbeey3A==", + "body": "{\n \"id\": 43245,\n \"name\": \"Foo\",\n \"price\": 258\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975709495", + "SenderId": "AROAIFU457DVZ5L2J53F2", + "ApproximateFirstReceiveTimestamp": "1601975709499" + }, + "messageAttributes": { + }, + "md5OfBody": "0f96e88a291edb4429f2f7b9fdc3df96", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "d9144555-9a4f-4ec3-99a0-fc4e625a8db4", + "receiptHandle": "7kam5bfzbDsjtcjElvhSbxeLJbeey3A==", + "body": "{\n \"id\": 43246,\n \"name\": \"FooBar XYZ\",\n \"price\": 258\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975709495", + "SenderId": "AROAIFU457DVZ5L2J53F2", + "ApproximateFirstReceiveTimestamp": "1601975709499" + }, + "messageAttributes": { + }, + "md5OfBody": "0f96e88a291edb4429f2f7b9fdc3df96", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + }, + { + "messageId": "d9144555-9a4f-4ec3-99a0-fc4e625a8db5", + "receiptHandle": "7kam5bfzbDsjtcjElvhSbxeLJbeey3A==", + "body": "{\n \"id\": \"stringId\",\n \"name\": \"FooBar XY\",\n \"price\": 258\n}", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1601975709495", + "SenderId": "AROAIFU457DVZ5L2J53F2", + "ApproximateFirstReceiveTimestamp": "1601975709499" + }, + "messageAttributes": { + }, + "md5OfBody": "0f96e88a291edb4429f2f7b9fdc3df96", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda", + "awsRegion": "eu-central-1" + } + ] +} \ No newline at end of file diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index eca7e266f..53226e3c2 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -8,26 +8,94 @@ https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html --> <FindBugsFilter> - <!-- Internals of Log event for apache log4j--> + <!-- These open matches are new rules that we have not yet addressed. Let's block them generically, and come + back to them later. --> <Match> - <Bug pattern="EI_EXPOSE_REP"/> + <Bug pattern="CT_CONSTRUCTOR_THROW" /> + </Match> + <Match> + <Bug pattern="PA_PUBLIC_PRIMITIVE_ATTRIBUTE" /> + </Match> + <Match> + <Bug pattern="SING_SINGLETON_GETTER_NOT_SYNCHRONIZED" /> + </Match> + <Match> + <Bug pattern="SING_SINGLETON_GETTER_NOT_SYNCHRONIZED" /> + </Match> + + <!-- Regular matches --> + <Match> + <Bug pattern="EI_EXPOSE_REP2"/> <Or> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout$LogEventWithAdditionalFields"/> - <Method name="getLogEvent"/> + <Class name="software.amazon.lambda.powertools.parameters.BaseProvider"/> + <Field name="cacheManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.BaseProvider"/> + <Field name="transformationManager"/> </And> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout$LogEventWithAdditionalFields"/> - <Method name="getAdditionalFields"/> + <Class name="software.amazon.lambda.powertools.parameters.transform.TransformationManager"/> + <Field name="transformation"/> </And> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy$Builder"/> - <Method name="getAdditionalFields"/> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="transformationManager"/> </And> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy$LogEventWithAdditionalFields"/> - <Method name="getAdditionalFields"/> + <Class name="software.amazon.lambda.powertools.largemessages.LargeMessageConfig"/> + <Method name="getS3Client"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.secrets.SecretsProviderBuilder"/> + <Field name="cacheManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.secrets.SecretsProviderBuilder"/> + <Field name="transformationManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.secrets.SecretsProviderBuilder"/> + <Field name="client"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="client"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="cacheManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProviderBuilder"/> + <Field name="cacheManager"/> </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProviderBuilder"/> + <Field name="client"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProviderBuilder"/> + <Field name="transformationManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.appconfig.AppConfigProviderBuilder"/> + <Field name="cacheManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.appconfig.AppConfigProviderBuilder"/> + <Field name="transformationManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.JsonSerializer"/> + </And> + </Or> + </Match> + <!-- Internals of Log event for apache log4j--> + <Match> + <Bug pattern="EI_EXPOSE_REP"/> + <Or> <And> <Class name="software.amazon.lambda.powertools.idempotency.Idempotency"/> <Method name="getPersistenceStore"/> @@ -40,39 +108,39 @@ <Class name="software.amazon.lambda.powertools.largemessages.LargeMessageConfig"/> <Method name="getS3Client"/> </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy$Builder" /> + <Method name="getAdditionalFields"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy$LogEventWithAdditionalFields" /> + <Method name="getAdditionalFields"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout$LogEventWithAdditionalFields" /> + <Method name="getAdditionalFields"/> + </And> </Or> </Match> <Match> <Bug pattern="EI_EXPOSE_REP2"/> <Or> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout$LogEventWithAdditionalFields"/> - <Field name="logEvent"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout$LogEventWithAdditionalFields"/> - <Field name="additionalFields"/> + <Class name="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"/> + <Field name="throwableConverter"/> </And> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy$Builder"/> - <Field name="additionalFields"/> + <Class name="software.amazon.lambda.powertools.parameters.transform.TransformationManager"/> + <Field name="transformer"/> </And> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy$LogEventWithAdditionalFields"/> - <Field name="additionalFields"/> + <Class name="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"/> + <Field name="throwableConverter"/> </And> <And> <Class name="software.amazon.lambda.powertools.sqs.internal.BatchContext"/> <Field name="client"/> </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.BaseProvider"/> - <Field name="cacheManager"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.transform.TransformationManager"/> - <Field name="transformer"/> - </And> <And> <Class name="software.amazon.lambda.powertools.idempotency.Idempotency$Config"/> <Field name="store"/> @@ -86,7 +154,7 @@ <Field name="pjp"/> </And> <And> - <Class name="software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore$Builder"/> + <Class name="software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore$Builder"/> <Field name="dynamoDbClient"/> </And> <And> @@ -101,16 +169,45 @@ <Class name="software.amazon.lambda.powertools.largemessages.LargeMessageConfig"/> <Field name="s3Client"/> </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy" /> + <Method name="LogEventWithAdditionalFields"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.AbstractJacksonLayoutCopy$Builder" /> + <Method name="setAdditionalFields"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout" /> + <Method name="LogEventWithAdditionalFields"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.LambdaEcsEncoder" /> + <Method name="setThrowableConverter"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.LambdaJsonEncoder" /> + <Method name="setThrowableConverter"/> + </And> </Or> </Match> + <Match> + <Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT"/> + <Class name="software.amazon.lambda.powertools.validation.ValidationConfig"/> + <Method name="beforeCheckpoint"/> + </Match> <!--Functionally needed--> <Match> - <Bug pattern="EI_EXPOSE_STATIC_REP2"/> + <Bug pattern="MS_SHOULD_BE_FINAL"/> <Or> <And> - <Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/> - <Method name="defaultObjectMapper"/> + <Class name="software.amazon.lambda.powertools.logging.internal.LoggingConstants"/> </And> + </Or> + </Match> + <Match> + <Bug pattern="EI_EXPOSE_STATIC_REP2"/> + <Or> <And> <Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/> <Method name="defaultObjectMapper"/> @@ -128,10 +225,6 @@ <Match> <Bug pattern="MS_EXPOSE_REP"/> <Or> - <And> - <Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/> - <Method name="objectMapper"/> - </And> <And> <Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/> <Method name="objectMapper"/> @@ -140,17 +233,13 @@ <Class name="software.amazon.lambda.powertools.sqs.SqsUtils"/> <Method name="objectMapper"/> </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.ParamManager"/> - <Method name="getCacheManager"/> - </And> <And> <Class name="software.amazon.lambda.powertools.sqs.SqsUtils"/> <Method name="s3Client"/> </And> <And> - <Class name="software.amazon.lambda.powertools.parameters.ParamManager"/> - <Method name="getTransformationManager"/> + <Class name="software.amazon.lambda.powertools.metrics.MetricsFactory"/> + <Method name="getMetricsInstance"/> </And> </Or> </Match> @@ -168,5 +257,5 @@ <Method name="threadOption1"/> </And> </Or> - </Match> -</FindBugsFilter> \ No newline at end of file + </Match> +</FindBugsFilter>